Find simple shapes in an image

In this post I am going to solve this problem, how to find simple shapes like triangular and square ( or rectangular) in the image.  For simple shape like square or triangular I normally use this procedure:

1. Find Contours in the image ( image should be binary)
2. Approximate each contour using approxPolyDP function.
3. Check number of elements in the approximated contours of all the shapes to recognize the shape. For eg, triangular will have 3; for square or rectangle, it has to meet the following conditions:
* It is convex.
* It has 4 vertices.
* All angles are ~90 degree.
4. Assign the color, run the code for your test image, check its number, fill it with corresponding colors.

Assumptions: Shapes don’t overlap, both of them solid (meaning, there is no white pixels inside the shape (all shapes are black). There can be multiple shapes in the image and they can be rotated any arbitrary number of degrees, and they can be of any size. Important: Triangles are non-obtuse!

 Functions:
1. Sort corners respect to center of mass counterclockwise:

import numpy as np
import numpy.linalg as la
import math
import cv2

#N1
def SortCorners(points):
    """
    sort corners respect to center of mass counterclockwise
    """
    C=np.mean(points,axis=0)[0]
    angle=[]
    for i in range(len(points)):
        a=(points[i])[0]
        angle.append(math.atan2(a[1]-C[1],a[0]-C[0]))
    ID=np.argsort(angle)
    sorted_p=points[ID]
    #print points
    #print ID
    #print sorted_p
    return sorted_p

2. Find the angle between three points:

#N2
def FindAngle(a,b,c):
    """
    find the angle between three points
    """
    ab=b-a
    ac=c-a
    dot=np.dot(ab, ac)
    norm_ab=la.norm(ab)
    norm_ac=la.norm(ac)
    angle=math.acos(dot/(norm_ac*norm_ab))*180/np.pi
    return angle

3. Check whether the shape is square (or rectangle):

#N3
def IsSquare(vcti):
    """"
    check whether the shape is square (or rectangle)
    """
    angles=[]
    vct=SortCorners(vcti)
    s_vct=len(vct)
    for i in range (s_vct):
        angles.append(FindAngle((vct[i%s_vct])[0], (vct[(i-1)%s_vct])[0], (vct[(i+1)%s_vct])[0]))
        #print angles
    #print angles
    if 88<=angles[0]<=92 and 88<=angles[1]<=92 and 88<=angles[2]<=92:
        ## tolerance 90(+/-)2
        return True

4. Find simple shapes like triangle or square in the image:

#N4
def FindShapes(pic):
    """
    find simple shapes like triangle or square ...
    (or rectangle) in the image; pic: input(e.g.'book.png').
    """
    img = cv2.imread(pic)
    img_g = cv2.imread(pic,0)

    _,thresh = cv2.threshold(img_g,127,255,1)
    # find the contours in the mask
    contours,_= cv2.findContours(thresh,1,2)

    triangle_n=0
    square_n=0
    for cnt in contours:
        approx_c = cv2.approxPolyDP(cnt,0.02*cv2.arcLength(cnt,True),True)
        #print approx_c
        # Skip small or non-convex objects
        if cv2.contourArea < 100 or cv2.isContourConvex=='False':
            continue
        if len(approx_c)==3:
            cv2.drawContours(img,[cnt],0,(0,255,0),-1)
            triangle_n+=1
        elif len(approx_c)==4 and IsSquare(approx_c):
            cv2.drawContours(img,[cnt],0,(0,0,255),-1)
            square_n+=1

    print str(triangle_n) + ' triangle' + ' and ' + str(square_n) + ' square'
    cv2.imshow('img',img)
    #cv2.imwrite('t5.png',img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

Examples:

pic='T1.png'
FindShapes(pic)
raw_input("Press Enter to continue...")

pic='T2.png'
FindShapes(pic)

Leave a Reply

Your email address will not be published. Required fields are marked *