Showing posts with label centroid. Show all posts
Showing posts with label centroid. Show all posts

Saturday, June 16, 2012

Contours - 3 : Extraction

Hi,

This is our third article on contours and direct continuation of Contours 1 : Getting Started and Contours - 2 : Brotherhood. Hope you have read and understood it well before reading this.

In this article, we won't be using any new function from OpenCV, instead we use the methods from previous article to extract useful data of a contour or an object. You will be using some of these routines in your codes often. So we can get into the topic now.

What are these features actually ? Yes, that is a relative question, i think. It can be anything you want to find about an object and it directly depends on your goals. Some times, you may be interested in its size, sometimes its center, or its average color, or minimum and maximum intensity of that object, and even its orientation, ie its slope etc. I would like to list some of the normally used features.

1 - Area and Perimeter :

This, we have already discussed in last articles, which can be found out using cv2.contourArea() and cv2.arcLength() functions, respectively. You can refer that.

2 - Centroid :

Centroids are found using cv2.Moments() function where centroid can be defined as :

centroid_x = M10/M00 and centroid_y = M01/M00

M = cv2.moments(cnt)
centroid_x = int(M['m10']/M['m00'])
centroid_y = int(M['m01']/M['m00'])

Remember, actual result obtained will be 'float', so convert it into 'int'.

If you draw a circle at that point, you can see the centroid.








3 - Aspect Ratio :

Aspect Ratio is the ratio of width to height.

It will be useful in the cases where you want to filter out some shapes. The best example which comes to my mind is ANPR (Automatic Number Plate Recognition). ANPR is used in several traffic surveillance systems to track vehicles going that way. So, in such scenarios, first step is to extract rectangles in the image (since number plate is a rectangle). But there may be false ones also. So use aspect ratio to remove unwanted rectangles (You can google several papers using this method)

x,y,w,h = cv2.boundingRect(cnt)
aspect_ratio = float(w)/h

4 - Extent :

Extent is the ratio of contour area to bounding rectangle area.

area = cv2.contourArea(cnt)
x,y,w,h = cv2.boundingRect(cnt)
rect_area = w*h
extent = float(area)/rect_area

5 - Solidity :

Solidity is the ratio of contour area to its convex hull area.

area = cv2.contourArea(cnt)
hull = cv2.convexHull(cnt)
hull_area = cv2.contourArea(hull)
solidity = float(area)/hull_area

6 - Equivalent Diameter :

Equivalent Diameter is the diameter of the circle whose area is same as the contour area.

It is calculated as, Equivalent Diameter = 4 * A / Π
where A = Area of contour

area = cv2.contourArea(cnt)
equi_diameter = np.sqrt(4*area/np.pi)

7 - Orientation :

Orientation is the angle at which object is directed.

(x,y),(MA,ma),angle = cv2.fitEllipse(cnt)

8 - Pixel Points :

In some cases, we may need all the points which comprises that object. It can be done as follows:

mask = np.zeros(imgray.shape,np.uint8)
cv2.drawContours(mask,[cnt],0,255,-1)
pixelpoints = np.transpose(np.nonzero(mask))

9 - Maximum Value and Minimum Value :

We can find these parameters using a mask image.

min_val, max_val, min_loc,max_loc = cv2.minMaxLoc(imgray,mask = mask)

where mask is same as above. Remember, this is for grayscale images, not for color images.

10 - Mean Color or Mean Intensity :

Here, we can find the average color of an object. Or it can be average intensity of the object in grayscale mode. We again use the same mask to do it.

mean_val = cv2.mean(im,mask = mask)

Remember, if you are trying for color matching or color based object tracking, first convert image to HSV space, because HSV is more better representation of color that RGB space. We will deal it in more detail in another article.

11 - Extreme Points :

Extreme Points means topmost,bottommost,rightmost,leftmost points of the object.

leftmost = tuple(cnt[cnt[:,:,0].argmin()][0])
rightmost = tuple(cnt[cnt[:,:,0].argmax()][0])
topmost = tuple(cnt[cnt[:,:,1].argmin()][0])
bottommost = tuple(cnt[cnt[:,:,1].argmax()][0])

For eg, if I apply it to an Indian map, I get the following result :

Extreme Points

For those who couldn't understand above piece of code, I will explain one for you, ie leftmost.

We have contour points (x,y) stored as a [rows,1,2]. Number of rows equal to number of contour points. So to find the leftmost point, we need to find the point where 'x' is minimum. 'y' doesn't matter. So for that, we extract 'x' coordinates of all the points.

x = cnt[ : , : , 0]

Now we find the location of minimum value in it.( Not minimum value, but position or index of the minimum value)

x_min_loc = x.argmin()

Now we find the point (x,y) in cnt at this location(x_min_loc).

point = cnt[x_min_loc]

Sometimes, there may be more than one leftmost points, like rectangles. So we have to take only one of them. And convert it into tuple.

leftmost = tuple(point[0])

That gives you the answer.

So these are some of the features used frequently.

Now only few more things are there to explain about the contours like convexity defects, point polygon test etc. We will be dealing it in next article.

Send me your feedbacks, comments etc.

Regards,
ARK


Thursday, April 19, 2012

Contour features


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()


Subscribe to: Posts (Atom)

AltStyle によって変換されたページ (->オリジナル) /