00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 """
00026 Tracker classes must implement track( frame ) which returns a tuple (x,y,area)
00027 if the tracker does not find an object then it will return None
00028 """
00029
00030 import numpy as np
00031 import math
00032 import cv2
00033 import time
00034
00035 lk_params = dict( winSize = ( 30, 30 ),
00036 maxLevel = 2,
00037 criteria = ( cv2.TERM_CRITERIA_EPS |
00038 cv2.TERM_CRITERIA_COUNT, 10, 0.03 ) )
00039
00040 feature_params = dict( maxCorners = 50,
00041 qualityLevel = 0.3,
00042 minDistance = 10,
00043 blockSize = 10 )
00044
00045 class DummyTracker:
00046 def track( self, frame ):
00047 (y,x,n) = frame.shape
00048 cv2.imshow( 'Dummy', frame )
00049 return( 50, 50, 50 )
00050
00051 class LkTracker:
00052 def __init__(self):
00053 self.track_len = 10
00054 self.detect_interval = 5
00055 self.tracks = []
00056 self.frame_idx = 0
00057 self.frame = None
00058 self.mouseDown = False
00059 self.timers = {}
00060
00061 self.userRect = None
00062
00063
00064
00065 self.vis = None
00066
00067 def on_mouse( self, event, x, y, flags, param ):
00068 if self.frame is None:
00069 return
00070
00071 if event == cv2.cv.CV_EVENT_LBUTTONDOWN:
00072 self.mouseDown = True
00073 self.userRect = np.int32( ( ( x, y ), ( x, y ) ) )
00074
00075 elif event == cv2.cv.CV_EVENT_MOUSEMOVE and self.mouseDown == True:
00076 xmin = min( self.userRect[0,0], self.userRect[1,0], x)
00077 xmax = max( self.userRect[0,0], self.userRect[1,0], x)
00078 ymin = min( self.userRect[0,1], self.userRect[1,1], y)
00079 ymax = max( self.userRect[0,1], self.userRect[1,1], y)
00080 self.userRect = np.int32( ( ( xmin, ymin ), ( xmax, ymax ) ) )
00081
00082 elif event == cv2.cv.CV_EVENT_LBUTTONUP:
00083 self.mouseDown = False
00084 self.pickFeatures()
00085 self.initCamshift()
00086 self.userRect = None
00087
00088 def initCamshift(self):
00089 x0,y0,x1,y1 = (self.userRect[0,0],
00090 self.userRect[0,1],
00091 self.userRect[1,0],
00092 self.userRect[1,1])
00093
00094 hsv_roi = self.hsv[ y0:y1, x0:x1 ]
00095 mask_roi = self.hsv_mask[ y0:y1, x0:x1 ]
00096
00097 hist = cv2.calcHist( [hsv_roi], [0], mask_roi, [16], [0,180] )
00098 cv2.normalize( hist, hist, 0, 255, cv2.NORM_MINMAX )
00099 self.hist = hist.reshape(-1)
00100 self.track_window = ( x0, y0, x1-x0, y1-y0 )
00101 self.showHist()
00102
00103 def getCamShift(self):
00104 prob = cv2.calcBackProject( [self.hsv], [0], self.hist, [0,180], 1 )
00105 prob &= self.hsv_mask
00106 term_crit = ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )
00107 track_box, self.track_window = cv2.CamShift( prob, self.track_window,
00108 term_crit )
00109
00110 return track_box
00111
00112 def showHist(self):
00113 bin_count = self.hist.shape[0]
00114 bin_w = 24
00115 img = np.zeros((256, bin_count*bin_w, 3), np.uint8)
00116 for i in xrange(bin_count):
00117 h = int(self.hist[i])
00118 cv2.rectangle(img, (i*bin_w+2, 255), ((i+1)*bin_w-2, 255-h), (int(180.0*i/bin_count), 255, 255), -1)
00119 img = cv2.cvtColor(img, cv2.COLOR_HSV2BGR)
00120
00121
00122 def pickFeatures(self):
00123 mask = np.zeros_like( self.frame_gray )
00124
00125
00126 cv2.rectangle( mask, tuple( self.userRect[0] ), tuple( self.userRect[1] ), 255, -1 )
00127
00128
00129 start = cv2.getTickCount() / cv2.getTickFrequency()
00130 p = cv2.goodFeaturesToTrack( self.frame_gray, mask = mask, **feature_params )
00131 self.timers['GoodFeatures'] = cv2.getTickCount() / cv2.getTickFrequency() - start
00132
00133 if p is not None:
00134 self.tracks = [ [ (x,y) ] for x, y in np.float32(p).reshape(-1, 2) ]
00135
00136 def equalizeHist(self, frame):
00137 splits = cv2.split( frame )
00138 equalized = map( cv2.equalizeHist, splits )
00139 return cv2.merge( equalized )
00140
00141 def initializeFrame( self, frame ):
00142 self.frame = cv2.pyrDown( frame )
00143 self.hsv = cv2.cvtColor( self.frame, cv2.COLOR_BGR2HSV )
00144 self.hsv = self.equalizeHist( self.hsv )
00145 self.hsv_mask = cv2.inRange( self.hsv, np.array((0.0, 33.0, 33.0)),
00146 np.array((180.0,254.,254.)) )
00147
00148
00149 self.hsv_mask = cv2.dilate( self.hsv_mask, None, iterations = 5 )
00150
00151
00152 hsv_split = cv2.split( self.hsv )
00153 self.frame_gray = hsv_split[0]
00154
00155
00156
00157 cv2.imshow( 'gray', self.frame_gray )
00158
00159
00160 def filterOutliers( self, deviations ):
00161 pts = np.int32( [ tr[-1] for tr in self.tracks ] )
00162 if pts.size < 10:
00163 return
00164
00165 x_median = np.median( pts[:,0] )
00166 y_median = np.median( pts[:,1] )
00167
00168 distances = ( np.square(pts[:,0] - x_median) +
00169 np.square(pts[:,1] - y_median) )
00170 distance_median = np.median( distances )
00171
00172 self.tracks = [ tr for (i,tr) in enumerate( self.tracks )
00173 if distances[i] < distance_median *
00174 np.square( deviations ) ]
00175
00176 def runOpticalFlow( self ):
00177 if len(self.tracks) > 0:
00178 img0, img1 = self.prev_gray, self.frame_gray
00179 p0 = np.float32([tr[-1] for tr in self.tracks]).reshape(-1, 1, 2 )
00180 start = cv2.getTickCount() / cv2.getTickFrequency()
00181 p1, st, err = cv2.calcOpticalFlowPyrLK( img0, img1, p0, None, **lk_params )
00182 p0r, st, err = cv2.calcOpticalFlowPyrLK( img1, img0, p1, None, **lk_params )
00183 self.timers['LKTrack'] = cv2.getTickCount() / cv2.getTickFrequency() - start
00184
00185 d = abs( p0-p0r ).reshape(-1, 2).max(-1)
00186 good = d < 1
00187 new_tracks = []
00188 for tr, (x, y), good_flag in zip( self.tracks, p1.reshape(-1,2), good ):
00189 if not good_flag:
00190 continue
00191 tr.append((x, y))
00192 if len(tr) > self.track_len:
00193 del tr[0]
00194
00195 new_tracks.append(tr)
00196
00197 self.tracks = new_tracks
00198
00199 def reDetect( self, use_camshift = True, expand_pixels = 1 ):
00200 if self.frame_idx % self.detect_interval == 0 and len(self.tracks) > 0:
00201
00202 if use_camshift:
00203 mask_camshift = np.zeros_like( self.frame_gray )
00204 ellipse_camshift = self.getCamShift()
00205 cv2.ellipse( mask_camshift, ellipse_camshift, 255, -1 )
00206
00207 mask_tracked = np.zeros_like( self.frame_gray )
00208 pts = np.int32( [ [tr[-1]] for tr in self.tracks ] )
00209
00210 if len(pts) > 5:
00211 ellipse_tracked = cv2.fitEllipse( pts )
00212 else:
00213 ellipse_tracked = cv2.minAreaRect( pts )
00214
00215 boundingRect = cv2.boundingRect( pts )
00216
00217
00218
00219
00220
00221
00222 x0,y0,x1,y1 = ( boundingRect[0] - expand_pixels,
00223 boundingRect[1] - expand_pixels,
00224 boundingRect[0] + boundingRect[2] + expand_pixels,
00225 boundingRect[1] + boundingRect[3] + expand_pixels )
00226
00227 if len( pts ) > 2:
00228 cv2.rectangle( mask_tracked, (x0,y0), (x1,y1), 255, -1 )
00229
00230 cv2.ellipse( mask_tracked, ellipse_tracked, 255, -1 )
00231
00232
00233
00234
00235 if use_camshift:
00236 mask = mask_camshift & mask_tracked
00237 else:
00238 mask = mask_tracked
00239
00240 for x, y in [np.int32(tr[-1]) for tr in self.tracks]:
00241 cv2.circle(mask, (x,y), 5, 0, -1 )
00242
00243
00244 start = cv2.getTickCount() / cv2.getTickFrequency()
00245 p = cv2.goodFeaturesToTrack( self.frame_gray, mask = mask, **feature_params )
00246 self.timers['GoodFeatures'] = cv2.getTickCount() / cv2.getTickFrequency() - start
00247
00248 if p is not None:
00249 for x, y in np.float32(p).reshape(-1, 2):
00250 self.tracks.append([(x,y)])
00251
00252 def drawAndGetTrack( self ):
00253 vis = self.frame.copy()
00254
00255 if len(self.tracks) > 0:
00256 cv2.polylines( vis, [ np.int32(tr) for tr in self.tracks ], False, ( 0, 255, 0 ) )
00257 pts = np.int32( [ [tr[-1]] for tr in self.tracks ] )
00258
00259 for [(x,y)] in pts:
00260 cv2.circle( vis, (x, y), 2, (0, 255, 0), -1 )
00261
00262 if len( pts ) > 2:
00263 boundingRect = cv2.boundingRect( pts )
00264 x0,y0,x1,y1 = ( boundingRect[0], boundingRect[1],
00265 boundingRect[0] + boundingRect[2],
00266 boundingRect[1] + boundingRect[3] )
00267 area = boundingRect[3] * boundingRect[2]
00268 cx,cy = ( boundingRect[0] + boundingRect[2]/2,
00269 boundingRect[1] + boundingRect[3]/2 )
00270
00271 cv2.rectangle( vis, (x0,y0), (x1,y1), (0,255,255), 3, 8, 0 )
00272
00273 cv2.circle( vis, (cx,cy), 5, (0,255,255), -1, 8, 0 )
00274
00275 if self.userRect != None:
00276 cv2.rectangle( vis, tuple( self.userRect[0] ), tuple( self.userRect[1] ), ( 255, 255, 255 ) )
00277
00278 i = 0
00279 total = 0
00280 for ( timer, time ) in self.timers.items():
00281 i += 1
00282 total += time
00283 cv2.putText( vis, '%s: %.1f ms' % ( timer, time * 1e3 ),
00284 ( 20, i*20 ), cv2.FONT_HERSHEY_PLAIN, 1.0,
00285 (255, 255, 255) )
00286 cv2.putText( vis, '%s: %.1f ms' % ( timer, time * 1e3 ),
00287 ( 20, i*20 ), cv2.FONT_HERSHEY_PLAIN, 1.0,
00288 ( 0, 0, 0), thickness = 2 )
00289
00290
00291 i += 1
00292 cv2.putText( vis, 'Total: %.1f ms' % ( total * 1e3 ),
00293 ( 20, i*20 ), cv2.FONT_HERSHEY_PLAIN, 1.0,
00294 (255, 255, 255) )
00295 cv2.putText( vis, 'Total: %.1f ms' % ( total * 1e3 ),
00296 ( 20, i*20 ), cv2.FONT_HERSHEY_PLAIN, 1.0,
00297 ( 0, 0, 0), thickness = 2 )
00298
00299 self.vis = vis.copy()
00300
00301
00302
00303
00304 if len( self.tracks ) > 2:
00305 imageSize = self.frame.shape
00306 imageArea = imageSize[0]*imageSize[1]
00307
00308 xRel = cx*100/imageSize[1]
00309 yRel = cy*100/imageSize[0]
00310 areaRel = math.sqrt( float( area ) / float( imageArea ) ) * 100
00311 return xRel,yRel,areaRel,cx,cy
00312 else:
00313 return None
00314
00315 def get_vis( self ):
00316 return self.vis
00317
00318 def track(self, frame):
00319 self.initializeFrame( frame )
00320 self.runOpticalFlow()
00321 self.filterOutliers( 3 )
00322
00323 self.reDetect()
00324 trackData = self.drawAndGetTrack()
00325
00326 self.frame_idx += 1
00327 self.prev_gray = self.frame_gray
00328
00329 return trackData
00330
00331 class CascadeTracker( LkTracker ):
00332 def __init__( self, cascadeFn ):
00333 self.cascade = cv2.CascadeClassifier( cascadeFn )
00334 self.trackData = None
00335 self.cascade_interval = 30
00336
00337 LkTracker.__init__( self )
00338
00339 def initCamshift( self ):
00340 pass
00341
00342 def track( self, frame ):
00343 self.frame = frame
00344 self.frame_gray = cv2.cvtColor( self.frame, cv2.COLOR_BGR2GRAY )
00345 self.frame_gray = cv2.equalizeHist( self.frame_gray )
00346
00347
00348 self.runOpticalFlow()
00349 self.trackData = self.drawAndGetTrack()
00350
00351
00352 detectedObject = self.detectObject()
00353
00354
00355 if detectedObject == None:
00356 self.filterOutliers( 4 )
00357 self.reDetect( False, 0 )
00358
00359
00360 else:
00361
00362 self.userRect = [ [ detectedObject[0],
00363 detectedObject[1] ],
00364 [ detectedObject[0] + detectedObject[2],
00365 detectedObject[1] + detectedObject[3] ] ]
00366 self.pickFeatures()
00367
00368
00369 corners = [ [ ( detectedObject[0], detectedObject[1] ) ],
00370 [ ( detectedObject[0] + detectedObject[2],
00371 detectedObject[1] ) ],
00372 [ ( detectedObject[0],
00373 detectedObject[1] + detectedObject[3] ) ],
00374 [ ( detectedObject[0] + detectedObject[2],
00375 detectedObject[1] + detectedObject[3] ) ]
00376 ]
00377
00378 self.tracks.extend( corners )
00379
00380 self.userRect = None
00381 self.trackData = self.drawAndGetTrack()
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395 self.frame_idx += 1
00396 self.prev_gray = self.frame_gray
00397
00398 return self.trackData
00399
00400 def detectObject( self ):
00401
00402 if self.frame_idx % self.cascade_interval != 0:
00403 return None
00404
00405 start = cv2.getTickCount() / cv2.getTickFrequency()
00406 objects = self.cascade.detectMultiScale( self.frame_gray,
00407 scaleFactor=1.3,
00408 minNeighbors=4,
00409 minSize=(15, 15),
00410 flags = cv2.cv.CV_HAAR_SCALE_IMAGE)
00411 self.timers['Cascade'] = cv2.getTickCount() / cv2.getTickFrequency() - start
00412
00413 vis = self.frame_gray.copy()
00414
00415 if len( objects ) > 0:
00416 for i in objects:
00417 cv2.rectangle( vis,
00418 ( int(i[0]), int(i[1]) ),
00419 ( int(i[0]+i[2]), int(i[1]+i[3]) ),
00420 cv2.cv.CV_RGB(0,255,0), 3, 8, 0)
00421
00422
00423
00424
00425
00426
00427
00428 if len( objects ) > 0:
00429 if len( objects ) == 1 or not self.trackData:
00430 return objects[0]
00431 else:
00432 distances_squared = ( np.square(objects[:,0]+objects[:,2]/2 -
00433 self.trackData[3]) +
00434 np.square(objects[:,1]+objects[:,2]/2 -
00435 self.trackData[4]) )
00436 min_index = np.argmin( distances_squared )
00437 return objects[min_index]
00438 else:
00439 return None
00440
00441 def main():
00442 import sys
00443 try: video_src = sys.argv[1]
00444 except: video_src = 0
00445
00446 tracker = CascadeTracker(
00447 '/home/sameer/ros/falkor_ardrone/cascade/haarcascade_falkorlogopaper.xml' )
00448 cam = cv2.VideoCapture( video_src )
00449
00450 while True:
00451 ret, frame = cam.read()
00452 if ret:
00453 trackData = tracker.track( frame )
00454
00455 if trackData:
00456 print trackData
00457
00458 ch = 0xFF & cv2.waitKey(1)
00459 if ch == 27:
00460 break
00461
00462 if __name__ == '__main__':
00463 main()
00464
00465
00466
00467
00468
00469
00470
00471
00472