Сортировка связанной иерархии с уже отсортированными контурами в OpenCv в Python

Aadit спросил: 28 марта 2018 в 04:13 в: python

Я извлекаю самые внутренние контуры из изображения (input.png), используя следующий код
( Я использую Python 3.6.3 и opencv-python == 3.4.0.12 )

input.png

import copy
import cv2BLACK_THRESHOLD = 200
THIN_THRESHOLD = 10
ANNOTATION_COLOUR = (0, 0, 255)img = cv2.imread('input.png')
orig = copy.copy(img)
gray = cv2.cvtColor(img, 6)
thresh = cv2.threshold(gray, thresh=BLACK_THRESHOLD, maxval=255, type=cv2.THRESH_BINARY_INV)[1]# Find the contours
_, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)hierarchy = hierarchy[0]  # get the actual inner list of hierarchy descriptions
idx = 0
# For each contour, find the bounding rectangle and extract it
for component in zip(contours, hierarchy):
    currentContour = component[0]
    currentHierarchy = component[1]
    x, y, w, h = cv2.boundingRect(currentContour)
    roi = img[y+2:y + h-2, x+2:x + w-2]
    # Skip thin contours (vertical and horizontal lines)
    if h < THIN_THRESHOLD or w < THIN_THRESHOLD:
        continue
    if h > 300 and w > 300:
        continue
    if h < 40 or w < 40:
        continue
    if currentHierarchy[3] > 0:
        # these are the innermost child components
        idx += 1
        cv2.imwrite(str(idx) + '.png', roi)

Result:

Как вы можете видеть, извлеченные изображения не находятся в каком-либо определенном порядке. Поэтому, чтобы исправить эти I отсортированные контуры на основе их координат оси x . Ниже приведен код:

import copy
import cv2BLACK_THRESHOLD = 200
THIN_THRESHOLD = 10
ANNOTATION_COLOUR = (0, 0, 255)img = cv2.imread('input.png')
orig = copy.copy(img)
gray = cv2.cvtColor(img, 6)
thresh = cv2.threshold(gray, thresh=BLACK_THRESHOLD, maxval=255, type=cv2.THRESH_BINARY_INV)[1]# Find the contours
_, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)# Sort Contours on the basis of their x-axis coordinates in ascending order
def sort_contours(cnts, method="left-to-right"):
    # initialize the reverse flag and sort index
    reverse = False
    i = 0
    # handle if we need to sort in reverse
    if method == "right-to-left" or method == "bottom-to-top":
        reverse = True
    # handle if we are sorting against the y-coordinate rather than
    # the x-coordinate of the bounding box
    if method == "top-to-bottom" or method == "bottom-to-top":
        i = 1
    # construct the list of bounding boxes and sort them from top to
    # bottom
    boundingBoxes = [cv2.boundingRect(c) for c in cnts]
    (cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
                                        key=lambda b: b[1][i], reverse=reverse))
    # return the list of sorted contours
    return cntssorted_contours = sort_contours(contours)idx = 0
# For each contour, find the bounding rectangle and extract it
for component in sorted_contours:
    currentContour = component
    x, y, w, h = cv2.boundingRect(currentContour)
    roi = img[y + 2:y + h - 2, x + 2:x + w - 2]
    # Skip thin contours (vertical and horizontal lines)
    if h < THIN_THRESHOLD or w < THIN_THRESHOLD:
        continue
    if h > 300 and w > 300:
        continue
    if h < 40 or w < 40:
        continue
    idx += 1
    print(x, idx)
    cv2.imwrite(str(idx) + '.png', roi)

Результат:

Это отлично упорядочило контуры. Но теперь, как вы видите, я получаю все контуры (, что является причиной двух копий каждой цифры ) , потому что я не использую иерархию но когда я потратил некоторое время на отладку, я понял, что сортируются только контуры, но не связанная с ними иерархия . Так кто может рассказать мне, как сортировать иерархию вместе с контурами, чтобы я мог получить только самые сокровенные контуры отсортированных контуров. Спасибо!


1 ответ

Есть решение
Dan Mašek ответил: 28 марта 2018 в 09:45

Давайте начнем с вашего первого сценария, поскольку он дал вам хорошие результаты, которые просто не были правильно отсортированы.

Обратите внимание, что это единственное решение, основанное на иерархии (когда вы решаете, следует ли обрабатывать данный контур как цифра) currentHierarchy[3] > 0 Почему бы нам не начать с выбора только контуров, соответствующих этому критерию, и выполнить дальнейшую обработку только для этого подмножества (больше не нужно заботиться об иерархии).

# Find the contours
_, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)hierarchy = hierarchy[0]  # get the actual inner list of hierarchy descriptions# Grab only the innermost child components
inner_contours = [c[0] for c in zip(contours, hierarchy) if c[1][3] > 0]

Теперь у нас остались только интересующие нас контуры, мы просто должны их отсортировать. Мы можем повторно использовать упрощенную версию вашей исходной функции сортировки:

# Sort Contours on the basis of their x-axis coordinates in ascending order
def sort_contours(contours):
    # construct the list of bounding boxes and sort them from top to bottom
    boundingBoxes = [cv2.boundingRect(c) for c in contours]
    (contours, boundingBoxes) = zip(*sorted(zip(contours, boundingBoxes)
       , key=lambda b: b[1][0], reverse=False))
    # return the list of sorted contours
    return contours

и получить отсортированные контуры:

sorted_contours = sort_contours(inner_contours)

Наконец, мы хотим отфильтровать мусор и вывести правильные контуры с правильной маркировкой:

MIN_SIZE = 40
MAX_SIZE = 300
THIN_THRESHOLD = max(10, MIN_SIZE)
PADDING = 2# ...idx = 0
# For each contour, find the bounding rectangle and extract it
for contour in sorted_contours:
    x, y, w, h = cv2.boundingRect(contour)
    roi = img[(y + PADDING):(y + h - PADDING), (x + PADDING):(x + w - PADDING)]
    # Skip thin contours (vertical and horizontal lines)
    if (h < THIN_THRESHOLD) or (w < THIN_THRESHOLD):
        continue
    if (h > MAX_SIZE) and (w > MAX_SIZE):
        continue
    idx += 1
    cv2.imwrite(str(idx) + '.png', roi)

Full Script (используя Python 2.7.x и OpenCV 3.4 1

Aadit ответил: 29 марта 2018 в 05:49
Работал как шарм. Такой простой, но удивительный подход в отличие от того, о чем я думал вначале. Большое спасибо @ DanMašek