OpenCV (36) --- используйте алгоритм водораздела для сегментации и извлечения изображений.

OpenCV
OpenCV (36) --- используйте алгоритм водораздела для сегментации и извлечения изображений.

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

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

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

алгоритм водораздела

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

Ниже блогер кратко представляет соответствующее содержание алгоритма водораздела. (Подробности см. в книге Гонсалеса «Цифровая обработка изображений»).

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

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

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

Если вы мало знаете о теории, вы можете использовать функцию «удалить фон» в программе PowerPoint, чтобы наблюдать и понимать.

водосборная функция

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

Морфологическая сегментация

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

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread("36.jpg")
k=np.ones((5,5),dtype=np.uint8)
e=cv2.erode(img,k)
result=cv2.subtract(img,e)

plt.subplot(131)
plt.imshow(img, cmap="gray")
plt.axis('off')

plt.subplot(132)
plt.imshow(e, cmap="gray")
plt.axis('off')

plt.subplot(133)
plt.imshow(result, cmap="gray")
plt.axis('off')
plt.show()

Напомним, что наша предыдущая функция открытой операции — cv2.erode(), здесь мы сначала удаляем шум посредством открытой операции. Затем операция вычитания cv2.subtract() получает границы изображения. После запуска эффект следующий:

效果

Функция DistanceTransform

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

cv2.distanceTransform() отражает отношение расстояния между каждым пикселем и фоном (пиксели со значением 0). как правило:

  1. Если центр объекта переднего плана находится дальше от пикселя со значением 0, он получит большее значение.
  2. Если край объекта переднего плана ближе к пикселю со значением 0, он получит меньшее значение.

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

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread("36.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
k = np.ones((5, 5), dtype=np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, k, iterations=2)
distTransform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
ret, fore = cv2.threshold(distTransform, 0.7 * distTransform.max(), 255, 0)

plt.subplot(131)
plt.imshow(img, cmap="gray")
plt.axis('off')

plt.subplot(132)
plt.imshow(distTransform, cmap="gray")
plt.axis('off')

plt.subplot(133)
plt.imshow(fore, cmap="gray")
plt.axis('off')
plt.show()

Здесь мы используем функцию cv2.morphologyEx для выполнения операции открытия и в то же время используем cv2.distanceTransform для получения изображения расстояния и, наконец, пороговое значение изображения расстояния с помощью cv2.threshold для определения переднего плана. После запуска эффект следующий:

前景

Определите неизвестные области

Через функцию расстояния получаем «центр» изображения, то есть «определяем передний план». Для удобства последующих объяснений мы будем называть детерминированный передний план F.

На изображении есть определенный передний план F и определенный задний план B, а оставшаяся область — это неизвестная область UN. Эта часть области является областью, где алгоритм водосбора нуждается в дальнейшем уточнении.

Для изображения 0 неизвестную область UN можно получить с помощью следующего соотношения:

Неизвестная область UN=изображение 0-определить фон B-определить передний план F

Преобразуйте приведенную выше формулу, чтобы получить:

Неизвестная область UN=(изображение 0 - определить фон B) - определить передний план F

где (изображение 0 — определить фон B) — это начатая нами операция вычитания, полученная путем морфологического расширения. Также просто добавьте 4 строки к приведенному выше коду и измените показанное содержимое кода:

bg=cv2.dilate(opening,k,iterations=3)
fore=np.uint8(fore)
un=cv2.subtract(bg,fore)

plt.subplot(221)
plt.imshow(img, cmap="gray")
plt.axis('off')

plt.subplot(222)
plt.imshow(bg, cmap="gray")
plt.axis('off')

plt.subplot(223)
plt.imshow(fore, cmap="gray")
plt.axis('off')

plt.subplot(224)
plt.imshow(un, cmap="gray")
plt.axis('off')
plt.show()

После запуска эффект следующий:

4附图

Слева вверху исходное изображение

Справа вверху — изображение bg, полученное после расширения исходного изображения, а его фоновое изображение — это определенный фон B. Изображение переднего плана: «Исходное изображение 0 — определить фон B».

Нижний левый — определить переднее изображение переднего плана.

Внизу справа изображение неизвестной местности ООН

Функция ConnectedComponents

После того, как определенные перспективы четко обозначены, определенные перспективы могут быть отмечены. В OpenCV он предоставляет функцию cv2.ConnectedComponents() для аннотации.

Эта функция пометит фон как 0, а другие объекты пометит положительными целыми числами, начиная с 1. Он имеет только один параметр 8-битного одноканального изображения для маркировки.

Есть два возвращаемых значения: retval — количество возвращаемых меток, а labels — результирующее изображение метки.

Далее воспользуемся этой функцией для аннотации. Код выглядит следующим образом (просто измените код ниже bg выше):

bg = cv2.dilate(opening, k, iterations=3)
fore = np.uint8(fore)
ret, markets = cv2.connectedComponents(fore)
unknown=cv2.subtract(bg,fore)
markets=markets+1
markets[unknown==255]=0

plt.subplot(131)
plt.imshow(img, cmap="gray")
plt.axis('off')

plt.subplot(132)
plt.imshow(fore, cmap="gray")
plt.axis('off')

plt.subplot(133)
plt.imshow(markets, cmap="gray")
plt.axis('off')
plt.show()

Измените приведенный выше код fore = np.uint8(fore) и измените вывод. После запуска мы получим исходное изображение, изображение центральной точки перед изображением переднего плана и помеченные результаты рынков изображений. Эффект следующий:

效果图

Практический алгоритм водораздела

После предыдущего введения мы узнали основные этапы использования алгоритма водораздела для сегментации изображения:

  1. Удаление шума исходного изображения 0 с помощью операции морфологического открытия
  2. «Определенный фон B» был получен в результате операции эрозии. Следует отметить, что здесь можно получить «исходное изображение — определить фон».
  3. Используйте функцию преобразования расстояния для работы с исходным изображением и выполните для него пороговую обработку, чтобы получить «определение переднего плана F».
  4. Вычислить неизвестную площадь UN (UN=0-B-F)
  5. Используйте функцию cv2.connectedComponents(), чтобы пометить исходное изображение 0
  6. Исправлен результат аннотации функции cv2.connectedComponents().
  7. Сегментация изображения выполняется с помощью функции водораздела.

Полный код выглядит следующим образом:

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread("36.jpg")
plt.subplot(121)
plt.imshow(img, cmap="gray")
plt.axis('off')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
k = np.ones((5, 5), dtype=np.uint8)
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, k, iterations=2)
distTransform = cv2.distanceTransform(opening, cv2.DIST_L2, 5)
ret, fore = cv2.threshold(distTransform, 0.2 * distTransform.max(), 255, 0)
bg = cv2.dilate(opening, k, iterations=3)
fore = np.uint8(fore)
ret, markets = cv2.connectedComponents(fore)
unknown = cv2.subtract(bg, fore)
markets = markets + 1
markets[unknown == 255] = 0
markets = cv2.watershed(img, markets)
img[markets == -1] = [255, 0, 0]

plt.subplot(122)
plt.imshow(img, cmap="gray")
plt.axis('off')
plt.show()

После запуска мы можем получить сегментированное изображение:

参数

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