For more details on contours, visit :
1) Contours - 1 : Getting Started
2) Contours - 2 : Brotherhood
''' filename : contourfeatures.py
This sample calculates some useful parameters of a contour. This is an OpenCV implementation of regionprops function in matlab with some additional features.
Benefit : Learn to find different parameters of a contour region.
Get familier with different contour functions in OpenCV.
Level : Beginner or Intermediate
Usage : python contourfeatures.py <image_file>
Abid Rahman 3/25/12 '''
import cv2
import numpy as np
class Contour:
''' Provides detailed parameter informations about a contour
Create a Contour instant as follows: c = Contour(src_img, contour)
where src_img should be grayscale image.
Attributes:
c.area -- gives the area of the region
c.parameter -- gives the perimeter of the region
c.moments -- gives all values of moments as a dict
c.centroid -- gives the centroid of the region as a tuple (x,y)
c.bounding_box -- gives the bounding box parameters as a tuple => (x,y,width,height)
c.bx,c.by,c.bw,c.bh -- corresponds to (x,y,width,height) of the bounding box
c.aspect_ratio -- aspect ratio is the ratio of width to height
c.equi_diameter -- equivalent diameter of the circle with same as area as that of region
c.extent -- extent = contour area/bounding box area
c.convex_hull -- gives the convex hull of the region
c.convex_area -- gives the area of the convex hull
c.solidity -- solidity = contour area / convex hull area
c.center -- gives the center of the ellipse
c.majoraxis_length -- gives the length of major axis
c.minoraxis_length -- gives the length of minor axis
c.orientation -- gives the orientation of ellipse
c.eccentricity -- gives the eccentricity of ellipse
c.filledImage -- returns the image where region is white and others are black
c.filledArea -- finds the number of white pixels in filledImage
c.convexImage -- returns the image where convex hull region is white and others are black
c.pixelList -- array of indices of on-pixels in filledImage
c.maxval -- corresponds to max intensity in the contour region
c.maxloc -- location of max.intensity pixel location
c.minval -- corresponds to min intensity in the contour region
c.minloc -- corresponds to min.intensity pixel location
c.meanval -- finds mean intensity in the contour region
c.leftmost -- leftmost point of the contour
c.rightmost -- rightmost point of the contour
c.topmost -- topmost point of the contour
c.bottommost -- bottommost point of the contour
c.distance_image((x,y)) -- return the distance (x,y) from the contour.
c.distance_image() -- return the distance image where distance to all points on image are calculated
'''
def __init__(self,img,cnt):
self.img = img
self.cnt = cnt
self.size = len(cnt)
# MAIN PARAMETERS
#Contour.area - Area bounded by the contour region'''
self.area = cv2.contourArea(self.cnt)
# contour perimeter
self.perimeter = cv2.arcLength(cnt,True)
# centroid
self.moments = cv2.moments(cnt)
if self.moments['m00'] != 0.0:
self.cx = self.moments['m10']/self.moments['m00']
self.cy = self.moments['m01']/self.moments['m00']
self.centroid = (self.cx,self.cy)
else:
self.centroid = "Region has zero area"
# bounding box
self.bounding_box=cv2.boundingRect(cnt)
(self.bx,self.by,self.bw,self.bh) = self.bounding_box
# aspect ratio
self.aspect_ratio = self.bw/float(self.bh)
# equivalent diameter
self.equi_diameter = np.sqrt(4*self.area/np.pi)
# extent = contour area/boundingrect area
self.extent = self.area/(self.bw*self.bh)
### CONVEX HULL ###
# convex hull
self.convex_hull = cv2.convexHull(cnt)
# convex hull area
self.convex_area = cv2.contourArea(self.convex_hull)
# solidity = contour area / convex hull area
self.solidity = self.area/float(self.convex_area)
### ELLIPSE ###
self.ellipse = cv2.fitEllipse(cnt)
# center, axis_length and orientation of ellipse
(self.center,self.axes,self.orientation) = self.ellipse
# length of MAJOR and minor axis
self.majoraxis_length = max(self.axes)
self.minoraxis_length = min(self.axes)
# eccentricity = sqrt( 1 - (ma/MA)^2) --- ma= minor axis --- MA= major axis
self.eccentricity = np.sqrt(1-(self.minoraxis_length/self.majoraxis_length)**2)
### CONTOUR APPROXIMATION ###
self.approx = cv2.approxPolyDP(cnt,0.02*self.perimeter,True)
### EXTRA IMAGES ###
# filled image :- binary image with contour region white and others black
self.filledImage = np.zeros(self.img.shape[0:2],np.uint8)
cv2.drawContours(self.filledImage,[self.cnt],0,255,-1)
# area of filled image
filledArea = cv2.countNonZero(self.filledImage)
# pixelList - array of indices of contour region
self.pixelList = np.transpose(np.nonzero(self.filledImage))
# convex image :- binary image with convex hull region white and others black
self.convexImage = np.zeros(self.img.shape[0:2],np.uint8)
cv2.drawContours(self.convexImage,[self.convex_hull],0,255,-1)
### PIXEL PARAMETERS
# mean value, minvalue, maxvalue
self.minval,self.maxval,self.minloc,self.maxloc = cv2.minMaxLoc(self.img,mask = self.filledImage)
self.meanval = cv2.mean(self.img,mask = self.filledImage)
### EXTREME POINTS ###
# Finds the leftmost, rightmost, topmost and bottommost points
self.leftmost = tuple(self.cnt[self.cnt[:,:,0].argmin()][0])
self.rightmost = tuple(self.cnt[self.cnt[:,:,0].argmax()][0])
self.topmost = tuple(self.cnt[self.cnt[:,:,1].argmin()][0])
self.bottommost = tuple(self.cnt[self.cnt[:,:,1].argmax()][0])
self.extreme = (self.leftmost,self.rightmost,self.topmost,self.bottommost)
### DISTANCE CALCULATION
def distance_image(self,point=None):
'''find the distance between a point and adjacent point on contour specified. Point should be a tuple or list (x,y)
If no point is given, distance to all point is calculated and distance image is returned'''
if type(point) == tuple:
if len(point)==2:
self.dist = cv2.pointPolygonTest(self.cnt,point,True)
return self.dist
else:
dst = np.empty(self.img.shape)
for i in xrange(self.img.shape[0]):
for j in xrange(self.img.shape[1]):
dst.itemset(i,j,cv2.pointPolygonTest(self.cnt,(j,i),True))
dst = dst+127
dst = np.uint8(np.clip(dst,0,255))
# plotting using palette method in numpy
palette = []
for i in xrange(256):
if i<127:
palette.append([2*i,0,0])
elif i==127:
palette.append([255,255,255])
elif i>127:
l = i-128
palette.append([0,0,255-2*l])
palette = np.array(palette,np.uint8)
self.h2 = palette[dst]
return self.h2
#### DEMO ######
if __name__=='__main__':
import sys
if len(sys.argv)>1:
image = sys.argv[1]
else:
image = 'new.bmp'
print "Usage : python contourfeatures.py <image_file>"
im = cv2.imread(image)
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
thresh = cv2.adaptiveThreshold(imgray,255,0,1,11,2)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
k = 1000
for cnt in contours:
# first shows the original image
im2 = im.copy()
c = Contour(imgray,cnt)
print c.leftmost,c.rightmost
cv2.putText(im2,'original image',(20,20), cv2.FONT_HERSHEY_PLAIN, 1.0,(0,255,0))
cv2.imshow('image',im2)
if cv2.waitKey(k)==27:
break
im2 = im.copy()
# Now shows original contours, approximated contours, convex hull
cv2.drawContours(im2,[cnt],0,(0,255,0),4)
string1 = 'green : original contour'
cv2.putText(im2,string1,(20,20), cv2.FONT_HERSHEY_PLAIN, 1.0,(0,255,0))
cv2.imshow('image',im2)
if cv2.waitKey(k)==27:
break
approx = c.approx
cv2.drawContours(im2,[approx],0,(255,0,0),2)
string2 = 'blue : approximated contours'
cv2.putText(im2,string2,(20,40), cv2.FONT_HERSHEY_PLAIN, 1.0,(0,255,0))
cv2.imshow('image',im2)
if cv2.waitKey(k)==27:
break
hull = c.convex_hull
cv2.drawContours(im2,[hull],0,(0,0,255),2)
string3 = 'red : convex hull'
cv2.putText(im2,string3,(20,60), cv2.FONT_HERSHEY_PLAIN, 1.0,(0,255,0))
cv2.imshow('image',im2)
if cv2.waitKey(k)==27:
break
im2 = im.copy()
# Now mark centroid and bounding box on image
(cx,cy) = c.centroid
cv2.circle(im2,(int(cx),int(cy)),5,(0,255,0),-1)
cv2.putText(im2,'green : centroid',(20,20), cv2.FONT_HERSHEY_PLAIN, 1.0,(0,255,0))
(x,y,w,h) = c.bounding_box
cv2.rectangle(im2,(x,y),(x+w,y+h),(0,0,255))
cv2.putText(im2,'red : bounding rectangle',(20,40), cv2.FONT_HERSHEY_PLAIN, 1.0,(0,255,0))
(center , axis, angle) = c.ellipse
cx,cy = int(center[0]),int(center[1])
ax1,ax2 = int(axis[0]),int(axis[1])
orientation = int(angle)
cv2.ellipse(im2,(cx,cy),(ax1,ax2),orientation,0,360,(255,255,255),3)
cv2.putText(im2,'white : fitting ellipse',(20,60), cv2.FONT_HERSHEY_PLAIN, 1.0,(255,255,255))
cv2.circle(im2,c.leftmost,5,(0,255,0),-1)
cv2.circle(im2,c.rightmost,5,(0,255,0))
cv2.circle(im2,c.topmost,5,(0,0,255),-1)
cv2.circle(im2,c.bottommost,5,(0,0,255))
cv2.imshow('image',im2)
if cv2.waitKey(k)==27:
break
# Now shows the filled image, convex image, and distance image
filledimage = c.filledImage
cv2.putText(filledimage,'filledImage',(20,20), cv2.FONT_HERSHEY_PLAIN, 1.0,255)
cv2.imshow('image',filledimage)
if cv2.waitKey(k)==27:
break
conveximage = c.convexImage
cv2.putText(conveximage,'convexImage',(20,20), cv2.FONT_HERSHEY_PLAIN, 1.0,255)
cv2.imshow('image',conveximage)
if cv2.waitKey(k)==27:
break
distance_image = c.distance_image()
cv2.imshow('image',distance_image)
cv2.putText(distance_image,'distance_image',(20,20), cv2.FONT_HERSHEY_PLAIN, 1.0,(255,255,255))
if cv2.waitKey(k)==27:
break
cv2.destroyAllWindows()
hey bro i've been trying to figure out the contour functions in python binding.. any idea on how to proceed ?.. Just guide me through how to retrieve contour information and stuff like that..!!
ReplyDeleteHi, this full article is about extracting contour features. I have tried to include all properties here. Explain what you want to do exactly?
Deletei am having a bit of problem with this code. The convex area comes out to 0 and stops the execution. also if i try to except that error then a problem arises in the ellipse fitting.
ReplyDeleteHi, you need to add more details. Area zero means, there may be no contours detected. Check your input image. You need to provide here a print of error, error lines etc.
DeleteI'm having the same problem.
DeleteTraceback (most recent call last):
File "../nova/contourfeatures.py", line 212, in
c = Contour(imgray,cnt)
File "../nova/contourfeatures.py", line 103, in __init__
self.solidity = self.area/float(self.convex_area)
ZeroDivisionError: float division by zero
Hi, this was not meant to be a tutorial actually, just a piece of code written for my use. Check other articles on contour features in this blog and try yourself.
DeleteMeanwhile, check what is the area of actual contour. Even a single white pixel is selected as a contour, but its area would be zero.
do you know how to calculate shape elongation explained here http://www.site.uottawa.ca/~mstoj075/Publications_files/elongation-JMIV.pdf like m the deļ¬nition of shape orientation which is based on the axis of the last second moment of inertia. Precisely, the axis of the least second moment of inertia [6, 7, 9] is the line which minimizes the integral of the squares of distances of the points (belonging to
ReplyDeletethe shape) to the line.