[英]Finding properties of sloppy hand-drawn rectangles
Image I'm working with: 我正在使用的图片:
I'm trying to find each of the boxes in this image. 我正试图找到这张图片中的每个方框。 The results don't have to be 100% accurate, just as long as the boxes found are approximately correct in position/size.
只要找到的方框在位置/大小上大致正确,结果就不必100%准确。 From playing with the example for square detection, I've managed to get contours, bounding boxes, corners and the centers of boxes.
从玩方形检测的例子,我已经设法得到轮廓,边界框,角落和盒子的中心。
There are a few issues I'm running into here: 我遇到了一些问题:
Image resulting from code: 代码产生的图像:
Here's the code I'm using to generate the image above: 这是我用来生成上面图像的代码:
import numpy as np
import cv2
from operator import itemgetter
from glob import glob
def angle_cos(p0, p1, p2):
d1, d2 = (p0-p1).astype('float'), (p2-p1).astype('float')
return abs( np.dot(d1, d2) / np.sqrt( np.dot(d1, d1)*np.dot(d2, d2) ) )
def makebin(gray):
bin = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 5, 2)
return cv2.bitwise_not(bin)
def find_squares(img):
img = cv2.GaussianBlur(img, (11, 11), 0)
squares = []
points = []`
for gray in cv2.split(img):
bin = makebin(gray)
contours, hierarchy = cv2.findContours(bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
corners = cv2.goodFeaturesToTrack(gray,len(contours)*4,0.2,15)
cv2.cornerSubPix(gray,corners,(6,6),(-1,-1),(cv2.TERM_CRITERIA_MAX_ITER | cv2.TERM_CRITERIA_EPS,10, 0.1))
for cnt in contours:
cnt_len = cv2.arcLength(cnt, True)
if len(cnt) >= 4 and cv2.contourArea(cnt) > 200:
rect = cv2.boundingRect(cnt)
if rect not in squares:
squares.append(rect)
return squares, corners, contours
if __name__ == '__main__':
for fn in glob('../1 (Small).jpg'):
img = cv2.imread(fn)
squares, corners, contours = find_squares(img)
for p in corners:
cv2.circle(img, (p[0][0],p[0][3]), 3, (0,0,255),2)
squares = sorted(squares,key=itemgetter(1,0,2,3))
areas = []
moments = []
centers = []
for s in squares:
areas.append(s[2]*s[3])
cv2.rectangle( img, (s[0],s[1]),(s[0]+s[2],s[1]+s[3]),(0,255,0),1)
for c in contours:
moments.append(cv2.moments(np.array(c)))
for m in moments:
centers.append((int(m["m10"] // m["m00"]), int(m["m01"] // m["m00"])))
for cent in centers:
print cent
cv2.circle(img, (cent[0],cent[1]), 3, (0,255,0),2)
cv2.imshow('squares', img)
ch = 0xFF & cv2.waitKey()
if ch == 27:
break
cv2.destroyAllWindows()
I suggest a simpler approach as a starting point. 我建议采用更简单的方法作为起点。 For instance, morphological gradient can serve as a good local detector of strong edges, and threshold on it tends to be simple.
例如,形态梯度可以作为强边缘的良好局部检测器,并且其上的阈值趋于简单。 Then, you can remove too small components, which is relatively easy for your problem too.
然后,您可以删除太小的组件,这对您的问题也相对容易。 In your example, each remaining connected component is a single box, so the problem is solved in this instance.
在您的示例中,每个剩余的连接组件都是单个框,因此在此实例中解决了该问题。
Here is what you would obtain with this simple procedure: 以下是您通过以下简单程序获得的内容:
The red points represent the centroid of the component, so you could grow another box from there that is contained in the yellow one if the yellow ones are bad for you. 红点代表组件的质心,所以你可以从那里生长另一个盒子,如果黄色的盒子对你不好的话,它会包含在黄色盒子中。
Here is the code for achieving that: 以下是实现这一目标的代码:
import sys
import numpy
from PIL import Image, ImageOps, ImageDraw
from scipy.ndimage import morphology, label
def boxes(orig):
img = ImageOps.grayscale(orig)
im = numpy.array(img)
# Inner morphological gradient.
im = morphology.grey_dilation(im, (3, 3)) - im
# Binarize.
mean, std = im.mean(), im.std()
t = mean + std
im[im < t] = 0
im[im >= t] = 1
# Connected components.
lbl, numcc = label(im)
# Size threshold.
min_size = 200 # pixels
box = []
for i in range(1, numcc + 1):
py, px = numpy.nonzero(lbl == i)
if len(py) < min_size:
im[lbl == i] = 0
continue
xmin, xmax, ymin, ymax = px.min(), px.max(), py.min(), py.max()
# Four corners and centroid.
box.append([
[(xmin, ymin), (xmax, ymin), (xmax, ymax), (xmin, ymax)],
(numpy.mean(px), numpy.mean(py))])
return im.astype(numpy.uint8) * 255, box
orig = Image.open(sys.argv[1])
im, box = boxes(orig)
# Boxes found.
Image.fromarray(im).save(sys.argv[2])
# Draw perfect rectangles and the component centroid.
img = Image.fromarray(im)
visual = img.convert('RGB')
draw = ImageDraw.Draw(visual)
for b, centroid in box:
draw.line(b + [b[0]], fill='yellow')
cx, cy = centroid
draw.ellipse((cx - 2, cy - 2, cx + 2, cy + 2), fill='red')
visual.save(sys.argv[3])
I see you have already got the answer. 我知道你已经得到了答案。 But I think there is a much more simpler,shorter and better method available in OpenCV to resolve this problem.
但我认为OpenCV中有一个更简单,更短,更好的方法来解决这个问题。
While finding contours, you are also finding the hierarchy of the contours. 在找到轮廓时,您还可以找到轮廓的层次结构。 Hierarchy of the contours is the relation between different contours.
轮廓的层次结构是不同轮廓之间的关系。
So the flag you used in your code, cv2.RETR_TREE
provides all the hierarchical relationship. 因此,您在代码中使用的标志
cv2.RETR_TREE
提供了所有层次关系。
cv2.RETR_LIST
provides no hierarchy while cv2.RETR_EXTERNAL
gives you only external contours. cv2.RETR_LIST
提供层次结构,而cv2.RETR_EXTERNAL
只提供外部轮廓。
The best one for you is cv2.RETR_CCOMP
which provides you all the contour, and a two-level hierarchical relationship. 最适合您的是
cv2.RETR_CCOMP
,它为您提供所有轮廓和两级层次关系。 ie outer contour is always parent and inner hole contour always is child. 即外轮廓始终为父轮廓,内孔轮廓始终为小轮廓。
Please read following article for more information on hierarchy : Contour - 5 : Hierarchy 有关层次结构的更多信息,请阅读以下文章: 轮廓 - 5:层次结构
So hierarchy of a contour is a 4 element array in which last element is the index pointer to its parent. 因此,轮廓的层次结构是一个4元素数组,其中最后一个元素是指向其父元素的索引指针。
If a contour has no parent, it is external contour and it has a value -1
. If a contour has no parent, it is external contour and it has a value -1
。 If it is a inner contour, it is a child and it will have some value which points to its parent. 如果它是一个内部轮廓,它是一个孩子,它将有一些指向其父级的值。 We are going to exploit this feature in your problem.
我们将在您的问题中利用此功能。
import cv2
import numpy as np
# Normal routines
img = cv2.imread('square.JPG')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,50,255,1)
# Remove some small noise if any.
dilate = cv2.dilate(thresh,None)
erode = cv2.erode(dilate,None)
# Find contours with cv2.RETR_CCOMP
contours,hierarchy = cv2.findContours(erode,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)
for i,cnt in enumerate(contours):
# Check if it is an external contour and its area is more than 100
if hierarchy[0,i,3] == -1 and cv2.contourArea(cnt)>100:
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
m = cv2.moments(cnt)
cx,cy = m['m10']/m['m00'],m['m01']/m['m00']
cv2.circle(img,(int(cx),int(cy)),3,255,-1)
cv2.imshow('img',img)
cv2.imwrite('sofsqure.png',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result : 结果:
This question is related to python image recognition . 这个问题与python图像识别有关 。 A solution is given in squres.py demo .
squres.py演示中给出了一个解决方案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.