Как заказать контуры cv2 в сетке, которые могут иметь искажения?

Я написал файл python для обнаружения контуров в сетке cv2 и упорядочивания их, спускаясь по столбцам слева направо. (См. изображение grid1 ниже).

Это довольно тривиально для сортировки. Я вытащил верхний левый угол контура и отсортировал его по его координате x, затем по его координате y, а затем использовал отсортированные углы для сортировки списка контуров. Это прекрасно работает, когда сетка идеально прямая.

Сетка1

Теперь, если сетка имеет искажение, то это больше не работает. Глядя на сетку 2, мы видим, что координата x верхнего левого угла элемента, помеченного 2, меньше, чем координата x верхнего левого угла элемента, помеченного 1 (как показано зеленая линия).

Следовательно, когда я применяю свою функцию сортировки, которая работала для grid1, она сортирует по x, а затем по y, и, следовательно, часть, помеченная 2, неправильно упорядочена, чтобы быть первым элементом отсортированных контуров, а не вторым, как это должно быть.

Сетка2

Я ищу хороший метод для правильной сортировки обоих случаев.

У кого-нибудь есть предложения?


person user9940344    schedule 06.03.2020    source источник
comment
Пожалуйста, добавьте код! Я не совсем понимаю, что ты хотел сделать.   -  person Saeed Masoomi    schedule 06.03.2020
comment
Не могли бы вы объяснить, какую часть вы не понимаете, я попытаюсь объяснить?   -  person user9940344    schedule 06.03.2020


Ответы (1)


arrow_upward
0
arrow_downward

Вы можете основывать свой выбор упорядочения как на расстоянии угла от начала координат, так и на относительном положении угла.

  • Поиск контуров и иерархии.
    Сохранение контуров без дочерних элементов (на основе иерархии).
  • Найдите углы ограничивающих прямоугольников.

Проанализируйте углы на основе следующих условий (или найдите более простые условия):

  • Верхний левый контур — это контур с минимальным расстоянием до верхнего левого угла.
  • Нижний правый контур - это контур с максимальным расстоянием от верхнего левого угла.
  • Два других контура могут быть разделены максимальным значением x и максимальным значением y (после исключения верхнего левого и нижнего правого).

Решение ниже рисует ограничивающие прямоугольники в цветах для тестирования:

  1. Красный
  2. Зеленый
  3. Синий
  4. Желтый

Вот пример рабочего кода (пожалуйста, прочтите комментарии):

import numpy as np
import cv2

# Read input image as Grayscale
img = cv2.imread('img.png', cv2.IMREAD_GRAYSCALE)

# Convert img to uint8 binary image with values 0 and 255
# All black pixels goes to 0, and other pixels goes to 255
ret, thresh_gray = cv2.threshold(img, 1, 255, cv2.THRESH_BINARY)

# Find contours in thresh_gray.
cnts, hiers = cv2.findContours(thresh_gray, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[-2:]  # [-2:] indexing takes return value before last (due to OpenCV compatibility issues).

corners = [] # List of corners
dist = np.array([]) # Array of distance from axes origin

# Iterate cnts and hiers, find bounding rectangles, and add corners to a list
for c, h in zip(cnts, hiers[0]):
    # If contours has no child
    if h[2] == -1:
        # Get bounding rectangle
        x, y, w, h = cv2.boundingRect(c)

        # Append corner to list of corners - format is corners[i] holds a tuple: ((x0, y0), (x1, y1))
        p0 = (x, y)
        p1 = (x+w, y+h)
        corners.append((p0, p1))

        # Distance of corners from origin
        d = np.array([np.linalg.norm(p0), np.linalg.norm(p1)])

        if dist.size == 0:
            dist = d
        else:
            dist = np.vstack((dist, d))


top_left = np.argmin(dist[:,0]) # Index of top left corner (assume minimum distance from origin)
bottom_right = np.argmax(dist[:,1]) # Index of top bottom right corner (assume maximum distance from origin)

tmp_corners = np.array(corners)
tmp_corners[top_left, :, :] = np.array(((0,0), (0,0))) #Ignore top_left corners
tmp_corners[bottom_right, :, :] = np.array(((0,0), (0,0))) #Ignore bottom_right corners
bottom_left = np.argmax(tmp_corners[:,1,1]) #Maximum y is bottom left
tmp_corners[bottom_left, :, :] = np.array(((0,0), (0,0))) #Ignore bottom_left corners
top_right = np.argmax(tmp_corners[:,1,0])  #Maximum x is top right

# Convert Grayscale to BGR (just for testing - for drawing rectangles in green color).
out = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)

# Draw rectangles (for testing)
# 1. Red
# 2. Green
# 3. Blue
# 4. Yellow
cv2.rectangle(out, corners[top_left][0], corners[top_left][1], (0, 0, 255), thickness = 2)
cv2.rectangle(out, corners[bottom_left][0], corners[bottom_left][1], (0, 255, 0), thickness = 2)
cv2.rectangle(out, corners[top_right][0], corners[top_right][1], (255, 0, 0), thickness = 2)
cv2.rectangle(out, corners[bottom_right][0], corners[bottom_right][1], (0, 255, 255), thickness = 2)

cv2.imwrite('out.png', out)  #Save out to file (for testing).


# Show result (for testing).
cv2.imshow('thresh_gray', thresh_gray)
cv2.imshow('out', out)
cv2.waitKey(0)
cv2.destroyAllWindows()

Результат:

person Rotem    schedule 06.03.2020
comment
Спасибо за ответ, у меня нет времени просматривать и пытаться внедрить в свой код, но я посмотрю и приму его на следующей неделе, если все пойдет хорошо :) - person user9940344; 06.03.2020
comment
Спасибо за ответ. Это сработало для сетки 2x2. К сожалению, я хотел что-то, что работало бы для сетки nxm для натуральных чисел n, m. Однако я придумал общее решение, которое включает сортировку и подсортировку. Я принимаю этот ответ, поскольку он сработал для вопроса, как указано. - person user9940344; 09.03.2020