Обзор алгоритмов сегментации

21ec0278e7f349d696960a65974204d4.jpg


Этим летом мне посчастливилось попасть на летнюю стажировку в компанию Itseez. Мне было предложено исследовать современные методы, которые позволили бы выделить местоположения объектов на изображении. В основном такие методы опираются на сегментацию, поэтому я начала свою работу со знакомства с этой областью компьютерного зрения.
Сегментация изображения — это разбиение изображения на множество покрывающих его областей. Сегментация применяется во многих областях, например, в производстве для индикации дефектов при сборке деталей, в медицине для первичной обработки снимков, также для составления карт местности по снимкам со спутников. Для тех, кому интересно разобраться, как работают такие алгоритмы, добро пожаловать под кат. Мы рассмотрим несколько методов из библиотеки компьютерного зрения OpenCV.

Алгоритм сегментации по водоразделам (WaterShed)


b404066220ae40729682db0b2dec473e.jpg

4a40f6e1e7e84ca9bc23ed47219e160e.jpg

Алгоритм работает с изображением как с функцией от двух переменных f=I (x, y), где x, y — координаты пикселя:

231d1b2379ce4d3ebffbee72c5aa4843.jpg29e319da25364ecea5ee135f6fc0f70b.jpg215c911a0f5149aa801ff6c277b68d41.jpg

Значением функции может быть интенсивность или модуль градиента. Для наибольшего контраста можно взять градиент от изображения. Если по оси OZ откладывать абсолютное значение градиента, то в местах перепада интенсивности образуются хребты, а в однородных регионах — равнины. После нахождения минимумов функции f, идет процесс заполнения «водой», который начинается с глобального минимума. Как только уровень воды достигает значения очередного локального минимума, начинается его заполнение водой. Когда два региона начинают сливаться, строится перегородка, чтобы предотвратить объединение областей [2]. Вода продолжит подниматься до тех пор, пока регионы не будут отделяться только искусственно построенными перегородками (рис. 1).

ee2fa77cfd104aa599f25d0de1d8f948.jpgd35b2c1116d6484893c63135b2ad17bd.jpge60aad0827824b078e195900d29dd5b7.jpg
Рис. 1. Иллюстрация процесса заполнения водой

Такой алгоритм может быть полезным, если на изображении небольшое число локальных минимумов, в случае же их большого количества возникает избыточное разбиение на сегменты. Например, если непосредственно применить алгоритм к рис. 2, получим много мелких деталей рис. 3.

32c8e42377154175883228dcb9bef93b.jpg
Рис. 2. Исходное изображение

a470cb63c7034f68bccb12f36131a70b.jpg
Рис. 3. Изображение после сегментации алгоритмом WaterShed

Как справиться с мелкими деталями?
Чтобы избавиться от избытка мелких деталей, можно задать области, которые будут привязаны к ближайшим минимумам. Перегородка будет строиться только в том случае, если происходит объединение двух регионов с маркерами, в противном случае будет происходить слияние этих сегментов. Такой подход убирает эффект избыточной сегментации, но требует предварительной обработки изображения для выделения маркеров, которые можно обозначить интерактивно на изображении рис. 4, 5.

640faaf4a7524a238ec78a91c456072b.jpg
Рис. 4. Изображение с маркерами

ac6bc37fb3464ed385a0dead9effcd00.jpg
Рис. 5. Изображение после сегментации алгоритмом WaterShed с использованием маркеров

Если требуется действовать автоматически без вмешательства пользователя, то можно использовать, например, функцию findContours () для выделения маркеров, но тут тоже для лучшей сегментации мелкие контуры следует исключить рис. 6., например, убирая их по порогу по длине контура. Или перед выделением контуров использовать эрозию с дилатацией, чтобы убрать мелкие детали.

249fd52400a143e4b0c9b955dbd424a8.jpg
Рис. 6. В качестве маркеров использовались контуры, имеющие длину выше определенного порога


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

Пример кода для запуска алгоритма:
Mat image = imread("coins.jpg", CV_LOAD_IMAGE_COLOR);
// выделим контуры 
Mat imageGray, imageBin;
cvtColor(image, imageGray, CV_BGR2GRAY);
threshold(imageGray, imageBin, 100, 255, THRESH_BINARY);
std::vector > contours;
std::vector hierarchy;
findContours(imageBin, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE);
Mat markers(image.size(), CV_32SC1);
markers = Scalar::all(0);
int compCount = 0;
for(int idx = 0; idx >= 0; idx = hierarchy[idx][0], compCount++)
{
   drawContours(markers, contours, idx, Scalar::all(compCount+1), -1, 8, hierarchy, INT_MAX);
}
std::vector colorTab(compCount);
for(int i = 0; i < compCount; i++)
{
   colorTab[i] = Vec3b(rand()&255, rand()&255, rand()&255);
}
watershed(image, markers);
Mat wshed(markers.size(), CV_8UC3);
for(int i = 0; i < markers.rows; i++)
{
   for(int j = 0; j < markers.cols; j++)
   {
       int index = markers.at(i, j);
       if(index == -1)  wshed.at(i, j) = Vec3b(0, 0, 0);
       else if (index == 0) wshed.at(i, j) = Vec3b(255, 255, 255);
               else  wshed.at(i, j) = colorTab[index - 1];
   }
}
imshow("watershed transform", wshed);
waitKey(0);




Алгоритм сегментации MeanShift


MeanShift группирует объекты с близкими признаками. Пиксели со схожими признаками объединяются в один сегмент, на выходе получаем изображение с однородными областями.

ed29264a51f640ea9b9b0df9de28f08f.jpg85fab18e3db949fc8510bf968b7fb3aa.jpg

Например, в качестве координат в пространстве признаков можно выбрать координаты пикселя (x, y) и компоненты RGB пикселя. Изобразив пиксели в пространстве признаков, можно заметить сгущения в определенных местах.

85ffc0f9dad544cc97ffe860d4e6e583.png

Рис. 7. (a) Пиксели в двухмерном пространстве признаков. (b) Пиксели, пришедшие в один локальный максимум, окрашены в один цвет. © 4519466bfe504e92aa4bb5ee3a90d304.png — функция плотности, максимумы соответствуют местам наибольшей концентрации пикселей. Рисунок взят из статьи [3].

Чтобы легче было описывать сгущения точек, вводится функция плотности: e9d68b32c2dd43838dce8c8c1b2cc0d0.png
bd753af56ff94f5895b4d9643c216f2c.png— вектор признаков i-ого пикселя, d — количество признаков, N — число пикселей, h — параметр, отвечающий за гладкость, 1d1cd321acfd4b50abd418400a59bacb.png — ядро. Максимумы функции 4519466bfe504e92aa4bb5ee3a90d304.png расположены в точках сгущения пикселей изображения в пространстве признаков. Пиксели, принадлежащие одному локальному максимуму, объединяются в один сегмент. Получается, чтобы найти к какому из центров сгущения относится пиксель, надо шагать по градиенту 4519466bfe504e92aa4bb5ee3a90d304.png для нахождения ближайшего локального максимума.

Оценка градиента от функции плотности
Для оценки градиента функции плотности можно использовать вектор среднего сдвига 6ddc9fa1c02b4cd3b160e6cf94f6f155.png
В качестве ядра 1d1cd321acfd4b50abd418400a59bacb.png в OpenCV используется ядро Епанечникова [4]:
3fca7a5f718c484ea553f5ce9a7849c2.png
5f8dd04bb2a04eb68da390c5e72a7b4e.png — это объем d-мерной сферы c единичным радиусом.
5e578658bc1547ac8e9bc2ea1442ae9d.png
f342e20058274b209bf447417208584d.png означает, что сумма идет не по всем пикселям, а только по тем, которые попали в сферу радиусом h с центром в точке, куда указывает вектор c6132b7158674a1ab80f546515af5878.png в пространстве признаков [4]. Это вводится специально, чтобы уменьшить количество вычислений. d88e639d57cd4886b6f75dad55258876.png — объем d-мерной сферы с радиусом h, Можно отдельно задавать радиус для пространственных координат и отдельно радиус в пространстве цветов. 856fa767ebb64997b210f6e2c645a869.png — число пикселей, попавших в сферу. Величину 5abdb916a58a4b96baaf2ac398fb680d.png можно рассматривать как оценку значения 4519466bfe504e92aa4bb5ee3a90d304.png в области 182cabe2481f4a20b14947a07b414624.png.
9793549615ff4ed5b78d7151f020831b.png
41268430c0fa4d43ba2173a1ed8aa725.png
Поэтому, чтобы шагать по градиенту, достаточно вычислить значение 6ddc9fa1c02b4cd3b160e6cf94f6f155.png — вектора среднего сдвига. Следует помнить, что при выборе другого ядра вектор среднего сдвига будет выглядеть иначе.


При выборе в качестве признаков координат пикселей и интенсивностей по цветам в один сегмент будут объединяться пиксели с близкими цветами и расположенные недалеко друг от друга. Соответственно, если выбрать другой вектор признаков, то объединение пикселей в сегменты уже будет идти по нему. Например, если убрать из признаков координаты, то небо и озеро будут считаться одним сегментом, так как пиксели этих объектов в пространстве признаков попали бы в один локальный максимум.

Если объект, который хотим выделить, состоит из областей, сильно различающихся по цвету, то MeanShift не сможет объединить эти регионы в один, и наш объект будет состоять из нескольких сегментов. Но зато хорошо справиться с однородным по цвету предметом на пестром фоне. Ещё MeanShift используют при реализации алгоритма слежения за движущимися объектами [5].

Пример кода для запуска алгоритма:
Mat image = imread("strawberry.jpg", CV_LOAD_IMAGE_COLOR);
Mat imageSegment;
int spatialRadius = 35;
int colorRadius = 60;
int pyramidLevels = 3;
pyrMeanShiftFiltering(image, imageSegment, spatialRadius, colorRadius, pyramidLevels);
imshow("MeanShift", imageSegment);
waitKey(0);


Результат:
d252fcaf138542529f2682f23661c776.jpg
Рис. 8. Исходное изображение

7f2dc9f029ac441fa52e1b8896daba40.jpg
Рис. 9. После сегментации алгоритмом MeanShift


Алгоритм сегментации FloodFill


С помощью FloodFill (заливка или метод «наводнения») можно выделить однородные по цвету регионы. Для этого нужно выбрать начальный пиксель и задать интервал изменения цвета соседних пикселей относительно исходного. Интервал может быть и несимметричным. Алгоритм будет объединять пиксели в один сегмент (заливая их одним цветом), если они попадают в указанный диапазон. На выходе будет сегмент, залитый определенным цветом, и его площадь в пикселях.

Такой алгоритм может быть полезен для заливки области со слабыми перепадами цвета однородным фоном. Одним из вариантов использования FloodFill может быть выявление поврежденных краев объекта. Например, если, заливая однородные области определенным цветом, алгоритм заполнит и соседние регионы, то значит нарушена целостность границы между этими областями. Ниже на изображении можно заметить, что целостность границ заливаемых областей сохраняется:
fbcb9cf8f52a4d958bdca8be7d2d6c6e.jpg8d45e4476821422d94e24c77958a9610.jpg
Рис. 10, 11. Исходное изображение и результат после заливки нескольких областей

А на следующих картинках показан вариант работы FloodFill в случае повреждения одной из границ в предыдущем изображении.

6cd232bd6f3b4b21b2aeb38693e336a4.jpga6fc21da82f64b48b29672e125f9e1e3.jpg
Рис. 12, 13. Иллюстрация работы FloodFill при нарушение целостности границы между заливаемыми областями

Пример кода для запуска алгоритма:
Mat image = imread("cherry.jpg", CV_LOAD_IMAGE_COLOR);           
Point startPoint;
startPoint.x = image.cols / 2;
startPoint.y = image.rows / 2;
Scalar loDiff(20, 20, 255);
Scalar upDiff(5, 5, 255);
Scalar fillColor(0, 0, 255);
int neighbors = 8;
Rect domain;
int area = floodFill(image, startPoint, fillColor, &domain, loDiff, upDiff, neighbors);
rectangle(image, domain, Scalar(255, 0, 0));
imshow("floodFill segmentation", image);
waitKey(0);


В переменную area запишется количество «залитых» пикселей.
Результат:
df1b32c826f14e00a116fada71af9998.jpg5c7767b20e294e869e7d8e1243fb0589.jpg


Алгоритм сегментации GrabCut


Это интерактивный алгоритм выделения объекта, разрабатывался как более удобная альтернатива магнитному лассо (чтобы выделить объект, пользователю требовалось обвести его контур с помощью мыши). Для работы алгоритма достаточно заключить объект вместе с частью фона в прямоугольник (grab). Сегментирование объекта произойдет автоматически (cut).

e65888b302b44bcfbc27a5141b2a682b.jpg3aa90e96d87941979322b874fb22c22d.jpg

Могут возникнуть сложности при сегментации, если внутри ограничивающего прямоугольника присутствуют цвета, которые встречаются в большом количестве не только в объекте, но и на фоне. В этом случае можно поставить дополнительные метки объекта (красная линия) и фона (синяя линия).

862567db2d4b4487b2e5e5f5767383b6.jpg98dd376497974363910c702076d81389.jpg767f9775c2fd4bfc826ac2ceff47ffc2.jpg

Рассмотрим идею алгоритма. За основу взят алгоритм интерактивной сегментации GraphCut, где пользователю надо поставить маркеры на фон и на объект. Изображение рассматривается как массив 74594af97b3f48e293bac6ff7fb4bea4.png. Z — значения интенсивности пикселей, N-общее число пикселей. Для отделения объекта от фона алгоритм определяет значения элементов массива прозрачности 6cacfd5b1fd1498abfb2c75bd4b16ccc.png, причем b6baf76b26ce43559c6fa15c447428ed.png может принимать два значения, если b6baf76b26ce43559c6fa15c447428ed.png= 0, значит пиксель принадлежит фону, еслиb6baf76b26ce43559c6fa15c447428ed.png= 1, то объекту. Внутренний параметр 131a8b70f03c47529620ea3f4b9eb2fd.png содержит гистограмму распределения интенсивности переднего плана и гистограмму фона:
d009fd94f84f4deea47aa5f55df51ada.png.
Задача сегментации — найти неизвестные b6baf76b26ce43559c6fa15c447428ed.png. Рассматривается функция энергии:
ddb27eff972b4cecb0bb2bfd283bc6c6.png
Причем минимум энергии соответствует наилучшей сегментации.
24fd294ed9b04df28941bf7605ca4b25.png
V (a, z) — слагаемое отвечает за связь между пикселями. Сумма идет по всем парам пикселей, которые являются соседями, dis (m, n) — евклидово расстояние. 3994de0d7a834c53ae5c267fb9d58b87.png отвечает за участие пар пикселей в сумме, если a n = a m, то эта пара не будет учитываться.
c4a41935921b48cab6363c4e6a96ec26.png — отвечает за качество сегментации, т.е. разделение объекта от фона.

Найдя глобальный минимум функции энергии E, получим массив прозрачности 326d4ae053dc4ac2be5bfe821c6ea2e4.png. Для минимизации функции энергии, изображение описывается как граф и ищется минимальный разрез графа. В отличие от GraphCut в алгоритме GrabCut пиксели рассматриваются в RGB пространстве, поэтому для описания цветовой статистики используют смесь гауссиан (Gaussian Mixture Model — GMM). Работу алгоритма GrabCut можно посмотреть, запустив сэмпл OpenCV grabcut.cpp.

Мы рассмотрели только небольшую часть существующих алгоритмов. В результате сегментации на изображении выделяются области, в которые объединяются пиксели по выбранным признакам. Для заливки однородных по цвету объектов подойдет FloodFill. С задачей отделения конкретного объекта от фона хорошо справится GrabCut. Если использовать реализацию MeanShift из OpenCV, то пиксели, близкие по цвету и координатам, будут кластеризованы. WaterShed подойдет для изображений с простой текстурой. Таким образом, алгоритм сегментации следует выбирать, конечно, исходя из конкретной задачи.

Литература:


  1. G. Bradski, A. Kaehler Learning OpenCV: OReilly, second edition 2013
  2. Р. Гонсалес, Р. Вудс Цифровая обработка изображений, Москва: Техносфера, 2005. — 1072 с.
  3. D. Comaniciu, P. Meer Mean Shift: A Robust Approach Toward Feature Space Analysis. IEEE Transactions on Pattern Analysis and Machine Intelligence, 2002, pp. 603–619.
  4. D. Comaniciu, P. Meer Mean shift analysis and applications, IEEE International Conference on Computer Vision, 1999, vol. 2, pp. 1197.
  5. D. Comaniciu, V. Ramesh, P. Meer Real-Time Tracking of Non-Rigid Objects Using Mean Shift, Conference on CVPR, 2000, vol. 2, pp. 1–8.
  6. С. Rother, V. Kolmogorov, A. Blake Grabcut — interactive foreground extraction using iterated graph cuts, 2004
  7. Сэмплы OpenCV


© Habrahabr.ru