Thresholding


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

Люди, знакомы с фильмом «Терминатор», наверняка согласятся, что это был величайший научно-фантастический фильм той эпохи. В фильме Джеймс Кэмерон представил интересную концепцию визуальных эффектов, которая позволила зрителям скрыться за глазами киборга под названием Терминатор. Этот эффект стал известен как «Терминаторное видение» (англ. Terminator Vision). В некотором смысле, он отделял силуэты людей от фона. Тогда это могло звучать совершенно неуместно, но сегментация изображений сегодня является важной частью многих методов обработки изображений.

Сегментация изображения


Имеются ряд библиотек, написанных для анализа изображений. В этой статье мы подробно обсудим scikit-image, библиотеку обработки изображений на среде Python.

Scikit-image


image

Scikit-image — это библиотека Python, предназначенная для обработки изображений.

Установка


scikit-image устанавливается следующим образом:

pip install -U scikit-image(Linux and OSX)
pip install scikit-image(Windows)
# For Conda-based distributions
conda install scikit-image



Обзор изображений в Python


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

Импорт GrayScale Image из библиотеки skimage


Модуль данных skimage содержит несколько встроенных примеров наборов данных, которые обычно хранятся в формате jpeg или png.

from skimage import data
import numpy as np
import matplotlib.pyplot as plt
image = data.binary_blobs()
plt.imshow(image, cmap='gray')

Импорт цветного изображения из библиотеки skimage


from skimage import data
import numpy as np
import matplotlib.pyplot as plt
image = data.astronaut()
plt.imshow(image)



Импорт изображения из внешнего источника


# The I/O module is used for importing the image
from skimage import data
import numpy as np
import matplotlib.pyplot as plt
from skimage import io
image = io.imread('skimage_logo.png')
plt.imshow(image);



Загрузка нескольких изображений


images = io.ImageCollection('../images/*.png:../images/*.jpg')
print('Type:', type(images))
images.files
Out[]: Type: <class ‘skimage.io.collection.ImageCollection’>

Сохранение изображений


#Saving file as ‘logo.png’
io.imsave('logo.png', logo)

Сегментация изображения


Теперь, когда у нас есть представление о scikit-image, предлагаем рассмотреть детали сегментации изображений. Сегментация изображения – это процесс разделения цифрового изображения на несколько сегментов, чтобы упростить и / или изменить представление изображения на что-то более значимое и более простое для анализа.

В этой статье мы рассмотрим алгоритмы для моделей обучаемые как с учителем (supervised) так и без учителя (unsupervised).


Некоторые из алгоритмов сегментации доступны в библиотеке scikit-image

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

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

Давайте попробуем это на изображении учебника, который поставляется с предустановленным набором данных scikit-image.

Обычный импорт


import numpy as np
import matplotlib.pyplot as plt
import skimage.data as data
import skimage.segmentation as seg
import skimage.filters as filters
import skimage.draw as draw
import skimage.color as color

Простая функция для построения изображений

def image_show(image, nrows=1, ncols=1, cmap='gray'):
    fig, ax = plt.subplots(nrows=nrows, ncols=ncols, figsize=(14, 14))
    ax.imshow(image, cmap='gray')
    ax.axis('off')
    return fig, ax

Образ


text = data.page()
image_show(text)



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

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

fig, ax = plt.subplots(1, 1)
ax.hist(text.ravel(), bins=32, range=[0, 256])
ax.set_xlim(0, 256);



Наш пример оказался 8-битным изображением, поэтому мы имеем 256 возможных значений по оси X. По гистограмме видно, что существует концентрация довольно светлых пикселей (0: черный, 255: белый). Скорее всего, это наш довольно светлый текстовый фон, но остальное немного размыто. Идеальная гистограмма сегментации была бы бимодальной, чтобы мы могли выбрать число прямо посередине. Теперь давайте попробуем создать несколько сегментированных изображений на основе простого порогового значения.

Контролируемый порог


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

text_segmented = text > (value concluded from histogram i.e 50,70,120 )
image_show(text_segmented);


Слева: текст> 50 | Середина: текст> 70 | Справа: текст> 120

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

Неконтролируемый порог


Неконтролируемый порог Scikit-image имеет ряд автоматических методов определения порога, которые не требуют ввода при выборе оптимального порога. Вот некоторые из методов: otsu, li, local.

text_threshold = filters.threshold_  # Hit tab with the cursor after the underscore to get all the methods.
image_show(text < text_threshold);


Слева otsu || Справа: li

В случае local, нам также нужно указать block_size. Offset помогает настроить изображение для лучших результатов.

text_threshold = filters.threshold_local(text,block_size=51, offset=10) 
image_show(text > text_threshold);



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

Сегментация с алгоритмом для модели с учителем


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

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

# import the image
from skimage import io
image = io.imread('girl.jpg') 
plt.imshow(image);



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

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

image_gray = color.rgb2gray(image) 
image_show(image_gray);



Мы будем использовать два метода сегментации, которые работают на совершенно разных принципах.

Активная контурная сегментация (англ. Active contour segmentation)


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

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

def circle_points(resolution, center, radius):
"""
    Generate points which define a circle on an image.Centre refers to the centre of the circle
    """   
    radians = np.linspace(0, 2*np.pi, resolution)
c = center[1] + radius*np.cos(radians)#polar co-ordinates
    r = center[0] + radius*np.sin(radians)
    
    return np.array([c, r]).T
# Exclude last point because a closed path should not have duplicate points
points = circle_points(200, [80, 250], 80)[:-1]

Вышеприведенные вычисления вычисляют координаты x и y точек на периферии круга. Так как мы дали разрешение 200, оно вычислит 200 таких точек.

fig, ax = image_show(image)
ax.plot(points[:, 0], points[:, 1], '--r', lw=3)



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



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

snake = seg.active_contour(image_gray, points,alpha=0.06,beta=0.3)
fig, ax = image_show(image)
ax.plot(points[:, 0], points[:, 1], '--r', lw=3)
ax.plot(snake[:, 0], snake[:, 1], '-b', lw=3);



Сегментация случайного прохождения (англ. Random walker segmentation)


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

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

image_labels = np.zeros(image_gray.shape, dtype=np.uint8)

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

indices = draw.circle_perimeter(80, 250,20)#from here
image_labels[indices] = 1
image_labels[points[:, 1].astype(np.int), points[:, 0].astype(np.int)] = 2
image_show(image_labels);


Теперь давайте использовать Random Walker и посмотрим, что произойдет.

image_segmented = seg.random_walker(image_gray, image_labels)
# Check our results
fig, ax = image_show(image_gray)
ax.imshow(image_segmented == 1, alpha=0.3);



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

image_segmented = seg.random_walker(image_gray, image_labels, beta = 3000)
# Check our results
fig, ax = image_show(image_gray)
ax.imshow(image_segmented == 1, alpha=0.3);



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

Сегментация без учителя


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

Простая Линейно-итеративная Кластеризация


Метод ( англ. Simple Linear Iterative Clustering или SLIC) использует алгоритм машинного обучения под названием K-Means. Он принимает все значения пикселей изображения и пытается разделить их на заданное количество подобластей. Прочитайте эту работу для подробной инфомации.

SLIC работает с разными цветами, поэтому мы будем использовать исходное изображение.

image_slic = seg.slic(image,n_segments=155)

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

# label2rgb replaces each discrete label with the average interior color
image_show(color.label2rgb(image_slic, image, kind='avg'));



Мы сократили это изображение с 512 * 512 = 262 000 пикселей до 155 сегментов.

Felzenszwalb


Этот метод также использует алгоритм машинного обучения, называемый кластеризацией минимально охватывающего дерева (англ. minimum-spanning tree clustering). Felzenszwaib не сообщает нам точное количество кластеров, на которые будет разделено изображение. Он будет генерировать столько кластеров, сколько он считает нужным для этого.

image_felzenszwalb = seg.felzenszwalb(image) 
image_show(image_felzenszwalb);



На рисунке слишком много регионов. Давайте подсчитаем количество уникальных сегментов.

np.unique(image_felzenszwalb).size
3368

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

image_felzenszwalb_colored = color.label2rgb(image_felzenszwalb, image, kind='avg')
image_show(image_felzenszwalb_colored);

Теперь мы получаем меньше количество сегментов. Если бы мы хотели еще меньше сегментов, мы могли бы изменить параметр scale. Этот подход иногда называют избыточной сегментацией (англ. over-segmentation).



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

Заключение


Сегментация изображения является очень важным этапом обработки изображения. Это активная область исследований с различными приложениями, начиная от компьютерного зрения и заканчивая медицинскими изображениями, дорожным движением и видеонаблюдением. Python предоставляет надежную библиотеку scikit-image, имеющую большое количество алгоритмов обработки изображений. Он доступен бесплатно и без ограничений, за которым стоит активное сообщество. Рекомендую ознакомьтесь с их документацией. Оригинал статьи можно находится тут.

Комментарии (0)