К оглавлению

Сегменты

С.И.Хашин

В этом модуле не производится сегментация изображений!
В нём лишь даются инструменты, удобные для работы с сегментами.

segment.h - заголовочный файл
segment.cpp - реализация.

ОГЛАВЛЕНИЕ


Определения

Определение. Сегментацией изображения будем называть его разбиение на непересекающиеся подмножества – сегменты.

Сегментацию будем задавать с помощью матрицы segm: значение матрицы в точке – это номер сегмента, которому она принадлежит. Номер сегмента – неотрицательное число, причем номер 0 обычно зарезервирован для специальных целей (но допустим), например, в процессе сегментации сегмент 0 – это точки, которые еще не принадлежат ни к какому сегменту.

Таким образом, область изображения определяется матрицей (segm) и целым числом – номером сегмента.

Другой способ задания сегмента – задать список его точек. В классе segm реализован переход между этими двумя способами задания сегмента.

Класс segm – расширение класса matr (матрица из целых чисел).

Определение. Пусть U – область на изображении. Точка называется внутренней граничной, если она принадлежит области U, а одна из соседних – не принадлежит. (Возможно два варианта: когда соседними считать лишь 4 точки, либо 8). Точка называется внешнней граничной, если она не принадлежит области U, а одна из соседних – принадлежит.

Определение. Точка на сегментированном изображении называется граничной, если она принадлежит одному сегменту, а одна из соседних – другому.

Определение. Расширением области называется добавление к ней всех точек внешней границы. Сокращением области называется удаление из нее всех точек внутренней границы. Если изображение целиком сегментировано, то удаляемые точки могут присоединяться либо к выделенному (нулевому) сегменту, либо к «наиболее близкому» в некотором смысле.

В этом модуле используются две функции (из vUtil.h) обхода соседей:

void neighbor (int x, int y, int k, int &x1, int &y1, int h=1); // соседняя точка номер k=0|1|2|3 на расстоянии h
void neighbor8(int x, int y, int k, int &x1, int &y1, int h=1); // соседняя точка номер k=0..7 на расстоянии h

Дополнительные данные:

    int max;                    // максимальный номер сегмента

Рисование сегментов

В модуле предлагаются следующие способы изображения сегмента в файле:

  void drawSegments(char *bmpname);
        // нарисовать сегменты в файле bmpname случайным цветом
        // каждый сегмент изображается своим цветом

  void drawSegments(matr &rgb, char *bmpname);
        // нарисовать границы сегментов по картинке rgb в файле bmpname
        // Граничные точки отмечаются цветом

  void drawSegments(matr3 &a, char *bmpname);
        // нарисовать границы сегментов по картинке rgb в файле bmpname
        // Граничные точки отмечаются цветом

  void drawSegments(matr &r, matr &g, matr &b, char *bmpname);
        // нарисовать границы сегментов по картинке rgb в файле bmpname
        // Граничные точки отмечаются цветом

Значения матрицы на сегменте

Значения матрицы на сегменте:

    int     minInSegm(int k, matr &a, int &x, int &y);// мин. значение матрицы a в сегменте #k
    int     maxInSegm(int k, matr &a, int &x, int &y);// макс.значение матрицы a в сегменте #k
    int     sumInSegm(int k, matr &a);              // сумма значений матрицы a в сегменте #k
    double  midInSegm(int k, matr &a);              // среднее значение матрицы a в сегменте #k
    void    setInSegm(int k, matr &a, int v);       // в сегменте #k установить значения в матрице a равными v

Упорядочение сегментов

Обычно предполагается, что номера сегментов идут подряд от 1 до максимального номера. Иногда может оказаться, что это не так, например, есть сегменты с номерами 1,2, 10, а промежуточных номеров нет. Метод

    void    minimizeIndexes();              // удалить неиспользуемые номера сегментов

служит для минимизации индексов сегментов, то есть удалении неиспользуемых номеров.

Для перенумерации сегментов таким образом, чтобы их размеры шли по убыванию служат методы:

    void    sortBySize();                   // сортировать по убыванию  размера
    void    sortBySize(int i1, int i2);     // сегменты i1..i2 сортировать по убыванию  размера
    void    join(int i, int j);             // объединить сегменты i и j со сдвигом нумерации

Списки точек

Как уже говорилось, сегмент может быть задан списком точек, класс pvector.
Методы для работы со списками:

    // списки точек
    void    getList( pvector &list, int v );    // list := список точек, в которых a[x,y]==v
    void    addList( pvector &list, int v );    // list += список точек, в которых a[x,y]==v
    void    setByList(pvector &list, int v );   // в точках из списка установить a[x,y]=v

Количество точек в сегменте v можно найти методом count, унаследованным от matr:

    segm sgm;
    int K = sgm.count(v);

Для нахождения количества точек сразу в сементах от v0 до v1-1:

    void    areas( int v0, int v1, int *sz );       // sz[i] := площадь сегмента v0+i

Массив sz уже должен иметь необходимый размер!

Соседи и проверка граничности точек

    // соседи
    int     neighborCount (int x, int y, int v=-1 );// количество соседних (8) точек со значениями НЕ РАВНЫМИ v
    int     neighborCount2(int x, int y, int v=-1 );// количество соседних (8) точек со значениями    РАВНЫМИ v

    // граничности
    bool    isBorder4( int x, int y );              // значение в точке не равно одному из 4-х соседних
    bool    isBorder8( int x, int y );              // значение в точке не равно одному из 8-х соседних
    int     box( int iSegm, int &x0, int &y0, int &x1, int &y1); 
                                                // (x0,y0)-(x1,y1) - прямоугольник, содержащий сегмент

    // точка из внутренней границы сегмента iSegm?
    bool    isInBorder4( int x, int y, int iSegm ); 
        // значение в точке равно iSegm, а в одной из 4-х соседних - не равно

    bool    isInBorder8( int x, int y, int iSegm ); 
        // значение в точке равно iSegm, а в одной из 8-ми соседних - не равно

    // точка из внешней границы сегмента iSegm?
    bool    isOutBorder4( int x, int y, int iSegm );
        // значение в точке не равно iSegm, а в одной из 4-х соседних - равно

    bool    isOutBorder8( int x, int y, int iSegm );
        // значение в точке не равно iSegm, а в одной из 8-ми соседних - равно

    // список точек границы:
    void inBorder(int iSegm, pvector &list);  // list := внутренние граничные точки в сегменте iSegm
    void outBorder(int iSegm, pvector &list); // list := внешнние граничные точки в сегменте iSegm

    int     nearestB(int x0, int y0, int &x1, int &y1); 
        // для точки (x,y) найти (x1,y1) - ближайшую точку другого сегмента


Связность сегмента

Определение. а) Сегмент называется связным (точнее, 4-связным), если из любой его точки можно добраться до любой другой переходя каждый раз к одной из 4-х соседних точек внутри сегмента.
б) Если от любой точки сегмента до любой другой можно добраться переходя к одной из восьми соседних, то такой сегмент будем называть 8-связным. (Топологи! Молчать!).

Обычно сегментация предполагает связные сегменты, но не обязательно. Для разбиения сегмента на связные компоненты:

    // в связной области вокруг (x0,y0) записать v2. Возвращает размер.
    int     connectRegion( int x0, int y0, int v2, int type=8, pvector *list=NULL);

    bool    isConnected(int iSegm, int type=8);
                 // сегмент связен? iSegm<0 - проверка всех сегментов 

    void    connectSegmentation(int type=8);
                 // каждую связную компоненту каждого сегмента сделать отдельным сегментом

    void    connectSegmentationP(int type=8);
                 // каждую связную компоненту каждого сегмента сделать отдельным сегментом
                 // (только положительные номера)

Увеличение/уменьшение сегмента

    // увеличение/уменьшение сегментов
    int     expandSegment(int iSegm);               // увеличить сегмент iSegm во все стороны на 1 точку
    int     expandSegment(int iSegm, int iSegm0);   // увеличить сегмент iSegm во все стороны на 1 точку но только за счет сегмента iSegm0
    int     expandSegments(int iSeg1, int iSeg2, int iSegm0);   // увеличить сегменты iSeg1..iSeg2 во все стороны на 1 точку но только за счет сегмента iSegm0

    int     decreaseSegment(int iSegm, int v);      // Все граничные точки сегмента iSegm перевести в сегмент v
    int     decreaseSegments(int iSegm1, int iSegm2, int v);// граничные точки сегментов iSegm1..iSegm2 перевести в сегмент v
    void    kill1();                                // Если 4 соседние точки из других сегментов, то передать точку в один из соседних сегментов
    void    kill_small(int minSize);                // если связная область сегмента имеет размер <=min, то присоединим её точки к соседним сегментам

Удаление маленьких сегментов означает перевод их точек в нулевой сегмента


Клеточный автомат

Клеточный автомат — одни из важнейших инструментов сглаживания сегментов.

У каждой клетки имеется 8 соседей. Идея алгоритма очень проста. Если точка принадлежит одному сегменту, в не менее k (4≤ k ≤8) соседних другому, что присоединяем нашу точку к соседнему сегменту.

Один шаг клеточного автомата с уровнем k состоит в том, что мы просматриваем все элементы матрицы и с каждой клеткой выполняем указанную операцию.

Этот шаг выплняем до тех пор, пока есть какие-либо изменения.

    int     cellAutom(int k);                       // клеточный автомат с уровнем k

Дополнительные данные и топология

Дополнительная информация о сегментах:

Топология состоит из двух симметричных матриц:

Будем считать, что top(i,i) равно общему количеству граничных пар у сегмента i, а ctop(i,i) - сумма разностей цветов по всем граничным парам у сегмента (i).

Сбор информации:

    void    getInfo();        // найти данные о сегментах без исходной матрицы.
    void    getInfo(matr  &a);// найти данные о сегментах с исходной серой матрицей
    void    getInfo(matr3 &a);// найти данные о сегментах с исходной цветной матрицей

Если матрица не задана, то и информация о цветах не собирается.

Получение данных:

    int     Area(int iSegm);
    double  MidX(int iSegm);
    double  MidY(int iSegm);
    double  Offs(int iSegm);

    double  MidR(int iSegm);
    double  MidG(int iSegm);
    double  MidB(int iSegm);

    double  colDist (int iSegm1, int iSegm2);       // цветовое расстояние между сегментами
    double  colBDist(int iSegm1, int iSegm2);       // цветовое расстояние между сегментами вдоль границы
    int     bordSize(int iSegm1, int iSegm2);       // длина общей границы между сегментами

К оглавлению


free counters