简体   繁体   English

寻找马虎手绘矩形的属性

[英]Finding properties of sloppy hand-drawn rectangles

Image I'm working with: 我正在使用的图片:

https://dl.dropbox.com/u/454490/1%20%28Small%29.JPG

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: 我遇到了一些问题:

  1. bounding rectangles are detected for both the inside and the outside of the drawn lines. 对于绘制线的内部和外部都检测边界矩形。
  2. some extraneous corners/centers are detected. 检测到一些无关的角落/中心。
  3. I'm not sure how to match corners/centers with the related contours/bounding boxes, especially when taking nested boxes into account. 我不确定如何将角/中心与相关的轮廓/边界框匹配,尤其是在考虑嵌套框时。

Image resulting from code: 代码产生的图像: https://dl.dropbox.com/u/454490/testresult.jpg

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM