Finding properties of sloppy hand-drawn rectangles Finding properties of sloppy hand-drawn rectangles python python

Finding properties of sloppy hand-drawn rectangles


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:

enter image description here

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 sysimport numpyfrom PIL import Image, ImageOps, ImageDrawfrom scipy.ndimage import morphology, labeldef 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, boxorig = 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.

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_LIST provides no hierarchy while cv2.RETR_EXTERNAL gives you only external contours.

The best one for you is cv2.RETR_CCOMP which provides you all the contour, and a two-level hierarchical relationship. 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

So hierarchy of a contour is a 4 element array in which last element is the index pointer to its parent. 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 cv2import numpy as np# Normal routinesimg = 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_CCOMPcontours,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 :

enter image description here