Контуры/линии заполнения зазоров

У меня есть следующее изображение:

Изображение с размытыми контурами

и я хотел бы заполнить его контуры (т.е. я хотел бы заполнить пробелы на этом изображении).

Я пробовал морфологическое закрытие, но использование прямоугольного ядра размером 3x3 с 10 итерациями не заполняет всю границу. Я также пробовал ядро ​​21x21 с итерацией 1, и мне тоже не повезло.

ОБНОВЛЕНИЕ:

Я пробовал это в OpenCV (Python), используя:

cv2.morphologyEx(img, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_RECT, (21,21)))

и

cv2.morphologyEx(img, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_RECT, (3,3)), iterations=10)

и scikit-image:

closing(img, square(21))

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


person Alex Rothberg    schedule 21.01.2015    source источник
comment
Указанный вами структурный элемент, вероятно, слишком мал. Если вы делаете 3 x 3, ядро ​​​​слишком мало, чтобы соединить соседние регионы вместе. Вы пытались увеличить размер? А может 9х9? Кроме того, какая платформа? Я вижу, вы отметили этот вопрос как OpenCV, но не указали, на каком языке. С++ или Питон?   -  person rayryeng    schedule 22.01.2015
comment
Подумайте о расширении, чтобы соединить все промежутки вместе, а затем используйте морфологическое прореживание, чтобы уменьшить объект до его минимального представления. Вот алгоритм, использующий OpenCV C++: opencv-code. com/quick-tips/ — вы можете легко транскрибировать это на Python. При этом используется классический алгоритм Чжан-Суэна.   -  person rayryeng    schedule 22.01.2015


Ответы (2)


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

обратите внимание на пробел

from skimage import io, morphology, img_as_bool, segmentation
from scipy import ndimage as ndi
import matplotlib.pyplot as plt

image = img_as_bool(io.imread('/tmp/gaps.png'))
out = ndi.distance_transform_edt(~image)
out = out < 0.05 * out.max()
out = morphology.skeletonize(out)
out = morphology.binary_dilation(out, morphology.selem.disk(1))
out = segmentation.clear_border(out)
out = out | image

plt.imshow(out, cmap='gray')
plt.imsave('/tmp/gaps_filled.png', out, cmap='gray')
plt.show()
person Stefan van der Walt    schedule 22.01.2015
comment
Это решение не работает, если черная рамка вокруг целевого изображения увеличивается. Например, используя OpenCV: image = cv2.copyMakeBorder(image, 200, 200, 200, 200, cv2.BORDER_CONSTANT) - person Alex Rothberg; 22.01.2015
comment
Просто поиграйте с порогом. Для этого изображения у меня сработало значение 0,01. - person Stefan van der Walt; 23.01.2015
comment
Я надеялся, что решение, которое, по крайней мере, устойчиво к изменениям, - это размер черной рамки. - person Alex Rothberg; 23.01.2015
comment
Вы хотя бы играли с кодом, чтобы увидеть, что он делает? - person Stefan van der Walt; 23.01.2015
comment
Это хороший ответ. Несмотря на общее мнение, я действительно запускал код. Выглядит неплохо! - person rayryeng; 23.01.2015

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

введите здесь описание изображения

import cv2
import numpy as np

image = cv2.imread('lineswithgaps.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

# apply dilation on src image
kernel = np.ones((3,3),np.uint8)
dilated_img = cv2.dilate(gray, kernel, iterations = 2)

cv2.imshow("filled gaps for contour detection", dilated_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

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

введите здесь описание изображения

canvas = dilated_img.copy() # Canvas for plotting contours on
canvas = cv2.cvtColor(canvas, cv2.COLOR_GRAY2RGB) # create 3 channel image so we can plot contours in color

contours, hierarchy = cv2.findContours(dilated_img, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)

# loop through the contours and check through their hierarchy, if they are inner contours
# more here: https://docs.opencv.org/master/d9/d8b/tutorial_py_contours_hierarchy.html
for i,cont in enumerate(contours):
    # look for hierarchy[i][3]!=-1, ie hole boundaries
    if ( hierarchy[0][i][3] != -1 ):
        #cv2.drawContours(canvas, cont, -1, (0, 180, 0), 1) # plot inner contours GREEN
        cv2.fillPoly(canvas, pts =[cont], color=(0, 180, 0)) # fill inner contours GREEN
    else:
        cv2.drawContours(canvas, cont, -1, (255, 0, 0), 1) # plot all others BLUE, for completeness

cv2.imshow("Contours detected", canvas)
cv2.waitKey(0)
cv2.destroyAllWindows()
person Steven    schedule 01.01.2021