4 из 9 для Введение в обработку изображений

Обнаружение больших двоичных объектов в действии

Что такое блобы и почему они важны?

В области обработки изображений обнаружение пятен и анализ связанных компонентов являются основными методами идентификации и анализа отдельных объектов или областей на изображении. В этом сообщении блога мы рассмотрим три популярных метода обнаружения больших двоичных объектов — лапласиан гауссова (LoG), разность гауссова (DoG) и детерминант гессиана ( DoH) — а также использование функции regionprops из библиотеки scikit-image для анализа подключенных компонентов. Я предоставлю фрагменты кода и примеры реализации, чтобы помочь вам лучше понять эти методы.

Обнаружение больших двоичных объектов

Что вообще такое blob?

Кляксы определяются как яркие объекты на темном фоне или наоборот.

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

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

from skimage.io import imread, imshow
from skimage.color import rgb2gray

im = rgb2gray(imread('blobs.png'))
imshow(im);

Мы использовали функцию rgb2gray в scikit-image, чтобы преобразовать изображение RGB в оттенки серого.

Чтобы преобразовать это изображение в градациях серого в бинарное изображение, нам нужно установить порог. Для этого упражнения примем пороговое значение 0,5.

im_bw = (im < 0.5) * 1
im_mask = im < 0.5
imshow(im_bw, cmap='gray');

im_bw и im_mask аналогичны друг другу: первое состоит из 0 и 1, а второе — из логических значений (True или False). Оба они понадобятся нам для следующей реализации.

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

Лапласиан гаусса (LoG)

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

from skimage.feature import blob_log

blobs = blob_log(im_mask, max_sigma=30, num_sigma=10, threshold=0.1)

fig, ax = plt.subplots()
ax.imshow(im_bw, cmap='gray')
for blob in blobs:
    y, x, area = blob
    ax.add_patch(plt.Circle((x, y), area*np.sqrt(2), color='y', fill=False))
plt.show()

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

Разница Гаусса (DoG)

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

from skimage.feature import blob_dog

blobs = blob_dog(im_mask, max_sigma=30, threshold=.1)

fig, ax = plt.subplots()
ax.imshow(im_bw, cmap='gray')
for blob in blobs:
    y, x, area = blob
    ax.add_patch(plt.Circle((x, y), area*np.sqrt(2), color='g', fill=False))
plt.show()

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

Определитель гессиана (DoH)

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

from skimage.feature import blob_doh

blobs = blob_doh(im_mask, max_sigma=30, threshold=.01)

fig, ax = plt.subplots()
ax.imshow(im_bw, cmap='gray')
for blob in blobs:
    y, x, area = blob
    ax.add_patch(plt.Circle((x, y), area*np.sqrt(2), color='r', fill=False))
plt.show()

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

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

Подключенные компоненты

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

Функция regionprops из библиотеки scikit-image предоставляет удобный способ извлечения свойств подключенных компонентов.

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

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

Кляксы определяются как яркие объекты на темном фоне или наоборот.

Вы не знаете который час? Пришло время бинаризировать наше изображение!

import skimage.io as io
import skimage.color as color
from skimage.filters import threshold_otsu

image = io.imread('rbc.jpeg')


gray_image = color.rgb2gray(image)
threshold = threshold_otsu(gray_image)
binary_image = gray_image < threshold
imshow(binary_image, cmap='gray');

Из реализации с использованием LoG, DoG и DoH вы, возможно, помните, что наш порог бинаризации установлен на 0,5 просто так. В этой реализации эвристический подход к установке порога бинаризации заменен методом Оцу.

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

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

from skimage.morphology import binary_opening, disk

# Apply morphological opening to separate connected cells
selem = disk(3)
opened_image = binary_opening(binary_image, selem)
imshow(opened_image);

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

from skimage.measure import label, regionprops

labeled_components = label(opened_image)
component_props = regionprops(labeled_components)

# Count the number of cells
num_cells = len(component_props)
print(f"Number of cells: {num_cells}")

# Calculate the mean area of the cells
areas = [region.area for region in component_props]
mean_area = np.mean(areas)

# Plot a histogram of cell sizes
fig, ax = plt.subplots(figsize=(10, 5))
plt.hist(areas, bins=30)
plt.xlabel("Area")
plt.ylabel("Frequency")
plt.show()

Number of cells: 371

На гистограмме виден высокий пик ближе к 0-му концу области. Это точки шума, сгенерированные при бинаризации изображения. Хотя мы можем все вместе называть их шумом в контексте обработки изображений, возможно, существуют компоненты крови, которые значительно меньше красных кровяных телец (эритроцитов), например тромбоциты. Между тем, в крайнем правом углу спектра мы, возможно, можем увидеть эритроциты, которые не были разделены нашими методами предварительной обработки изображения, такими как opening, который мы выполняли ранее. Игнорируя эти потенциальные выбросы, среднее значение распределения, по-видимому, находится в диапазоне 600–700.

Теперь, когда мы использовали regionprops для идентификации и сегментации эритроцитов на изображении, мы можем просто отфильтровать их по площади, чтобы идентифицировать потенциально неправильные клетки. Для следующей реализации мы выделим эритроциты, которые на 10%, 30% и 90% больше, чем средняя площадь клеток, соответственно.

# Cells above this threshold considered irregular
thresholds = [1.1, 1.3, 1.9]

for t in thresholds:
    highlighted_image = np.copy(image)
    for region in component_props:
        if region.area > t * mean_area:
            for coord in region.coords:
                highlighted_image[coord[0], coord[1]] = [255, 0, 0]

    plt.imshow(highlighted_image)
    plt.axis("off")
    plt.show()

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

Заключение

Такие методы обнаружения блоба, как лапласиан гаусса (LoG), разность гаусса (DoG) и детерминант гессиана (DoH), предоставляют ценные методы. для идентификации объектов на изображениях, каждый со своими сильными и слабыми сторонами. Еще более надежный метод обнаружения блоба, связанные компоненты (через такие функции, как regionprops) позволяют маркировать и анализировать интересующие области.

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