OpenCV Python single (rather than multiple) blob tracking?
Question: I've been trying to get single color blob tracking thru OpenCV on Python. The
below code is working, but it finds the centroid of all the tracked pixels,
not just the centroid of the biggest blob. This is because I'm taking the
moments of all the pixels, but I'm not sure how else to color track. I'm kind
of stuck on what exactly I need to do to make this a single blob tracker
instead of a multi-blob average-er.

Here's the code:

    
    
    #! /usr/bin/env python 
    
    #if using newer versions of opencv, just "import cv"
    import cv2.cv as cv
    
    color_tracker_window = "Color Tracker" 
    
    class ColorTracker: 
    
    def __init__(self): 
        cv.NamedWindow( color_tracker_window, 1 ) 
        self.capture = cv.CaptureFromCAM(0) 
    
    def run(self): 
        while True: 
            img = cv.QueryFrame( self.capture ) 
    
            #blur the source image to reduce color noise 
            cv.Smooth(img, img, cv.CV_BLUR, 3); 
    
            #convert the image to hsv(Hue, Saturation, Value) so its  
            #easier to determine the color to track(hue) 
            hsv_img = cv.CreateImage(cv.GetSize(img), 8, 3) 
            cv.CvtColor(img, hsv_img, cv.CV_BGR2HSV) 
    
            #limit all pixels that don't match our criteria, in this case we are  
            #looking for purple but if you want you can adjust the first value in  
            #both turples which is the hue range(120,140).  OpenCV uses 0-180 as  
            #a hue range for the HSV color model 
            thresholded_img =  cv.CreateImage(cv.GetSize(hsv_img), 8, 1) 
            cv.InRangeS(hsv_img, (120, 80, 80), (140, 255, 255), thresholded_img) 
    
            #determine the objects moments and check that the area is large  
            #enough to be our object 
            moments = cv.Moments(thresholded_img, 0) 
            area = cv.GetCentralMoment(moments, 0, 0) 
    
            #there can be noise in the video so ignore objects with small areas 
            if(area > 100000): 
                #determine the x and y coordinates of the center of the object 
                #we are tracking by dividing the 1, 0 and 0, 1 moments by the area 
                x = cv.GetSpatialMoment(moments, 1, 0)/area 
                y = cv.GetSpatialMoment(moments, 0, 1)/area 
    
                #print 'x: ' + str(x) + ' y: ' + str(y) + ' area: ' + str(area) 
    
                #create an overlay to mark the center of the tracked object 
                overlay = cv.CreateImage(cv.GetSize(img), 8, 3) 
    
                cv.Circle(overlay, (x, y), 2, (255, 255, 255), 20) 
                cv.Add(img, overlay, img) 
                #add the thresholded image back to the img so we can see what was  
                #left after it was applied 
                cv.Merge(thresholded_img, None, None, None, img) 
    
            #display the image  
            cv.ShowImage(color_tracker_window, img) 
    
            if cv.WaitKey(10) == 27: 
                break 
    
    if __name__=="__main__": 
        color_tracker = ColorTracker() 
        color_tracker.run() 
    


Answer: You need to do it like this :

1) Get the thresholded image using inRange function, and you can apply some
erosion and dilation to remove small noisy particles. It will help to improve
the processing speed.

2) find Contours using 'findContours' function

3) find areas of contours using 'contourArea' function and select one with
maximum area.

4) Now find its center as you did, and track it.

Below is a sample code for this in new cv2 module :

    
    
    import cv2
    import numpy as np
    
    # create video capture
    cap = cv2.VideoCapture(0)
    
    while(1):
    
        # read the frames
        _,frame = cap.read()
    
        # smooth it
        frame = cv2.blur(frame,(3,3))
    
        # convert to hsv and find range of colors
        hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
        thresh = cv2.inRange(hsv,np.array((0, 80, 80)), np.array((20, 255, 255)))
        thresh2 = thresh.copy()
    
        # find contours in the threshold image
        contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
    
        # finding contour with maximum area and store it as best_cnt
        max_area = 0
        for cnt in contours:
            area = cv2.contourArea(cnt)
            if area > max_area:
                max_area = area
                best_cnt = cnt
    
        # finding centroids of best_cnt and draw a circle there
        M = cv2.moments(best_cnt)
        cx,cy = int(M['m10']/M['m00']), int(M['m01']/M['m00'])
        cv2.circle(frame,(cx,cy),5,255,-1)
    
        # Show it, if key pressed is 'Esc', exit the loop
        cv2.imshow('frame',frame)
        cv2.imshow('thresh',thresh2)
        if cv2.waitKey(33)== 27:
            break
    
    # Clean up everything before leaving
    cv2.destroyAllWindows()
    cap.release()
    

You can find some samples on tracking colored objects here :
<https://github.com/abidrahmank/OpenCV-Python/tree/master/Other_Examples>

Also, try to use new cv2 interface. It is a lot simpler and faster than old
cv. For more details, checkout this : [What is different between all these
OpenCV Python interfaces?](http://stackoverflow.com/questions/10417108/what-
is-different-between-all-these-opencv-python-interfaces/10425504#10425504)

