Hello everybody, I am running a project in python involving recognition of shapes in images and creation of a graph out of the recognition. The script should be as more general as possible in order to create a graph out of any image given in input. The approach I used is to find the contours of the shapes and re-draw them into a new image. However I am having some troubles when for example the image contains a circle connected to a line: in this case the line is lost and not drawn in the graph. I show you what I mean:
this is the input picture (please don't laught about it):
After the processing, the graph created is this:
It is noticeable that the line of the left "arm" is missing and after a all afternoon of trial, I didn't get to anything. Moreover, Also the "smile" is recognized as pentagon even if the shape is disconnected.
The code written in python is a little ugly because I am not such an expert in python programming, so please excuse me for that:
import numpy as np
import cv2
import Image, ImageDraw
import math
#Function to find a cosine of angle between vectors
#from pt0->pt1 and pt0->pt2
def angle_cos(p1, p2, p0):
dx1 = (p1[0]-p0[0]).astype('double')
dy1 = (p1[1]-p0[1]).astype('double')
dx2 = (p2[0]-p0[0]).astype('double')
dy2 = (p2[1]-p0[1]).astype('double')
return (dx1*dx2 + dy1*dy2) / np.sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10)
#Routine
img = cv2.imread('snowman.png')
cv2.imshow('starting picture', img)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
#Remove of noise, if any
blur = cv2.GaussianBlur(gray, (5,5), 0)
#Create a new image of the same size of the starting image
height, width = gray.shape
newimg = np.zeros((height, width, 3), np.uint8)
#Edge detector
thresh = 175
edges = cv2.Canny(blur, thresh, thresh*2)
#Find contours
contours,hierarchy = cv2.findContours(edges, cv2.cv.CV_RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
for b,cnt in enumerate(contours):
if hierarchy[0,b,3] == -1: #Avoid inner contours
approx = cv2.approxPolyDP(cnt,0.025*cv2.arcLength(cnt,True), True)
if len(approx)==5:
print "pentagon" + " with area = " +str(cv2.contourArea(cnt))
cv2.drawContours(img,[cnt],-1,(255,0,0),-1)
#draw the pentagon in the new image
cv2.line(newimg, (approx[0][0][0], approx[0][0][1]), (approx[1][0][0], approx[1][0][1]), (255,0,0), 2)
cv2.line(newimg, (approx[1][0][0], approx[1][0][1]), (approx[2][0][0], approx[2][0][1]), (255,0,0), 2)
cv2.line(newimg, (approx[2][0][0], approx[2][0][1]), (approx[3][0][0], approx[3][0][1]), (255,0,0), 2)
cv2.line(newimg, (approx[3][0][0], approx[3][0][1]), (approx[4][0][0], approx[4][0][1]), (255,0,0), 2)
cv2.line(newimg, (approx[4][0][0], approx[4][0][1]), (approx[0][0][0], approx[0][0][1]), (255,0,0), 2)
cv2.circle(newimg, (approx[0][0][0], approx[0][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[1][0][0], approx[1][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[2][0][0], approx[2][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[3][0][0], approx[3][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[4][0][0], approx[4][0][1]), 3, (255,255,255), -1)
elif len(approx)==6:
print "hexagon" + " with area = " +str(cv2.contourArea(cnt))
cv2.drawContours(img,[cnt],-1,(255,100,100),-1)
#draw the hexagon in the new image
cv2.line(newimg, (approx[0][0][0], approx[0][0][1]), (approx[1][0][0], approx[1][0][1]), (255,100,100), 2)
cv2.line(newimg, (approx[1][0][0], approx[1][0][1]), (approx[2][0][0], approx[2][0][1]), (255,100,100), 2)
cv2.line(newimg, (approx[2][0][0], approx[2][0][1]), (approx[3][0][0], approx[3][0][1]), (255,100,100), 2)
cv2.line(newimg, (approx[3][0][0], approx[3][0][1]), (approx[4][0][0], approx[4][0][1]), (255,100,100), 2)
cv2.line(newimg, (approx[4][0][0], approx[4][0][1]), (approx[5][0][0], approx[5][0][1]), (255,100,100), 2)
cv2.line(newimg, (approx[5][0][0], approx[5][0][1]), (approx[0][0][0], approx[0][0][1]), (255,100,100), 2)
cv2.circle(newimg, (approx[0][0][0], approx[0][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[1][0][0], approx[1][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[2][0][0], approx[2][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[3][0][0], approx[3][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[4][0][0], approx[4][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[5][0][0], approx[5][0][1]), 3, (255,255,255), -1)
elif len(approx)==2:
print "line"
cv2.drawContours(img,[cnt],-1,(255,255,100),-1)
#draw the line in the new image
cv2.line(newimg, (approx[0][0][0], approx[0][0][1]), (approx[1][0][0], approx[1][0][1]), (255,255,100), 2)
cv2.circle(newimg, (approx[0][0][0], approx[0][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[1][0][0], approx[1][0][1]), 3, (255,255,255), -1)
elif len(approx)==1:
print "point"
cv2.drawContours(img,[cnt],-1,(255,255,100),-1)
#draw the point in the new image
cv2.circle(newimg, (approx[0][0][0], approx[0][0][1]), 3, (255,255,255), -1)
elif len(approx)==3:
print "triangle" + " with area = " +str(cv2.contourArea(cnt))
cv2.drawContours(img,[cnt],-1,(0,255,0),-1)
#draw the triangle in the new image
cv2.line(newimg, (approx[0][0][0], approx[0][0][1]), (approx[1][0][0], approx[1][0][1]), (0,255,0), 2)
cv2.line(newimg, (approx[1][0][0], approx[1][0][1]), (approx[2][0][0], approx[2][0][1]), (0,255,0), 2)
cv2.line(newimg, (approx[2][0][0], approx[2][0][1]), (approx[0][0][0], approx[0][0][1]), (0,255,0), 2)
cv2.circle(newimg, (approx[0][0][0], approx[0][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[1][0][0], approx[1][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[2][0][0], approx[2][0][1]), 3, (255,255,255), -1)
elif len(approx)==4:
#check if it is a square
if cv2.contourArea(approx) > 1000 and cv2.isContourConvex(approx):
approx = approx.reshape(-1, 2)
max_cos = np.max([angle_cos(approx[i%len(approx)], approx[i-2], approx[i-1] ) for i in xrange(2, len(approx)+1)])
min_cos = np.min([angle_cos(approx[i%len(approx)], approx[i-2], approx[i-1] ) for i in xrange(2, len(approx)+1)])
if min_cos >= -0.1 and max_cos <= 0.3:
print "square" + " with area = " +str(cv2.contourArea(cnt))
cv2.drawContours(img,[cnt],-1,(0,0,255),-1)
#draw the square in the new image
cv2.line(newimg,(approx[0][0], approx[0][1]), (approx[1][0], approx[1][1]), (0,0,255), 2)
cv2.line(newimg,(approx[1][0], approx[1][1]), (approx[2][0], approx[2][1]), (0,0,255), 2)
cv2.line(newimg,(approx[2][0], approx[2][1]), (approx[3][0], approx[3][1]), (0,0,255), 2)
cv2.line(newimg,(approx[3][0], approx[3][1]), (approx[0][0], approx[0][1]), (0,0,255), 2)
cv2.circle(newimg, (approx[0][0], approx[0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[1][0], approx[1][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[2][0], approx[2][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[3][0], approx[3][1]), 3, (255,255,255), -1)
elif len(approx) >=7:
#check if it is a circle
area = cv2.contourArea(cnt)
x, y, w, h = cv2.boundingRect(cnt)
radius = w/2
if abs(1 - (area / (math.pi * pow(radius, 2))))<=0.2:
print "circle" + " with area = " +str(cv2.contourArea(cnt))
M = cv2.moments(cnt)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
#draw the circle
cv2.circle(newimg, (cx, cy), radius, (0,255,255), 2)
#draw the center of the circle
cv2.circle(newimg, (cx, cy), 3, (69,13,254), -1)
cv2.drawContours(img,[cnt],-1,(0,255,255),-1)
#otherwise check if it is an octagon
elif len(approx)==8:
print "octagon" + " with area = " +str(cv2.contourArea(cnt))
cv2.drawContours(img,[cnt],-1,(155,200,255),-1)
#draw the octagon in the new image
cv2.line(newimg, (approx[0][0][0], approx[0][0][1]), (approx[1][0][0], approx[1][0][1]), (0,255,0), 2)
cv2.line(newimg, (approx[1][0][0], approx[1][0][1]), (approx[2][0][0], approx[2][0][1]), (0,255,0), 2)
cv2.line(newimg, (approx[2][0][0], approx[2][0][1]), (approx[3][0][0], approx[3][0][1]), (0,255,0), 2)
cv2.line(newimg, (approx[3][0][0], approx[3][0][1]), (approx[4][0][0], approx[4][0][1]), (0,255,0), 2)
cv2.line(newimg, (approx[4][0][0], approx[4][0][1]), (approx[5][0][0], approx[5][0][1]), (0,255,0), 2)
cv2.line(newimg, (approx[5][0][0], approx[5][0][1]), (approx[6][0][0], approx[6][0][1]), (0,255,0), 2)
cv2.line(newimg, (approx[6][0][0], approx[6][0][1]), (approx[7][0][0], approx[7][0][1]), (0,255,0), 2)
cv2.line(newimg, (approx[7][0][0], approx[7][0][1]), (approx[0][0][0], approx[0][0][1]), (0,255,0), 2)
cv2.circle(newimg, (approx[0][0][0], approx[0][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[1][0][0], approx[1][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[2][0][0], approx[2][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[3][0][0], approx[3][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[4][0][0], approx[4][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[5][0][0], approx[5][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[6][0][0], approx[6][0][1]), 3, (255,255,255), -1)
cv2.circle(newimg, (approx[7][0][0], approx[7][0][1]), 3, (255,255,255), -1)
cv2.imshow('graph', newimg)
cv2.imwrite('graph.png', newimg)
cv2.waitKey(0)
cv2.destroyAllWindows()