Удаление изолированных пикселей с помощью OpenCV
Я ищу способ удаления изолированных белых пикселей из двоичного изображения с помощью OpenCV. Подобный вопрос ( OpenCV избавиться от изолированных пикселей) имеет кучу "ответов", но ни один из них, похоже, не работает для меня. Я также безуспешно пробовал разные комбинации открытия и закрытия.
Статья здесь:
https://homepages.inf.ed.ac.uk/rbf/HIPR2/hitmiss.htm
Предполагается, что я могу использовать операцию удара или пропуска именно для этой цели:
1 используется для определения местоположения изолированных точек в двоичном изображении
И причина в том, что 0 интерпретируются иначе, чем когда они используются с эрозией / расширением напрямую (где 0 интерпретируются как "все равно", а не "не белый", что в основном и является тем, что мне нужно). Однако использование этого ядра просто отображает исходное изображение.
Мое входное изображение это:
Вы увидите несколько белых пикселей рядом с левой стороной изображения, от которых я бы хотел избавиться.
Вот код:
kernel = np.array([ [0, 0, 0],
[0, 1, 0],
[0, 0, 0]],np.uint8)
hitormiss = cv2.morphologyEx(input_image, cv2.MORPH_HITMISS, kernel)
cv2.imshow('hitormiss', hitormiss)
Как правильно удалять подобные пиксели?
Обновление: ответ Александра работает как очарование и является самым быстрым решением. Другой ответ также предоставляет решение, которое заключается в использовании функции cv2.connectedComponents, но она намного более интенсивно использует процессор. Вот функция, которая использует этот подход:
def remove_isolated_pixels(self, image):
connectivity = 8
output = cv2.connectedComponentsWithStats(image, connectivity, cv2.CV_32S)
num_stats = output[0]
labels = output[1]
stats = output[2]
new_image = image.copy()
for label in range(num_stats):
if stats[label,cv2.CC_STAT_AREA] == 1:
new_image[labels == label] = 0
return new_image
3 ответа
Я считаю, что реализация OpenCV была нарушена. Была проблема, связанная с GitHub OpenCV, которая, кажется, объединила запрос на удаление, чтобы исправить; Я думаю, что он был добавлен в OpenCV 3.3-rc, как указано в запросе на получение, так что, надеюсь, это должно быть исправлено при следующем обновлении OpenCV. Я не уверен, что проблема вызвана тем же или нет.
Креативное решение по выбранному ответу великолепно, но я согласен с вами: должен быть лучший путь, несмотря на неработающую реализацию.
В OpenCV Hit-or-Miss Tutorial говорится:
Следовательно, операция попадания или пропуска состоит из трех этапов:
- Удалите изображение A с помощью структурирующего элемента B1.
- Удалите дополнение изображения A (A_c) с помощью структурирующего элемента B2.
- И результаты от шага 1 и шага 2.
Далее говорится, что этого можно достичь с помощью одного ядра в преобразовании типа "попал или не попал", но, как мы знаем, оно не работает. Итак, давайте сделаем эти шаги вместо этого.
import cv2
import numpy as np
# load image, ensure binary, remove bar on the left
input_image = cv2.imread('calc.png', 0)
input_image = cv2.threshold(input_image, 254, 255, cv2.THRESH_BINARY)[1]
input_image_comp = cv2.bitwise_not(input_image) # could just use 255-img
kernel1 = np.array([[0, 0, 0],
[0, 1, 0],
[0, 0, 0]], np.uint8)
kernel2 = np.array([[1, 1, 1],
[1, 0, 1],
[1, 1, 1]], np.uint8)
hitormiss1 = cv2.morphologyEx(input_image, cv2.MORPH_ERODE, kernel1)
hitormiss2 = cv2.morphologyEx(input_image_comp, cv2.MORPH_ERODE, kernel2)
hitormiss = cv2.bitwise_and(hitormiss1, hitormiss2)
cv2.imshow('isolated.png', hitormiss)
cv2.waitKey()
А затем удалить, это так же просто, как инвертировать hitormiss
и используя это как mask
в cv2.bitwise_and()
с input_image
,
hitormiss_comp = cv2.bitwise_not(hitormiss) # could just use 255-img
del_isolated = cv2.bitwise_and(input_image, input_image, mask=hitormiss_comp)
cv2.imshow('removed.png', del_isolated)
cv2.waitKey()
Вот как я это решил:
import cv2 as cv
import numpy as np
# let's say "image" is a thresholded image
kernel = np.array([ [-1, -1, -1],
[-1, 1, -1],
[-1, -1, -1] ], dtype="int")
single_pixels = cv.morphologyEx(image, cv.MORPH_HITMISS, kernel)
single_pixels_inv = cv.bitwise_not(single_pixels)
image = cv.bitwise_and(image, image, mask=single_pixels_inv)
# now "image" shouldn't have alone pixels
- Запустите маркировку подключенных компонентов
- Рассчитать количество пикселей каждого компонента
- Для каждого компонента с количеством пикселей меньше мин. Необходимо преобразовать все пиксели компонента в ноль.