Python opencv 排序轮廓 [英] Python opencv sorting contours

查看:64
本文介绍了Python opencv 排序轮廓的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在关注这个问题:

我想按以下顺序找到轮廓:

用于查找轮廓的膨胀图像

解决方案

你真正需要的是设计一个公式来将你的轮廓信息转换为一个等级并使用该等级对轮廓进行排序,因为你需要对轮廓进行排序从上到下,从左到右,所以你的公式必须涉及给定轮廓的 origin 来计算它的等级.例如我们可以使用这个简单的方法:

def get_contour_precedence(contour, cols):原点 = cv2.boundingRect(contour)返回原点[1] * cols + 原点[0]

它根据轮廓的原点给每个轮廓一个等级.当两个连续的轮廓垂直放置时,它的变化很大,而当轮廓水平堆叠时,它的变化很小.因此,通过这种方式,首先将轮廓从上到下分组,并且在 Clash 的情况下,将使用水平放置的轮廓中较小的变体值.

导入 cv2def get_contour_precedence(contour, cols):容差系数 = 10原点 = cv2.boundingRect(contour)return ((origin[1]//tolerance_factor) * tolerance_factor) * cols + origin[0]img = cv2.imread("/Users/anmoluppal/Downloads/9VayB.png", 0)_, img = cv2.threshold(img, 70, 255, cv2.THRESH_BINARY)im, 轮廓, h = cv2.findContours(img.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)轮廓.sort(key=lambda x:get_contour_precedence(x, img.shape[1]))# 用于调试目的.对于 xrange(len(contours)) 中的 i:img = cv2.putText(img, str(i), cv2.boundingRect(contours[i])[:2], cv2.FONT_HERSHEY_COMPLEX, 1, [125])

如果你仔细看,3, 4, 5, 6 等高线所在的第三行 6 介于 3 和 5 之间,原因是 3,4,5,6code>6th 轮廓略低于 3,4,5 轮廓线.

告诉我您是否希望以其他方式输出,我们可以调整 get_contour_precedence 以获得校正轮廓的 3,4,5,6 等级.

I am following this question:

How can I sort contours from left to right and top to bottom?

to sort contours from left-to-right and top-to-bottom. However, my contours are found using this (OpenCV 3):

im2, contours, hierarchy = cv2.findContours(threshold,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

and they are formatted like this:

   array([[[ 1,  1]],

   [[ 1, 36]],

   [[63, 36]],

   [[64, 35]],

   [[88, 35]],

   [[89, 34]],

   [[94, 34]],

   [[94,  1]]], dtype=int32)]

When I run the code

max_width = max(contours, key=lambda r: r[0] + r[2])[0]
max_height = max(contours, key=lambda r: r[3])[3]
nearest = max_height * 1.4
contours.sort(key=lambda r: (int(nearest * round(float(r[1])/nearest)) * max_width + r[0]))

I am getting the error

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

so I changed it to this:

max_width = max(contours, key=lambda r:  np.max(r[0] + r[2]))[0]
max_height = max(contours, key=lambda r:  np.max(r[3]))[3]
nearest = max_height * 1.4
contours.sort(key=lambda r: (int(nearest * round(float(r[1])/nearest)) * max_width + r[0]))

but now I am getting the error:

TypeError: only length-1 arrays can be converted to Python scalars

EDIT:

After reading the answer below I modified my code:

EDIT 2

This is the code that I use to "dilate" the characters and find the contours

kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(35,35))

# dilate the image to get text
# binaryContour is just the black and white image shown below
dilation = cv2.dilate(binaryContour,kernel,iterations = 2)

END OF EDIT 2

im2, contours, hierarchy = cv2.findContours(dilation,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

myContours = []

# Process the raw contours to get bounding rectangles
for cnt in reversed(contours):

    epsilon = 0.1*cv2.arcLength(cnt,True)
    approx = cv2.approxPolyDP(cnt,epsilon,True)

    if len(approx == 4):

        rectangle = cv2.boundingRect(cnt)
        myContours.append(rectangle)

max_width = max(myContours, key=lambda r: r[0] + r[2])[0]
max_height = max(myContours, key=lambda r: r[3])[3]
nearest = max_height * 1.4
myContours.sort(key=lambda r: (int(nearest * round(float(r[1])/nearest)) * max_width + r[0]))

i=0
for x,y,w,h in myContours:

    letter = binaryContour[y:y+h, x:x+w]
    cv2.rectangle(binaryContour,(x,y),(x+w,y+h),(255,255,255),2)
    cv2.imwrite("pictures/"+str(i)+'.png', letter) # save contour to file
    i+=1

Contours before sorting:

[(1, 1, 94, 36), (460, 223, 914, 427), (888, 722, 739, 239), (35,723, 522, 228), 
(889, 1027, 242, 417), (70, 1028, 693, 423), (1138, 1028, 567, 643),     
(781, 1030, 98, 413), (497, 1527, 303, 132), (892, 1527, 168, 130),  
(37, 1719, 592, 130), (676, 1721, 413, 129), (1181, 1723, 206, 128), 
(30, 1925, 997, 236), (1038, 1929, 170, 129), (140, 2232, 1285, 436)]

Contours after sorting:

(NOTE: This is not the order I want the contours to be sorted in. Refer to image at the bottom)

[(1, 1, 94, 36), (460, 223, 914, 427), (35, 723, 522, 228), (70,1028, 693, 423), 
(781, 1030, 98, 413), (888, 722, 739, 239), (889, 1027, 242, 417), 
(1138, 1028, 567, 643), (30, 1925, 997, 236), (37, 1719, 592, 130), 
(140, 2232, 1285, 436), (497, 1527, 303, 132), (676, 1721, 413, 129), 
(892, 1527, 168, 130), (1038, 1929, 170, 129), (1181, 1723, 206, 128)]

Image I am working with

I want to find the contours in the following order:

Dilation image used for finding contours

解决方案

What you actually need is to devise a formula to convert your contour information to a rank and use that rank to sort the contours, Since you need to sort the contours from top to Bottom and left to right so your formula must involve the origin of a given contour to calculate its rank. For example we can use this simple method:

def get_contour_precedence(contour, cols):
    origin = cv2.boundingRect(contour)
    return origin[1] * cols + origin[0]

It gives a rank to each contour depending upon the origin of contour. It varies largely when two consecutive contours lie vertically but varies marginally when contours are stacked horizontally. So in this way, First the contours would be grouped from Top to Bottom and in case of Clash the less variant value among the horizontal laid contours would be used.

import cv2

def get_contour_precedence(contour, cols):
    tolerance_factor = 10
    origin = cv2.boundingRect(contour)
    return ((origin[1] // tolerance_factor) * tolerance_factor) * cols + origin[0]

img = cv2.imread("/Users/anmoluppal/Downloads/9VayB.png", 0)

_, img = cv2.threshold(img, 70, 255, cv2.THRESH_BINARY)

im, contours, h = cv2.findContours(img.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

contours.sort(key=lambda x:get_contour_precedence(x, img.shape[1]))

# For debugging purposes.
for i in xrange(len(contours)):
    img = cv2.putText(img, str(i), cv2.boundingRect(contours[i])[:2], cv2.FONT_HERSHEY_COMPLEX, 1, [125])

If you see closely, the third row where 3, 4, 5, 6 contours are placed the 6 comes between 3 and 5, The reason is that the 6th contour is slightly below the line of 3, 4, 5 contours.

Tell me is you want the output in other way around we can tweak the get_contour_precedence to get 3, 4, 5, 6 ranks of contour corrected.

这篇关于Python opencv 排序轮廓的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆