Нахождение границ объектов, которые близки друг к другу - PullRequest
0 голосов
/ 09 февраля 2019

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

Ввод:

enter image description here

Вывод:

enter image description here

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

img = cv2.imread('mask.jpg', 0)
img = (255 - img)

kernel = np.ones((11,11), np.uint8) 
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)

kernel = np.ones((5,5), np.uint8) 
img_erosion = cv2.erode(gradient, kernel, iterations=3) 

img_erosion[img_erosion > 200] = 255
img_erosion[img_erosion <= 200] = 0

def get_contours(mask):
    contours, hierarchy = cv2.findContours(mask,cv2.RETR_TREE,cv2.cv2.CHAIN_APPROX_NONE)
    return contours

cnts = get_contours(img_erosion)

img_new = np.zeros_like(img_erosion)
img_h, img_w = img_erosion.shape
for i in cnts:
    if cv2.contourArea(i) > 30:
        print(cv2.boundingRect(i), cv2.contourArea(i))
        x, y, h, w = cv2.boundingRect(i)
        if h/w > 5 or w/h > 5 or cv2.contourArea(i) > 100:  ## Should be elongated 
            if (x - 10 > 0) and (y - 10 > 0): ## Check if near top or left edge
                if (img_w - x > 10) and (img_h - y > 10): ## Check if near bottom or right edge

                    cv2.drawContours(img_new, [i], -1, (255,255,255), 2)
kernel = np.ones((3,3), np.uint8) 
img_new = cv2.dilate(img_new, kernel, iterations=2)
plt.figure(figsize=(6,6))
plt.imshow(img_new)

Результат:

enter image description here

Но, используя этот подход, я должен настроить многиепараметров, и он не работает во многих случаях, когда ориентация отличается или края немного далеко, или если "L" -образные края и т. д.

Я новичок в обработке изображений, есть ли другой метод, который может мне помочьчтобы решить эту задачу эффективно?

Редактировать: Прикрепить еще несколько изображений

enter image description here

enter image description here

(В основном прямоугольные многоугольники, но много различий в размерах и относительных положениях)

1 Ответ

0 голосов
/ 09 февраля 2019

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

Stroke Width Transform example

После установки порога этого изображения вам сообщается, где есть края, разделенные небольшим расстоянием.Например, все пиксели со значениями <40, скажем, находятся между двумя ребрами, которые разделены менее чем на 40 пикселей. </p>

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

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


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

def find_connection_paths(binimg, distance):

    h, w = binimg.shape[:2]
    overlap = np.zeros((h, w), dtype=np.int32)
    overlap_mask = np.zeros((h, w), dtype=np.uint8)
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (distance, distance))

    # grows the blobs by `distance` and sums to get overlaps
    nlabels, labeled = cv2.connectedComponents(binimg, connectivity=8)
    for label in range(1, nlabels):
        mask = 255 * np.uint8(labeled == label)
        overlap += cv2.dilate(mask, kernel, iterations=1) // 255
    overlap = np.uint8(overlap > 1)

    # for each overlap, does the overlap touch the original blob?
    noverlaps, overlap_components = cv2.connectedComponents(overlap, connectivity=8)
    for label in range(1, noverlaps):
        mask = 255 * np.uint8(overlap_components == label)
        if np.any(cv2.bitwise_and(binimg, mask)):
            overlap_mask = cv2.bitwise_or(overlap_mask, mask)
    return overlap_mask

Connecting regions

Теперь вывод не идеален - когда яРасширив BLOB-объекты, я расширил их внешне с помощью круга (ядра расширения), поэтому области соединения не совсем четкие.Тем не менее, это был лучший способ убедиться, что он будет работать на вещах любой ориентации.Вы могли бы потенциально отфильтровать это / обрезать это.Простой способ сделать это состоит в том, чтобы получить каждый соединительный элемент (показан синим цветом) и многократно разрушать его на пиксель до тех пор, пока не не будет перекрывать исходный BLOB-объект.На самом деле хорошо, давайте добавим, что:

def find_connection_paths(binimg, distance):

    h, w = binimg.shape[:2]
    overlap = np.zeros((h, w), dtype=np.int32)
    overlap_mask = np.zeros((h, w), dtype=np.uint8)
    overlap_min_mask = np.zeros((h, w), dtype=np.uint8)
    kernel_dilate = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (distance, distance))

    # grows the blobs by `distance` and sums to get overlaps
    nlabels, labeled = cv2.connectedComponents(binimg)
    for label in range(1, nlabels):
        mask = 255 * np.uint8(labeled == label)
        overlap += cv2.dilate(mask, kernel_dilate, iterations=1) // 255
    overlap = np.uint8(overlap > 1)

    # for each overlap, does the overlap touch the original blob?
    noverlaps, overlap_components = cv2.connectedComponents(overlap)
    for label in range(1, noverlaps):
        mask = 255 * np.uint8(overlap_components == label)
        if np.any(cv2.bitwise_and(binimg, mask)):
            overlap_mask = cv2.bitwise_or(overlap_mask, mask)

    # for each overlap, shrink until it doesn't touch the original blob
    kernel_erode = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
    noverlaps, overlap_components = cv2.connectedComponents(overlap_mask)
    for label in range(1, noverlaps):
        mask = 255 * np.uint8(overlap_components == label)
        while np.any(cv2.bitwise_and(binimg, mask)):
            mask = cv2.erode(mask, kernel_erode, iterations=1)
        overlap_min_mask = cv2.bitwise_or(overlap_min_mask, mask)

    return overlap_min_mask

Minimum overlapping regions

Конечно, если вы все еще хотите, чтобы они были немного больше или меньше, вы могли бы делать все, что выкак с ними, но это выглядит довольно близко к вашему запросу вывода, поэтому я оставлю это там.Кроме того, если вам интересно, я понятия не имею, куда ушла капля справа вверху.Я могу сделать еще один пропуск на этой последней части позже.Обратите внимание, что последние два шага могут быть объединены;проверьте, есть ли перекрытие, если оно есть, остудите - уменьшите его и сохраните в маске.

...