• Фундаментальные исследования. Методы обработки изображений. Сегментация

    Сегментация методом управляемого водораздела

    Довольно часто при анализе изображений возникает задача разделения пикселей изображений на группы по некоторым признакам. Такой процесс разбиения на группы называется сегментацией. Наиболее известными являются два вида сегментации - сегментация по яркости для бинарных изображений и сегментация по цветовым координатам для цветных изображений. Методы сегментации можно рассматривать как формализацию понятия выделяемости объекта из фона или понятий связанных с градиентом яркости. Алгоритмы сегментации характеризуются некоторыми параметрами надежности и достоверности обработки. Они зависят от того, насколько полно учитываются дополнительные характеристики распределения яркости в областях объектов или фона, количество перепадов яркости, форма объектов и др.

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

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

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

    Развитие технологий обработки изображений привело к возникновению новых подходов к решению задач сегментации изображений и применении их при решении многих практических задач.

    В данной работе рассмотрим относительно новый подход к решению задачи сегментации изображений - метод водораздела. Коротко объясним название этого метода и в чем его суть.

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

    Разделение соприкасающихся предметов на изображении является одной из важных задач обработки изображений. Часто для решения этой задачи используется так называемый метод маркерного водораздела. При преобразованиях с помощью этого метода нужно определить "водосборные бассейны" и "линии водораздела" на изображении путем обработки локальных областей в зависимости от их яркостных характеристик.

    Метод маркерного водораздела является одним из наиболее эффективных методов сегментации изображений. При реализации этого метода выполняются следующие основные процедуры:

      Вычисляется функция сегментации. Она касается изображений, где объекты размещены в темных областях и являются трудно различимыми.

      Вычисление маркеров переднего плана изображений. Они вычисляются на основании анализа связности пикселей каждого объекта.

      Вычисление фоновых маркеров. Они представляют собой пиксели, которые не являются частями объектов.

      Модификация функции сегментации на основании значений расположения маркеров фона и маркеров переднего плана.

      Вычисления на основании модифицированной функции сегментации.

    В данном примере среди функций пакета Image Processing Toolbox наиболее часто используются функции fspecial, imfilter, watershed, label2rgb, imopen, imclose, imreconstruct, imcomplement, imregionalmax, bwareaopen, graythresh и imimposemin.

    • Шаг 1: Считывание цветного изображения и преобразование его в полутоновое.
    • Шаг 2: Использование значения градиента в качестве функции сегментации.
    • Шаг 3: Маркировка объектов переднего плана.
    • Шаг 4: Вычисление маркеров фона.
    • Шаг 6: Визуализация результата обработки.

    Шаг 1: Считывание цветного изображения и преобразование его в полутоновое.

    Считаем данные из файла pears.png rgb=imread("pears.png"); и представим их в виде полутонового изображения. I=rgb2gray(rgb); imshow(I) text(732,501,"…",... "FontSize",7,"HorizontalAlignment","right")

    Шаг 2: Использование значения градиента в качестве функции сегментации.

    Для вычисления значения градиента используется оператор Собеля, функция imfilter и другие вычисления. Градиент имеет большие значения на границах объектов и небольшие (в большинстве случаев) вне границ объектов.

    Hy=fspecial("sobel"); hx=hy"; Iy=imfilter(double(I), hy, "replicate"); Ix=imfilter(double(I), hx, "replicate"); gradmag=sqrt(Ix.^2+Iy.^2); figure, imshow(gradmag,), title("значение градиента")

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

    L=watershed(gradmag); Lrgb=label2rgb(L); figure, imshow(Lrgb), title("Lrgb")

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

    Шаг 3: Маркировка объектов переднего плана.

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

    Как было сказано выше, при проведении маркировки объектов переднего плана используются также морфологические операции. Рассмотрим некоторые из них и сравним. Сначала реализуем операцию раскрытия с использованием функции imopen.

    Se=strel("disk", 20); Io=imopen(I, se); figure, imshow(Io), title("Io")

    Ie=imerode(I, se); Iobr=imreconstruct(Ie, I); figure, imshow(Iobr), title("Iobr")

    Последующие морфологические операции раскрытия и закрытия приведут к перемещению темных пятен и формированию маркеров. Проанализируем операции морфологического закрытия. Для этого сначала используем функцию imclose:

    Ioc=imclose(Io, se); figure, imshow(Ioc), title("Ioc")

    Iobrd=imdilate(Iobr, se); Iobrcbr=imreconstruct(imcomplement(Iobrd), imcomplement(Iobr)); Iobrcbr=imcomplement(Iobrcbr); figure, imshow(Iobrcbr), title("Iobrcbr")

    Сравнительный визуальный анализ Iobrcbr и Ioc показывает, что представленная реконструкция на основе морфологических операций открытия и закрытия является более эффективной в сравнении с стандартными операциями открытия и закрытия. Вычислим локальные максимумы Iobrcbr и получим маркеры переднего плана.

    Fgm=imregionalmax(Iobrcbr); figure, imshow(fgm), title("fgm")

    Наложим маркеры переднего плана на исходное изображение.

    I2=I; I2(fgm)=255; figure, imshow(I2), title("fgm, наложенное на исходное изображение")

    Отметим, что при этом некоторые скрытые или закрытые объекты изображения не являются маркированными. Это свойство влияет на формирование результата и многие такие объекты изображения не будут обработаны с точки зрения сегментации. Таким образом, маркеры переднего плана отображают границы только большинства объектов. Представленные таким образом границы подвергаются дальнейшей обработке. В частности, это могут быть морфологические операции.

    Se2=strel(ones(5, 5)); fgm2=imclose(fgm, se2); fgm3=imerode(fgm2, se2);

    В результате проведения такой операции пропадают отдельные изолированные пиксели изображения. Также можно использовать функцию bwareaopen, которая позволяет удалять заданное число пикселей.

    Fgm4=bwareaopen(fgm3, 20); I3=I; I3(fgm4)=255; figure, imshow(I3) title("fgm4, наложенное на исходное изображение")

    Шаг 4: Вычисление маркеров фона.

    Теперь проведем операцию маркирования фона. На изображении Iobrcbr темные пиксели относятся к фону. Таким образом, можно применить операцию пороговой обработки изображения.

    Bw=im2bw(Iobrcbr, graythresh(Iobrcbr)); figure, imshow(bw), title("bw")

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

    D=bwdist(bw); DL=watershed(D); bgm=DL==0; figure, imshow(bgm), title("bgm")

    Шаг 5: Вычисление по методу маркерного водораздела на основании модифицированной функции сегментации.

    Функция imimposemin может применяться для точного определения локальных минимумов изображения. На основании этого функция imimposemin также может корректировать значения градиентов на изображении и таким образом уточнять расположение маркеров переднего плана и фона.

    Gradmag2=imimposemin(gradmag, bgm | fgm4);

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

    L=watershed(gradmag2);

    Шаг 6: Визуализация результата обработки.

    Отобразим на исходном изображении наложенные маркеры переднего плана, маркеры фона и границы сегментированных объектов.

    I4=I; I4(imdilate(L==0, ones(3, 3))|bgm|fgm4)=255; figure, imshow(I4) title("Маркеры и границы объектов, наложенные на исходное изображение")

    В результате такого отображения можно визуально анализировать месторасположение маркеров переднего плана и фона.

    Представляет интерес также отображение результатов обработки с помощью цветного изображения. Матрица, которая генерируется функциями watershed и bwlabel, может быть конвертирована в truecolor-изображение посредством функции label2rgb.

    Lrgb=label2rgb(L, "jet", "w", "shuffle"); figure, imshow(Lrgb) title("Lrgb")

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

    Figure, imshow(I), hold on himage=imshow(Lrgb); set(himage, "AlphaData", 0.3); title("Lrgb, наложенное на исходное изображение в полупрозрачном режиме")

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

    Операция порогового разделения, которая в результате дает бинарное изображение, называется бинаризацией. Целью операции бинаризации является радикальное уменьшение количества информации, содержащейся на изображении. В процессе бинаризации исходное полутоновое изображение, имеющее некое количество уровней яркости, преобразуется в черно-белое изображение, пиксели которого имеют только два значения – 0 и 1

    Пороговая обработка изображения может проводиться разными способами.

    Бинаризация с нижним порогом
    Бинаризация с нижним порогом
    Бинаризация с нижним порогом является наиболее простой операцией, в которой используется только одно значение порога:

    Все значения вместо критерия становятся 1, в данном случае 255 (белый) и все значения(амплитуды) пикселей, которые больше порога t - 0 (черный).

    Бинаризации с верхним порогом
    Иногда можно использовать вариант первого метода, который дает негатив изображения, полученного в процессе бинаризации. Операция бинаризации с верхним порогом:

    Бинаризация с двойным ограничением
    Для выделения областей, в которых значения яркости пикселей может меняться в известном диапазоне, вводится бинаризация с двойным ограничением (t 1
    Так же возможны другие вариации с порогами, где пропускается только часть данных (средне полосовой фильтр).

    Неполная пороговая обработка
    Данное преобразование дает изображение, которое может быть проще для дальнейшего анализа, поскольку оно становится лишенным фона со всеми деталями, присутствующими на исходном изображении.

    Многоуровневое пороговое преобразование
    Данная операция формирует изображение, не являющееся бинарным, но состоящее из сегментов с различной яркостью.

    Что касается бинаризации, то по сути все. Хотя можно добавить, что есть глобальная, которая используется для всего изображения и так же существует локальная, которая захватывает часть картинки (изображения).

    Локальная пороговая обработка
    Метод Отса
    Метод использует гистограмму распределения значений яркости пикселей растрового изображения. Строится гистограмма по значениям p i =n i /N, где N – это общее кол-во пикселей на изображении, n i – это кол-во пикселей с уровнем яркости i. Диапазон яркостей делится на два класса с помощью порогового значения уровня яркости k,k - целое значение от 0 до L. Каждому классу соответствуют относительные частоты ω 0 ω 1:

    Средние уровни для каждого из двух классов изображения:
    Далее вычисляется максимальное значение оценки качества разделения изображения на две части:
    где (σ кл)2=ω 0 ω 1 (μ 1 -μ 0) 2 , – межклассовая дисперсия, а (σ общ) 2 – это общая дисперсия для всего изображения целиком.

    Определение порога на основе градиента яркости изображения
    Предположим, что анализируемое изображение можно разделить на два класса – объекты и фон. Алгоритм вычисления порогового значения состоит из следующих 2 шагов:
    1. Определяется модуль градиента яркости для каждого пикселя
    изображения

    2. Вычисление порога:
    Итого
    Что нашел с радостью выложил вам, в дальнейшем, если получится и будет время, постараюсь реализовать часть алгоритмов. Это лишь малая часть всего, что сегодня существует, но я рад поделится и этим.
    Спасибо за внимание.

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

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


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


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




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

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


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


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

    Как справиться с мелкими деталями?

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


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


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

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


    Рис. 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, 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; } } imshow("watershed transform", wshed); waitKey(0);

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

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


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

    Рис. 7. (a) Пиксели в двухмерном пространстве признаков. (b) Пиксели, пришедшие в один локальный максимум, окрашены в один цвет. (с) - функция плотности, максимумы соответствуют местам наибольшей концентрации пикселей. Рисунок взят из статьи .

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

    Оценка градиента от функции плотности

    Для оценки градиента функции плотности можно использовать вектор среднего сдвига
    В качестве ядра в OpenCV используется ядро Епанечникова :


    - это объем d -мерной сферы c единичным радиусом.


    означает, что сумма идет не по всем пикселям, а только по тем, которые попали в сферу радиусом h с центром в точке, куда указывает вектор в пространстве признаков . Это вводится специально, чтобы уменьшить количество вычислений. - объем d -мерной сферы с радиусом h, Можно отдельно задавать радиус для пространственных координат и отдельно радиус в пространстве цветов. - число пикселей, попавших в сферу. Величину можно рассматривать как оценку значения в области .


    Поэтому, чтобы шагать по градиенту, достаточно вычислить значение - вектора среднего сдвига. Следует помнить, что при выборе другого ядра вектор среднего сдвига будет выглядеть иначе.


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

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

    Пример кода для запуска алгоритма:

    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);
    Результат:


    Рис. 8. Исходное изображение


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

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

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

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

    Рис. 10, 11. Исходное изображение и результат после заливки нескольких областей

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


    Рис. 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 запишется количество “залитых" пикселей.
    Результат:


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

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


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


    Рассмотрим идею алгоритма. За основу взят алгоритм интерактивной сегментации GraphCut, где пользователю надо поставить маркеры на фон и на объект. Изображение рассматривается как массив . Z - значения интенсивности пикселей, N -общее число пикселей. Для отделения объекта от фона алгоритм определяет значения элементов массива прозрачности , причем может принимать два значения, если = 0 , значит пиксель принадлежит фону, если= 1 , то объекту. Внутренний параметр содержит гистограмму распределения интенсивности переднего плана и гистограмму фона:
    .
    Задача сегментации - найти неизвестные . Рассматривается функция энергии:

    Причем минимум энергии соответствует наилучшей сегментации.


    V (a, z) - слагаемое отвечает за связь между пикселями. Сумма идет по всем парам пикселей, которые являются соседями, dis(m,n) - евклидово расстояние. отвечает за участие пар пикселей в сумме, если a n = a m , то эта пара не будет учитываться.
    - отвечает за качество сегментации, т.е. разделение объекта от фона.

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

    Сегментация изображений с U-Net на практике

    Введение

    В этом блог посте мы посмотрим как Unet работает, как реализовать его, и какие данные нужны для его обучения. Для этого мы будем рассматривать:

    1. как источник для вдохновения.
    2. Pytorch как инструмент для реализации нашей задумки.
    3. Kaggle соревнования как место где мы можем опробовать наши гипотезы на реальных данных.

    Мы не будем следовать на 100% за статьей, но мы постараемся реализовать ее суть, адаптировать под наши нужды.

    Презентация проблемы

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

    Для понимания того что мы хотим, gif изображение ниже:

    Изображение слева - это исходное изображение, справа - маска, которая будет применяться на изображение. Мы будем использовать Unet нейронную сеть, которая будет учиться автоматически создавать маску.

    1. Подавая в нейронную сеть изображения автомобилей.
    2. Используя функцию потерь, сравнивая вывод нейронной сети с соответствующими масками и возвращающую ошибку для сети, чтобы узнать в каких местах сеть ошибается.

    Структура кода

    Код был максимально упрощен для понимания как это работает. Основной код находится в этом файле main.py , разберем его построчно.

    Код

    Мы будем итеративно проходить через код в main.py и через статью. Не волнуйтесь о деталях, спрятанных в других файлах проекта: нужные мы продемонстрируем по мере необходимости.

    Давайте начнем с начала :

    def main (): # Hyperparameters input_img_resize = (572 , 572 ) # The resize size of the input images of the neural net output_img_resize = (388 , 388 ) # The resize size of the output images of the neural net batch_size = 3 epochs = 50 threshold = 0. 5 validation_size = 0. 2 sample_size = None # -- Optional parameters threads = cpu_count() use_cuda = torch.cuda.is_available() script_dir = os.path.dirname(os.path.abspath(__file__ )) # Training callbacks tb_viz_cb = TensorboardVisualizerCallback(os.path.join(script_dir,"../logs/tb_viz" )) tb_logs_cb = TensorboardLoggerCallback(os.path.join(script_dir,"../logs/tb_logs" )) model_saver_cb = ModelSaverCallback(os.path.join(script_dir,"../output/models/model_" + helpers.get_model_timestamp()), verbose= True )

    В первом разделе вы определяете свои гиперпараметры, их можете настроить по своему усмотрению, например в зависимости от вашей памяти GPU. Optimal parametes определяют некоторые полезные параметры и callbacks . TensorboardVisualizerCallback - это класс, который будет сохранять предсказания в tensorboard в каждую эпоху тренировочного процесса, TensorboardLoggerCallback сохранит значения функций потерь и попиксельную «точность» в tensorboard . И наконец ModelSaverCallback сохранит вашу модель после завершения обучения.

    # Download the datasets ds_fetcher = DatasetFetcher () ds_fetcher. download_dataset()

    Этот раздел автоматически загружает и извлекает набор данных из Kaggle. Обратите внимание, что для успешной работы этого участка кода вам необходимо иметь учетную запись Kaggle с логином и паролем, которые должны быть помещены в переменные окружения KAGGLE_USER и KAGGLE_PASSWD перед запуском скрипта. Также требуется принять правила конкурса, перед загрузкой данных. Это можно сделать на вкладке загрузки данных конкурса

    # Get the path to the files for the neural net X_train, y_train, X_valid, y_valid = ds_fetcher.get_train_files(sample_size= sample_size, validation_size= validation_size) full_x_test = ds_fetcher.get_test_files(sample_size) # Testing callbacks pred_saver_cb = PredictionsSaverCallback(os.path.join (script_dir,"../output/submit.csv.gz" ), origin_img_size, threshold)

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

    После окончания процесса предсказания вы можете отправить полученный файл submit.csv.gz из выходной папки в Kaggle.

    # -- Define our neural net architecture # The original paper has 1 input channel, in our case we have 3 (RGB ) net = unet_origin. UNetOriginal ((3 , *img_resize)) classifier = nn. classifier. CarvanaClassifier (net, epochs) optimizer = optim. SGD (net. parameters() , lr= 0.01 , momentum= 0.99 ) train_ds = TrainImageDataset (X_train , y_train, input_img_resize, output_img_resize, X_transform = aug. augment_img) train_loader = DataLoader (train_ds, batch_size, sampler= RandomSampler (train_ds), num_workers= threads, pin_memory= use_cuda) valid_ds = TrainImageDataset (X_valid , y_valid, input_img_resize, output_img_resize, threshold= threshold) valid_loader = DataLoader (valid_ds, batch_size, sampler= SequentialSampler (valid_ds), num_workers= threads, pin_memory= use_cuda)

    print ("Training on {} samples and validating on {} samples " . format(len(train_loader. dataset), len(valid_loader. dataset))) # Train the classifier classifier. train(train_loader, valid_loader, epochs, callbacks= )

    test_ds = TestImageDataset (full_x_test, img_resize) test_loader = DataLoader (test_ds, batch_size, sampler= SequentialSampler (test_ds), num_workers= threads, pin_memory= use_cuda) # Predict & save classifier. predict(test_loader, callbacks= ) pred_saver_cb. close_saver()

    Наконец, мы делаем то же самое, что и выше, но для прогона предсказания. Мы вызываем наш pred_saver_cb.close_saver() , чтобы очистить и закрыть файл, который содержит предсказания.

    Реализация архитектуры нейронной сети

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

    Перед тем, как идти вперед, вы должны прочитать статью полностью хотя бы один раз. Не волнуйтесь, если вы не получили полного понимания математического аппарата, вы можете пропустить этот раздел, также как главу «Эксперименты». Наша цель заключается в получении общей картины.

    Задача оригинальной статьи отличается от нашей, нам нужно будет адаптировать некоторые части соответственно нашим потребностям.

    В то время, когда была написана работа, были пропущены 2 вещи, которые сейчас необходимы для ускорения сходимости нейронной сети:

    1. BatchNorm.
    2. Мощные GPU.

    Первое был изобретено всего за 3 месяца до Unet , и вероятно слишком рано, чтобы авторы Unet добавили его в свою статью.

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

    Что касается графических процессоров, в статье говорится:

    To minimize the overhead and make maximum use of the GPU memory, we favor large input tiles over a large batch size and hence reduce the batch to a single image

    Они использовали GPU с 6 ГБ RAM, но в настоящее время у GPU больше памяти, для размещения изображений в одном batch’e. Текущий batch равный трем, работает для графического процессора в GPU с 8 гб RAM. Если у вас нет такой видеокарты, попробуйте уменьшить batch до 2 или 1.

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

    Теперь давайте начнем с самого начала, проектируя архитектуру нейронной сети:

    Вот как выглядит Unet. Вы можете найти эквивалентную реализацию Pytorch в модуле nn.unet_origin.py.

    Все классы в этом файле имеют как минимум 2 метода:

    • __init__() где мы будем инициализировать наши уровни нейронной сети;
    • forward() который является методом, называемым, когда нейронная сеть получает вход.

    Давайте рассмотрим детали реализации:

    • ConvBnRelu - это блок, содержащий операции Conv2D, BatchNorm и Relu. Вместо того, чтобы набирать их 3 для каждого стека кодировщика (группа операций вниз) и стеков декодера (группа операций вверх), мы группируем их в этот объект и повторно используем его по мере необходимости.
    • StackEncoder инкапсулирует весь «стек» операций вниз, включая операции ConvBnRelu и MaxPool , как показано ниже:



    Мы отслеживаем вывод последней операции ConvBnRelu в x_trace и возвращаем ее, потому что мы будем конкатенировать этот вывод с помощью стеков декодера.

    • StackDecoder - это то же самое, что и StackEncoder, но для операций декодирования, окруженных ниже красным:



    Обратите внимание, что он учитывает операцию обрезки / конкатенации (окруженную оранжевым), передавая в down_tensor, который является не чем иным, как тензором x_trace, возвращаемым нашим StackEncoder .

    • UNetOriginal - это место, где происходит волшебство. Это наша нейронная сеть, которая будет собирать все маленькие кирпичики, представленные выше. Методы init и forward действительно сложны, они добавляют кучу StackEncoder , центральной части и под конец несколько StackDecoder . Затем мы получаем вывод StackDecoder , добавляем к нему свертку 1x1 в соответствии со статьей, но вместо того, чтобы определять два фильтра в качестве вывода, мы определяем только 1, который фактически будет нашим прогнозом маски в оттенках серого. Далее мы «сжимаем» наш вывод, чтобы удалить размер канала (всего 1, поэтому нам не нужно его хранить).

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

    Тренировка нейронной сети

    1. Функция потерь

    Теперь к реальному миру. Согласно статье:

    The energy function is computed by a pixel-wise soft-max over the final feature map combined with the cross-entropy loss function.

    Дело в том, что в нашем случае мы хотим использовать dice coefficient как функцию потерь вместо того, что они называют «энергетической функцией», так как это показатель, используемый в соревновании Kaggle , который определяется:

    X является нашим предсказанием и Y - правильно размеченной маской на текущем объекте. |X| означает мощность множества X (количество элементов в этом множестве) и ∩ для пересечения между X и Y .

    Код для dice coefficient можно найти в nn.losses.SoftDiceLoss .

    class SoftDiceLoss (nn.Module): def __init__(self, weight= None, size_average= True): super (SoftDiceLoss, self).__init__() def forward(self, logits, targets): smooth = 1 num = targets.size (0 ) probs = F.sigmoid(logits) m1 = probs.view(num, - 1 ) m2 = targets.view(num, - 1 ) intersection = (m1 * m2) score = 2 . * (intersection.sum(1 ) + smooth) / (m1.sum(1 ) + m2.sum(1 ) + smooth) score = 1 - score.sum() / num return score

    Причина, по которой пересечение реализуется как умножение, и мощность в виде sum() по axis 1 (сумма из трех каналов) заключается в том, что предсказания и цель являются one-hot encoded векторами.

    Например, предположим, что предсказание на пикселе (0, 0) равно 0,567, а цель равна 1, получаем 0,567 * 1 = 0,567. Если цель равна 0, мы получаем 0 в этой позиции пикселя.

    Мы также использовали плавный коэффициент 1 для обратного распространения. Если предсказание является жестким порогом, равным 0 и 1, трудно обратно распространять dice loss .

    Затем мы сравним dice loss с кросс-энтропией, чтобы получить нашу функцию полной потери, которую вы можете найти в методе _criterion из nn.Classifier.CarvanaClassifier . Согласно оригинальной статье они также используют weight map в функции потери кросс-энтропии, чтобы придать некоторым пикселям большее ошибки во время тренировки. В нашем случае нам не нужна такая вещь, поэтому мы просто используем кросс-энтропию без какого-либо weight map.

    2. Оптимизатор

    Поскольку мы имеем дело не с биомедицинскими изображениями, мы будем использовать наши собственные augmentations . Код можно найти в img.augmentation.augment_img . Там мы выполняем случайное смещение, поворот, переворот и масштабирование.

    Тренировка нейронной сети

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

    Для этого вам нужно запустить Tensorboard в папке logs с помощью команды:

    Tensorboard --logdir=./logs

    Пример того, что вы сможете увидеть в Tensorboard после эпохи 1:

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

    (7.1)

    где - единственный параметр обработки, называемый порогом. Уровни выходной яркости и , могут быть произвольными, они лишь выполняют функции меток, при помощи которых осуществляется разметка получаемой карты - отнесение ее точек к классам или соответственно. Если образуемый препарат подготавливается для визуального восприятия, то часто их значения соответствуют уровням черного и белого. Если существует более двух классов, то при пороговой обработке должно быть задано семейство порогов, отделяющих яркости различных классов друг от друга.

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

    Пусть , - гистограмма исходного цифрового изображения. Примем, что его диапазон яркостей заключен в пределах от 0 (уровень черного) до 255 (уровень белого). Первоначальная идея гистограммного метода определения порога основывалась на предположении о том, что распределения вероятностей для каждого класса унимодальны (содержат по одному пику), а точки границ, разделяющих участки разных классов на изображении, малочисленны. Этим предположениям должна отвечать гистограмма, которая имеет многомодальный характер. Отдельные моды соответствуют различным классам, а разделяющие их впадины - малочисленным по количеству входящих в них точек граничным областям. Пороги сегментации находятся при этом по положению впадин. Рис. 7.1 иллюстрирует сказанное выше применительно к случаю двух классов. В действительности воспользоваться такими простыми соображениями для выбора порога удается крайне редко. Дело в том, что реальные гистограммы обычно сильно изрезаны, что иллюстрирует приводимый па рис.7.2, в результат эксперимента. Это служит первым препятствием для определения точек минимума. Вторым препятствием является то, что границы между однородными участками на изображении бывают размыты, вследствие чего уровень гистограммы в тех ее частях, которые отображают точки границы, возрастает. Очевидно, это приводит к уменьшению провалов в гистограмме или даже их исчезновению.

    Рис.7.1.К выбору порога бинарной сегментации

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

    Пусть - гипотетическое значение порога, разбивающее распределение , на два класса. При этом обычно не играет большой роли, к какому из классов будут отнесены точки изображения, имеющие яркость , в силу малочисленности граничных точек, разделяющие участки разных классов. Вероятность того, что наугад взятая точка кадра принадлежит классу , равна

    (7.2)

    Аналогично вероятность ее принадлежности к классу определяется формулой

    (7.3)

    причем в силу нормировки распределения вероятностей имеет место равенство

    Далее считаем, что участок распределения , , ограниченный точкой , описывает часть изображения, принадлежащую , а участок , - принадлежащую . Это позволяет ввести в рассмотрение два распределения и , соответствующих и , конструируя их из распределения при помощи выражений:

    Здесь делением на вероятности и обеспечивается нормировка вводимых условных распределений.

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

    (7.4)

    где - ненормированное математическое ожидание для , - математическое ожидание для всего кадра.

    Аналогично, дисперсия дня всего кадра определяется выражением

    (7.6)

    Для построения дискриминантной функции дополнительно вводим еще один энергетический параметр , называемый межклассовой дисперсией:

    Безразмерная дискриминантная функция определяется выражением

    (7.8)

    Оптимальным, как говорилось выше, считается порог, отвечающим требованию

    (7.9)

    Поясним смысл критерия (7.9). Знаменатель в выражении (7.8) является дисперсией всего кадра и, следовательно, от величины пробного порога , разбивающего изображение на классы, не зависит. Поэтому точка максимума выражения (7.8) совпадает с точкой максимума числителя, т.е. определяется характером зависимости межклассовой дисперсии (7.7) от порога . При его стремлении к нулю вероятность , как следует из (7.2), также стремится к нулю. Поскольку при этом все изображение относится к классу , имеет место тенденция . Следовательно, оба слагаемых в (7.7) становятся равными нулю. Это же наблюдается и при другом крайнем значении порога =255. В силу неотрицательности величин, входящих в (7.7) и (7.9), и равенства функции нулю на краях области определения, внутри этой области существует максимум, абсцисса которого и принимается за оптимальный порог. Следует отметить качественный характер этих соображений. Более детальные исследования показывают, например, что при обработке некоторых изображений дискриминантная функция имеет несколько максимумов даже при наличии на изображении только двух классов. Это, в частности, проявляется, когда суммарные площади участков, занятых классами и ,существенно различны. Поэтому задача в общем случае несколько усложняется необходимостью определить абсолютный максимум функции .

    С вычислительной точки зрения для выполнения алгоритма необходимо найти для всего изображения математическое ожидание и дисперсию . Далее при каждом значении определяются вероятности и с использованием (7.2) и (7.3) (или условия нормировки), а также математические ожидания классов и при помощи соотношений (7.4), (7.5). Найденные таким образом величины дают возможность определить значение .

    Объем вычислений можно сократить, если выполнить некоторые преобразования формулы (7.7) для межклассовой дисперсии. Используя формулы (7.2)...(7.5), нетрудно получить соотношение для математических ожиданий:

    (7.11)

    Выражая из (7.10) величину и подставляя ее в (7.11), окончательно находим:

    (7.12)

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

    На рис. 7.2 приведены результаты эксперимента, иллюстрирующие описанный метод автоматической бинарной сегментации. На рис.7.2, а показан аэрофотоснимок участка земной поверхности "Поле", а на рис.7.2, б – результат его бинарной сегментации, выполненной на основе автоматического определения порога при помощи дискриминантного метода. Гистограмма распределения исходного изображения показана на рис.7.2, в, а дискриминантная функция , вычисленная по полученной гистограмме - на рис. 7.2, г. Сильная изрезанность гистограммы, порождающая большое количество минимумов, исключает возможность непосредственного определения единственного информационного минимума, разделяющего классы друг от друга. Функция же является существенно более гладкой и к тому же в данном случае унимодальной, что делает определение порога весьма простой задачей. Оптимальный порог, при котором получено сегментированное изображение, =100. Результаты показывают, что описанный метод нахождения порога, являясьразвитием гистограммного подхода, обладает сильным сглаживающим действием по отношению к изрезанности самой гистограммы.

    Коснемся вопроса о пороговой сегментации нестационарных изображений. Если средняя яркость изменяется внутри кадра, то пороги сегментации должны быть также изменяющимися. Часто в этих случаях прибегают к разбиению кадра на отдельные области, в пределах которых изменениями средней яркости можно пренебречь. Это позволяет применять внутри отдельных областей принципы определения порогов, пригодные для работы со стационарными изображениями. На обработанном изображении наблюдаются в этом случае области, на которые разбито исходное изображение, отчетливо видны границы между областями. Это – существенный недостаток метода.

    Более трудоемка, но и более эффективна процедура, использующая скользящее окно, при которой каждое новое положение рабочей области отличается от предыдущего только на один шаг по строке или по столбцу. Находимый на каждом шаге оптимальный порог относят к центральной точке текущей области. Таким образом, при этом методе порог изменяется в каждой точке кадра, причем эти изменения имеют характер, сопоставимый с характером нестационарности самого изображения. Процедура обработки, конечно, существенно усложняется.

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

    Рис.7.2.Пример бинарной сегментации с автоматическим определением порога

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