Минимальная длина линии обнаружения краев?

#python #opencv #image-processing

Вопрос:

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

Я начинаю с того, что беру один канал изображения и запускаю CV2 для обнаружения хитрых краев. После этого я просматриваю каждый пиксель и определяю, есть ли вокруг него белые (Правда, 255). Если это так, я добавляю его в группу истинных пикселей, а затем проверяю каждый пиксель вокруг него (и продолжаю зацикливаться, пока не останется белых/истинных пикселей. Затем я заменяю все пиксели на черный/Ложный, если количество групп меньше заданного порогового значения (в данном случае 100 пикселей).

Хотя это работает (как показано ниже), это ужасно медленно. Мне интересно, есть ли более быстрый и простой способ сделать это.

 import cv2

img = cv2.imread("edtest.jpg")
img_r = img.copy()
img_r[:, :, 0] = 0
img_r[:, :, 1] = 0
img_r = cv2.GaussianBlur(img_r, (3, 3), 0)
basic_edge = cv2.Canny(img_r, 240, 250)

culled_edge = basic_edge.copy()
min_threshold = 100

for x in range(len(culled_edge)):
    print(x)
    for y in range(len(culled_edge[x])):
        test_pixels = [(x, y)]
        true_pixels = [(x, y)]

        while len(test_pixels) != 0:

            xorigin = test_pixels[0][0]
            yorigin = test_pixels[0][1]
            if 0 < xorigin < len(culled_edge) - 1 and 0 < yorigin < len(culled_edge[0]) - 1:
                for testx in range(3):
                    for testy in range(3):
                        if culled_edge[xorigin-1 testx][yorigin - 1   testy] == 255 and (xorigin-1 testx, yorigin-1 testy) not in true_pixels:
                            test_pixels.append((xorigin-1 testx, yorigin-1 testy))
                            true_pixels.append((xorigin-1 testx, yorigin-1 testy))
            test_pixels.pop(0)

        if 1 < len(true_pixels) < min_threshold:
            for i in range(len(true_pixels)):
                culled_edge[true_pixels[i][0]][true_pixels[i][1]] = 0

cv2.imshow("basic_edge", basic_edge)
cv2.imshow("culled_edge", culled_edge)
cv2.waitKey(0)
 

Исходное Изображение:

Исходное Изображение

Точное обнаружение и отфильтрованные (идеальные) результаты: введите описание изображения здесь

Комментарии:

1. Вы можете получить контуры своих краев, а затем отфильтровать область контура или периметр. Вы также можете использовать connectedComponentsWithStats() и фильтр по области.

Ответ №1:

Операция, которую вы применяете, называется открытием области. Я не думаю, что в OpenCV есть реализация, но вы можете найти ее либо в scikit-image ( skimage.morphology.area_opening ), либо в DIPlib ( dip.BinaryAreaOpening ).

Например, с помощью DIPlib (раскрытие информации: я автор) вы бы изменили свой код следующим образом:

 import diplib as dip

# ...
basic_edge = cv2.Canny(img_r, 240, 250)
min_threshold = 100
culled_edge = dip.BinaryAreaOpening(basic_edge > 0, min_threshold)
 

Вывод, culled_edge , теперь является dip.Image объектом, который совместим с массивами NumPy, и вы должны иметь возможность использовать его как таковой во многих ситуациях. Если есть проблема, то вы можете вернуть ее в массив NumPy с culled_edge = np.array(culled_edge) помощью .