如何从左到右,从上到下排序轮廓?

时间:2016-07-29 08:42:18

标签: python

我正在尝试使用Python构建一个字符识别程序。我被困在整理轮廓。我使用constructor作为参考。

我设法使用以下代码找到轮廓:

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([]);

但是,结果段显示为随机顺序。 这是原始图像,后面是带有检测到的段的已处理图像。

this page

然后程序会单独输出每个细分,但顺序为:4 1 9 8 7 5 3 2 0 6而不是0 1 2 3 4 5 6 7 8 9。 简单地在“rect”中添加排序操作可以解决这个问题,但是对于包含多行的文档,同样的解决方案不适用。

所以我的问题是:如何从左到右,从上到下对轮廓进行排序?

7 个答案:

答案 0 :(得分:2)

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')

答案 1 :(得分:1)

我认为您不能以正确的顺序直接生成轮廓,但如下所示的简单排序应该可以满足您的需求:

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_width = np.sum(c[::, (0, 2)], axis=1).max()
max_height = np.max(c[::, 3])
nearest = max_height * 1.4

contours.sort(key=lambda r: (int(nearest * round(float(r[1])/nearest)) * max_width + r[0]))

for x, y, w, h in contours:
    print "{:4} {:4} {:4} {:4}".format(x, y, w, h) 

这将显示以下输出:

  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值进行分组,然后乘以宽度,结果是逐行增加的键。计算单个矩形的最大高度以确定nearest的合适分组值。 1.4值是行间距值。这也可以自动计算。因此,对于您的两个示例nearest约为70。

计算也可以直接在numpy中完成。

答案 2 :(得分:1)

当我完成任务时,我采取了这种方法(我认为这种方法尚未优化,可以改进):

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])

原始排序: original sorting 从上到下从左到右: up-to-bottom left-to-right 原始图像(如果要测试): original image

答案 3 :(得分: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 个轮廓。

同样,您可以使用从上到下。

答案 4 :(得分:0)

contours.sort(key=lambda r: round( float(r[1] / nearest)))会产生类似(int(nearest * round(float(r[1])/nearest)) * max_width + r[0])

的效果

答案 5 :(得分: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将从左到右,从上到下排序。

答案 6 :(得分:0)

用从左到右,从上到下的轮廓的边界框(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