Как различить guish заполненный круг / контур и незаполненный круг / контур в OpenCV? - PullRequest
3 голосов
/ 06 февраля 2020

Я не могу различить два нижних контура. cv2.contourArea дает одинаковое значение для обоих. Есть ли какая-либо функция для различения guish их в Python?

Fig: example circle/contour

1 Ответ

4 голосов
/ 07 февраля 2020

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

  • cv2.RETR_EXTERNAL - извлекаются только экстремальные внешние контуры (без иерархии)
  • cv2.RETR_LIST - извлекаются все контуры без установления каких-либо иерархических отношений
  • cv2.RETR_CCOMP - извлекает все контуры и организует их в двухуровневую иерархию. На верхнем уровне находятся внешние границы компонентов. На втором уровне есть границы отверстий. Если внутри отверстия подключенного компонента есть другой контур, он все равно помещается на верхний уровень
  • cv2.RETR_TREE - извлекает все контуры и восстанавливает полную иерархию вложенных контуров

Понимание контурных иерархий

Таким образом, с этой информацией мы можем использовать cv2.RETR_CCOMP или cv2.RETR_TREE для возврата списка иерархии. Возьмем, к примеру, это изображение:

image

Когда мы используем параметр cv2.RETR_TREE, контуры располагаются в иерархии, причем внешние контуры для каждого объекта находятся сверху. Двигаясь вниз по иерархии, каждый новый уровень контуров представляет следующий внутренний контур для каждого объекта. На изображении выше контуры на изображении окрашены, чтобы представить иерархическую структуру возвращенных данных контуров. Внешние контуры красного цвета, и они находятся на вершине иерархии. Следующие самые внутренние контуры - в данном случае косточки - зеленые.

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

(_, contours, hierarchy) = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

Третье возвращаемое значение, сохраненное в переменной hierarchy в этом коде, представляет собой трехмерный массив NumPy с одной строкой, X столбцы и «глубина» 4. Столбцы X соответствуют числу контуров, найденных функцией. Параметр cv2.RETR_TREE заставляет функцию находить внутренние контуры, а также внешние контуры для каждого объекта. Нулевой столбец соответствует первому контуру, первый столбец - второму и т. Д.

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

[next, previous, first child, parent]

Индекс next относится к следующему контуру на уровне иерархии этого контура, в то время как индекс previous относится к предыдущему контуру на уровне иерархии этого контура. Индекс первый дочерний элемент относится к первому контуру, который содержится внутри этого контура. Индекс parent относится к контуру, содержащему этот контур. Во всех случаях значение -1 указывает на отсутствие следующего , предыдущего , первого дочернего или родительского контура, по мере необходимости. Для более конкретного примера вот несколько примеров hierarchy значений. Значения указаны в квадратных скобках, а индексы контуров предшествуют каждой записи. Если вы распечатаете массив иерархии, вы получите что-то вроде этого

0:  [ 6 -1  1 -1]   18: [19 -1 -1 17]
1:  [ 2 -1 -1  0]   19: [20 18 -1 17]
2:  [ 3  1 -1  0]   20: [21 19 -1 17]
3:  [ 4  2 -1  0]   21: [22 20 -1 17]
4:  [ 5  3 -1  0]   22: [-1 21 -1 17]
5:  [-1  4 -1  0]   23: [27 17 24 -1]
6:  [11  0  7 -1]   24: [25 -1 -1 23]
7:  [ 8 -1 -1  6]   25: [26 24 -1 23]
8:  [ 9  7 -1  6]   26: [-1 25 -1 23]
9:  [10  8 -1  6]   27: [32 23 28 -1]
10: [-1  9 -1  6]   28: [29 -1 -1 27]
11: [17  6 12 -1]   29: [30 28 -1 27]
12: [15 -1 13 11]   30: [31 29 -1 27]
13: [14 -1 -1 12]   31: [-1 30 -1 27]
14: [-1 13 -1 12]   32: [-1 27 33 -1]
15: [16 12 -1 11]   33: [34 -1 -1 32]
16: [-1 15 -1 11]   34: [35 33 -1 32]
17: [23 11 18 -1]   35: [-1 34 -1 32]

Запись для первого контура - [6, -1, 1, -1]. Это представляет первый из самых внешних контуров; обратите внимание, что нет определенного порядка для контуров, например, они не сохраняются слева направо по умолчанию. Запись говорит нам, что следующим контуром кости является контур с индексом шесть, что в списке нет предыдущего контура, что первый контур внутри этого имеет индекс один, и что для этого контура нет родителя (нет контура, содержащего контур). этот). Мы можем визуализировать информацию в массиве hierarchy в виде семи деревьев, по одному для каждой кости на изображении.

enter image description here

Семь самых внешних контуров все те, у кого нет родителя, т.е. те, которые имеют значение -1 в четвертом поле их записи hierarchy. Каждый из дочерних узлов под одним из «корней» представляет контур внутри самого внешнего контура. Обратите внимание, как контуры 13 и 14 находятся ниже контура 12 на диаграмме. Эти два контура представляют самые внутренние контуры, возможно, шум или потерянную краску в одном из пунктов. Как только мы поймем, как контуры организованы в иерархию, мы можем выполнять более сложные задачи, такие как подсчет количества контуров в фигуре в дополнение к количеству объектов в изображении.


Возвращаясь к вашему вопросу, мы можем использовать иерархию, чтобы различать guish между внутренним и внешним контурами, чтобы определить, заполнен или не заполнен контур. Мы можем определить заполненный контур как контур без дочерних элементов, а незаполненный контур - как минимум с одним дочерним элементом. Итак, с этим снимком экрана вашего входного изображения (снято поле):

enter image description here

Результат

enter image description here

Код

import cv2
import numpy as np

# Load image, grayscale, Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Filter using contour hierarchy
cnts, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
hierarchy = hierarchy[0]
for component in zip(cnts, hierarchy):
    currentContour = component[0]
    currentHierarchy = component[1]
    x,y,w,h = cv2.boundingRect(currentContour)
    # Has inner contours which means it is unfilled
    if currentHierarchy[3] > 0:
        cv2.putText(image, 'Unfilled', (x,y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (36,255,12), 2)
    # No child which means it is filled
    elif currentHierarchy[2] == -1:
        cv2.putText(image, 'Filled', (x,y-5), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (36,255,12), 2)

cv2.imshow('image', image)
cv2.waitKey()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...