С.И.Хашин
В этом модуле не производится сегментация изображений!
В нём лишь даются инструменты, удобные для работы с сегментами.
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
// соседи 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); // длина общей границы между сегментами