Удаление изолированных пикселей с помощью 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 говорится:

Следовательно, операция попадания или пропуска состоит из трех этапов:

  1. Удалите изображение A с помощью структурирующего элемента B1.
  2. Удалите дополнение изображения A (A_c) с помощью структурирующего элемента B2.
  3. И результаты от шага 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
  1. Запустите маркировку подключенных компонентов
  2. Рассчитать количество пикселей каждого компонента
  3. Для каждого компонента с количеством пикселей меньше мин. Необходимо преобразовать все пиксели компонента в ноль.
Другие вопросы по тегам