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