如何在可能有一些失真的网格中订购 cv2 轮廓?
How to order order cv2 contours in a grid which may have some distortion?
我写了一个 python 文件来检测 cv2 网格中的轮廓,并通过从左到右沿着列向下排列它们。 (见下图 grid1)。
排序相当简单 我拉出轮廓的左上角并按其 x
排序,然后按其 y
坐标排序,然后使用排序后的角对轮廓列表进行排序。当网格完全笔直时,这很好用。
现在,如果网格有变形,那么这将不再有效查看 grid2 我们可以看到标记为 2
的片段左上角的 x
坐标小于 x
标记为 1
的片段的左上角坐标(如绿线所示)。
因此,当我应用适用于 grid1 的排序函数时,它按 x
然后 y
排序,因此标记为 2
的部分被错误地排序为第一个元素排序轮廓而不是第二个轮廓。
我正在寻找一种正确排序两种情况的好方法。
有人有建议吗?
您可以根据原点角的距离和相对角位置来进行排序选择。
- 查找轮廓和层次结构。
保持没有 child 的轮廓(基于层次结构)。
- 找到边界矩形的角。
根据以下条件(或寻找更简单的条件)分析角点:
- 左上角轮廓是距离左上角最小的轮廓。
- 右下轮廓是距离左上角最大的轮廓。
- 其他两个轮廓可以用最大x和最大y分开(去掉左上角和右下角后)。
下面的解决方案,以颜色绘制边界矩形以进行测试:
- 红色
- 绿色
- 蓝色
- 黄色
这是一个工作代码示例(请阅读评论):
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()
我写了一个 python 文件来检测 cv2 网格中的轮廓,并通过从左到右沿着列向下排列它们。 (见下图 grid1)。
排序相当简单 我拉出轮廓的左上角并按其 x
排序,然后按其 y
坐标排序,然后使用排序后的角对轮廓列表进行排序。当网格完全笔直时,这很好用。
现在,如果网格有变形,那么这将不再有效查看 grid2 我们可以看到标记为 2
的片段左上角的 x
坐标小于 x
标记为 1
的片段的左上角坐标(如绿线所示)。
因此,当我应用适用于 grid1 的排序函数时,它按 x
然后 y
排序,因此标记为 2
的部分被错误地排序为第一个元素排序轮廓而不是第二个轮廓。
我正在寻找一种正确排序两种情况的好方法。
有人有建议吗?
您可以根据原点角的距离和相对角位置来进行排序选择。
- 查找轮廓和层次结构。
保持没有 child 的轮廓(基于层次结构)。 - 找到边界矩形的角。
根据以下条件(或寻找更简单的条件)分析角点:
- 左上角轮廓是距离左上角最小的轮廓。
- 右下轮廓是距离左上角最大的轮廓。
- 其他两个轮廓可以用最大x和最大y分开(去掉左上角和右下角后)。
下面的解决方案,以颜色绘制边界矩形以进行测试:
- 红色
- 绿色
- 蓝色
- 黄色
这是一个工作代码示例(请阅读评论):
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()