[英]How can I sort contours from left to right and top to bottom in a consistent manner
[英]How can I sort contours from left to right and top to bottom?
我正在尝试使用 Python 构建一个字符识别程序。 我坚持对轮廓进行排序。 我正在使用此页面作为参考。
我设法使用以下代码找到轮廓:
mo_image = di_image.copy()
contour0 = cv2.findContours(mo_image.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
contours = [cv2.approxPolyDP(cnt,3,True) for cnt in contour0[0]]
并使用这部分代码添加了边界矩形并分割图像:
maxArea = 0
rect=[]
for ctr in contours:
maxArea = max(maxArea,cv2.contourArea(ctr))
if img == "Food.jpg":
areaRatio = 0.05
elif img == "Plate.jpg":
areaRatio = 0.5
for ctr in contours:
if cv2.contourArea(ctr) > maxArea * areaRatio:
rect.append(cv2.boundingRect(cv2.approxPolyDP(ctr,1,True)))
symbols=[]
for i in rect:
x = i[0]
y = i[1]
w = i[2]
h = i[3]
p1 = (x,y)
p2 = (x+w,y+h)
cv2.rectangle(mo_image,p1,p2,255,2)
image = cv2.resize(mo_image[y:y+h,x:x+w],(32,32))
symbols.append(image.reshape(1024,).astype("uint8"))
testset_data = np.array(symbols)
cv2.imshow("segmented",mo_image)
plt.subplot(2,3,6)
plt.title("Segmented")
plt.imshow(mo_image,'gray')
plt.xticks([]),plt.yticks([]);
然而,结果段似乎是随机顺序的。 这是原始图像,然后是带有检测到的片段的处理过的图像。
然后程序分别输出每个段,但是它的顺序是: 4 1 9 8 7 5 3 2 0 6
而不是0 1 2 3 4 5 6 7 8 9
。 只需在“rect”中添加排序操作即可解决此问题,但相同的解决方案不适用于具有多行的文档。
所以我的问题是:如何从左到右和从上到下对轮廓进行排序?
我不认为您将能够以正确的顺序直接生成轮廓,但是如下的简单排序应该可以满足您的需求:
import numpy as np
c = np.load(r"rect.npy")
contours = list(c)
# Example - contours = [(287, 117, 13, 46), (102, 117, 34, 47), (513, 116, 36, 49), (454, 116, 32, 49), (395, 116, 28, 48), (334, 116, 31, 49), (168, 116, 26, 49), (43, 116, 30, 48), (224, 115, 33, 50), (211, 33, 34, 47), ( 45, 33, 13, 46), (514, 32, 32, 49), (455, 32, 31, 49), (396, 32, 29, 48), (275, 32, 28, 48), (156, 32, 26, 49), (91, 32, 30, 48), (333, 31, 33, 50)]
max_height = np.max(c[::, 3])
nearest = max_height * 1.4
contours.sort(key=lambda r: [int(nearest * round(float(r[1]) / nearest)), r[0]])
for x, y, w, h in contours:
print(f"{x:4} {y:4} {w:4} {h:4}")
这将显示以下输出:
36 45 33 40
76 44 29 43
109 43 29 45
145 44 32 43
184 44 21 43
215 44 21 41
241 43 34 45
284 46 31 39
324 46 7 39
337 46 14 41
360 46 26 39
393 46 20 41
421 45 45 41
475 45 32 41
514 43 38 45
39 122 26 41
70 121 40 48
115 123 27 40
148 121 25 45
176 122 28 41
212 124 30 41
247 124 91 40
342 124 28 39
375 124 27 39
405 122 27 43
37 210 25 33
69 199 28 44
102 210 21 33
129 199 28 44
163 210 26 33
195 197 16 44
214 210 27 44
247 199 25 42
281 212 7 29
292 212 11 42
310 199 23 43
340 199 7 42
355 211 43 30
406 213 24 28
437 209 31 35
473 210 28 43
506 210 28 43
541 210 17 31
37 288 21 33
62 282 15 39
86 290 24 28
116 290 72 30
192 290 23 30
218 290 26 41
249 288 20 33
它的工作原理是将相似的y
值分组为行值,然后按矩形的 x 偏移量排序。 键是一个包含估计行和 x 偏移量的列表。
计算单个矩形的最大高度以确定nearest
分组值。 1.4
值是行间距值。 这也可以自动计算。 因此,对于您的两个示例, nearest
的大约是 70。
计算也可以直接在numpy中完成。
虽然我解决了我的任务,但我采用了这样的方法(我猜这个方法没有优化,可以改进):
import pandas as pd
import cv2
import cv2
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
import matplotlib
matplotlib.rcParams['figure.figsize'] = (20.0, 10.0)
matplotlib.rcParams['image.cmap'] = 'gray'
imageCopy = cv2.imread("./test.png")
imageGray = cv2.imread("./test.png", 0)
image = imageCopy.copy()
contours, hierarchy = cv2.findContours(imageGray, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
bboxes = [cv2.boundingRect(i) for i in contours]
bboxes=sorted(bboxes, key=lambda x: x[0])
df=pd.DataFrame(bboxes, columns=['x','y','w', 'h'], dtype=int)
df["x2"] = df["x"]+df["w"] # adding column for x on the right side
df = df.sort_values(["x","y", "x2"]) # sorting
for i in range(2): # change rows between each other by their coordinates several times
# to sort them completely
for ind in range(len(df)-1):
# print(ind, df.iloc[ind][4] > df.iloc[ind+1][0])
if df.iloc[ind][4] > df.iloc[ind+1][0] and df.iloc[ind][1]> df.iloc[ind+1][1]:
df.iloc[ind], df.iloc[ind+1] = df.iloc[ind+1].copy(), df.iloc[ind].copy()
num=0
for box in df.values.tolist():
x,y,w,h, hy = box
cv2.rectangle(image, (x,y), (x+w,y+h), (255,0,255), 2)
# Mark the contour number
cv2.putText(image, "{}".format(num + 1), (x+40, y-10), cv2.FONT_HERSHEY_SIMPLEX, 1,
(0, 0, 255), 2);
num+=1
plt.imshow(image[:,:,::-1])
给定一个二进制图像 - thresh
,我认为最短的方法是 -
import numpy as np
import cv2
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_NON) #thresh is a bia
cntr_index_LtoR = np.argsort([cv2.boundingRect(i)[0] for i in contours])
这里, cv2.boundingRect(i)[0]
刚刚返回x
从x,y,w,h = cv2.boundingRect(i)
的用于i
个轮廓。
同样,您可以使用从上到下。
contours.sort(key=lambda r: round( float(r[1] / nearest)))
会导致类似的效果(int(nearest * round(float(r[1])/nearest)) * max_width + r[0])
使用contours=cv2.findContours()
找到轮廓后,使用 -
boundary=[]
for c,cnt in enumerate(contours):
x,y,w,h = cv2.boundingRect(cnt)
boundary.append((x,y,w,h))
count=np.asarray(boundary)
max_width = np.sum(count[::, (0, 2)], axis=1).max()
max_height = np.max(count[::, 3])
nearest = max_height * 1.4
ind_list=np.lexsort((count[:,0],count[:,1]))
c=count[ind_list]
现在 c 将按从左到右和从上到下的顺序排序。
使用轮廓的边界框(x,y,w,h)从左到右,从上到下对轮廓进行排序的简单方法如下。
您可以使用boundingBoxes = cv2.boundingRect()方法获取边界框
def sort_bbox(boundingBoxes):
'''
function to sort bounding boxes from left to right, top to bottom
'''
# combine x and y as a single list and sort based on that
boundingBoxes = sorted(boundingBoxes, key=lambda b:b[0]+b[1], reverse=False))
return boundingboxes
该方法并未在所有情况下进行广泛测试,但发现对我正在做的项目非常有效。
链接到排序函数文档以供参考https://docs.python.org/3/howto/sorting.html
def sort_contours(contours, x_axis_sort='LEFT_TO_RIGHT', y_axis_sort='TOP_TO_BOTTOM'):
# initialize the reverse flag
x_reverse = False
y_reverse = False
if x_axis_sort == 'RIGHT_TO_LEFT':
x_reverse = True
if y_axis_sort == 'BOTTOM_TO_TOP':
y_reverse = True
boundingBoxes = [cv2.boundingRect(c) for c in contours]
# sorting on x-axis
sortedByX = zip(*sorted(zip(contours, boundingBoxes),
key=lambda b:b[1][0], reverse=x_reverse))
# sorting on y-axis
(contours, boundingBoxes) = zip(*sorted(zip(*sortedByX),
key=lambda b:b[1][1], reverse=y_reverse))
# return the list of sorted contours and bounding boxes
return (contours, boundingBoxes)
contours, hierarchy = cv2.findContours(img_vh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours, boundingBoxes = sort_contours(contours, x_axis_sort='LEFT_TO_RIGHT', y_axis_sort='TOP_TO_BOTTOM')
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.