00001 /* 00002 * Copyright (C) Massimo Cora' 2005 <maxcvs@email.it> 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License 00015 * along with this program; if not, write to the Free Software 00016 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 00017 */ 00018 00019 00020 #ifndef __OMNITRACKING_H__ 00021 #define __OMNITRACKING_H__ 00022 00023 00024 #include <stdio.h> 00025 00026 // opencv 00027 #include <cv.h> 00028 #include <highgui.h> 00029 00030 #include "OmniConfig.hh" 00031 #include "OmniTrackingTable.hh" 00032 #include "OmniAngle.hh" 00033 00034 00039 typedef struct _added_point_data { 00040 00041 CvPoint point; 00042 OmniAngle<int> angle_int; 00043 } added_point_data_t; 00044 00045 00046 00055 template <class ConverterT, class AngleT, class ImageT, class PointT> 00056 class OmniTracking : public OmniTrackingTable<ConverterT, AngleT, ImageT, PointT> { 00057 00058 public: 00072 OmniTracking( IplImage* first_frame, 00073 OmniConversion<ConverterT, AngleT, ImageT, PointT> *conv_table, 00074 int max_tracked_points_count = 100, 00075 int freeze_frame_bound = FREEZE_FRAME_BOUND, 00076 double track_window_hitrate = TRACK_WINDOW_HITRATE ); 00077 virtual ~OmniTracking(); 00078 00083 bool add_point_to_track( CvPoint &point ); 00084 00091 void process_next_frame( IplImage *next_frame ); 00092 00102 void register_image_callbacks( on_create_image_context_cb* create_image_context, 00103 on_image_do_action_cb* image_show, 00104 on_destroy_image_context_cb* destroy_image_context, 00105 void* callback_data 00106 ); 00107 00108 private: // functions 00114 void initialize_image_buffers( IplImage* first_video_frame ); 00115 00117 on_create_image_context_cb* create_image_context; 00118 00120 on_image_do_action_cb* image_show; 00121 00123 on_destroy_image_context_cb* destroy_image_context; 00124 00125 private: // data 00126 00128 IplImage *_image, 00129 *_grey, 00130 *_prev_grey, 00131 *_pyramid, 00132 *_prev_pyramid, 00133 *_swap_temp; 00134 00135 void* _callback_data_on_image_cb; 00136 00138 int _win_size; 00139 00141 CvPoint2D32f *_tracked_points[2], 00142 *_points[2], 00143 *_swap_points; 00144 00146 int _max_tracked_points_count; 00147 char* _tracked_points_status; 00148 00150 int _tracked_points_count; 00151 00152 int _video_width; 00153 int _video_height; 00154 00156 int _track_flags; 00157 00158 bool _add_remove_pt; 00159 list<added_point_data_t*> _points_added_list; 00160 }; 00161 00162 00163 00167 00168 00169 template <class ConverterT, class AngleT, class ImageT, class PointT> 00170 OmniTracking<ConverterT, AngleT, ImageT, PointT>::OmniTracking( IplImage* first_video_frame, 00171 OmniConversion<ConverterT, AngleT, ImageT, PointT> *lu_table, 00172 int max_tracked_points_count /*= 100*/, 00173 int _freeze_frame_bound /* = FREEZE_FRAME_BOUND */, 00174 double _track_window_hitrate /* = TRACK_WINDOW_HITRATE*/ ) 00175 : OmniTrackingTable<ConverterT, AngleT, ImageT, PointT>( lu_table, _freeze_frame_bound, _track_window_hitrate ) { 00176 00177 this->_max_tracked_points_count = max_tracked_points_count; 00178 _tracked_points_status = 0; 00179 _tracked_points_count = 0; 00180 00181 _tracked_points[0] = 0; 00182 _tracked_points[1] = 0; 00183 00184 _points[0] = 0; 00185 _points[1] = 0; 00186 00187 _win_size = 10; 00188 00189 _video_width = first_video_frame->width; 00190 _video_height = first_video_frame->height; 00191 00192 // no points have been added right now. 00193 _add_remove_pt = false; 00194 00195 // set the default callbacks. They can be changed with register_callbacks () call. 00196 // callback data to NULL to avoid strange behaviour on pointers. 00197 register_image_callbacks( omnistuff_create_image_context, omnistuff_image_do_action, 00198 omnistuff_destroy_image_context, NULL ); 00199 00200 initialize_image_buffers( first_video_frame ); 00201 } 00202 00203 00204 //------------------------------------------------------------------------- 00205 // 00206 00207 template <class ConverterT, class AngleT, class ImageT, class PointT> 00208 OmniTracking<ConverterT, AngleT, ImageT, PointT>::~OmniTracking() { 00209 00210 if ( !_points_added_list.empty () ) { 00211 added_point_data_t* tmp_data; 00212 00213 while ( (tmp_data = _points_added_list.front ())) { 00214 _points_added_list.pop_front (); 00215 if ( tmp_data ) 00216 delete tmp_data; 00217 } 00218 } 00219 } 00220 00221 00222 //------------------------------------------------------------------------- 00223 // 00224 00225 template <class ConverterT, class AngleT, class ImageT, class PointT> 00226 void OmniTracking<ConverterT, AngleT, ImageT, PointT>::initialize_image_buffers( IplImage* first_video_frame ) { 00227 00228 // allocate all the buffers 00229 _image = cvCreateImage( cvGetSize( first_video_frame ), 8, 3 ); 00230 _image->origin = first_video_frame->origin; 00231 00232 // grey image 00233 _grey = cvCreateImage( cvGetSize( first_video_frame ), 8, 1 ); 00234 _prev_grey = cvCreateImage( cvGetSize( first_video_frame ), 8, 1 ); 00235 00236 // pyramid image 00237 _pyramid = cvCreateImage( cvGetSize( first_video_frame ), 8, 1 ); 00238 _prev_pyramid = cvCreateImage( cvGetSize( first_video_frame ), 8, 1 ); 00239 00240 // alloc the space for the max number of tracked points 00241 _tracked_points[0] = (CvPoint2D32f*) 00242 cvAlloc( _max_tracked_points_count * sizeof(_tracked_points[0][0])); 00243 _tracked_points[1] = (CvPoint2D32f*) 00244 cvAlloc( _max_tracked_points_count * sizeof(_tracked_points[0][0])); 00245 00246 _tracked_points_status = (char*)cvAlloc( _max_tracked_points_count ); 00247 _track_flags = 0; 00248 } 00249 00250 00251 00252 //------------------------------------------------------------------------- 00253 // perform the real "tracking" on the n-next frame. The input frame should be 00254 // subsequent of the previuos, in a "video succession" format fashion. 00255 00256 template <class ConverterT, class AngleT, class ImageT, class PointT> 00257 void OmniTracking<ConverterT, AngleT, ImageT, PointT>::process_next_frame( IplImage *next_frame ) { 00258 00259 int i, curr_face; 00260 00261 if ( !next_frame ) 00262 return; 00263 00264 cvCopy( next_frame, _image, 0 ); 00265 cvCvtColor( _image, _grey, CV_BGR2GRAY ); 00266 00267 // proceed only if we have some point tracked 00268 if( _tracked_points_count > 0 ) { 00269 00270 // LK tracking algorithm 00271 cvCalcOpticalFlowPyrLK( _prev_grey, _grey, _prev_pyramid, _pyramid, 00272 _tracked_points[0], _tracked_points[1], _tracked_points_count, 00273 cvSize( _win_size, _win_size ), 3, _tracked_points_status, 0, 00274 cvTermCriteria( CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.03 ), _track_flags ); 00275 00276 _track_flags |= CV_LKFLOW_PYR_A_READY; 00277 00278 00279 // before the for loop we must set the update status of the tracking windows: 00280 this->update_begin(); 00281 00282 // store the tmp value of tracked points, and have loop for every point 00283 int curr_tracked_points_count = _tracked_points_count; 00284 00285 for( i = curr_face = 0; i < curr_tracked_points_count; i++ ) { 00286 if( _add_remove_pt ) { 00287 /*/ rarely this happens. With tracking windows points are most far away one from the other 00288 // 00289 // let's test the difference [distance] between the points already 00290 // existing in tracking process: if it's minor than a tot throw away the point 00291 // and go on. NOTE: we don't remove it coz it could happen that two points 00292 // are tracking an object next to another one. 00293 // 00294 00295 double dx = point_last_added.x - tracked_points[1][i].x; 00296 double dy = point_last_added.y - tracked_points[1][i].y; 00297 00298 if( dx*dx + dy*dy <= 5 ) { 00299 // the new point added is a wrong one. So we set to false the adding flag, 00300 // discarding it. 00301 add_remove_pt = false; 00302 continue; 00303 } 00304 /*/ 00305 } 00306 00307 if( !_tracked_points_status[curr_face] ) 00308 continue; 00309 00310 CvPoint angle_point = cvPointFrom32f( _tracked_points[1][curr_face] ); 00311 00312 float rad_angle = 00313 atan2f( angle_point.y - ((float)this->m_conv_table->get_omni_center_y()), 00314 angle_point.x - ((float)this->m_conv_table->get_omni_center_x())); 00315 00316 // get the angle interval we want to track [= a window] 00317 OmniAngle<int> angle_int; 00318 OmniAngle<double> angle_double( rad_angle - FACE_TRACK_ANGLE_WINDOW, 00319 rad_angle + FACE_TRACK_ANGLE_WINDOW, 00320 2 * FACE_TRACK_ANGLE_WINDOW ); 00321 this->m_conv_table->angle_type_conversion( angle_double, angle_int ); 00322 00323 #ifdef SHOW_TRACKED_FACES 00324 char title[30]; 00325 sprintf(title, "Tracking face id #%d", curr_face ); 00326 create_image_context( title, _callback_data_on_image_cb ); 00327 #endif 00328 00329 // do an update tracking window here 00330 if ( this->update_tracking_window_bounds( curr_face, angle_int.angle_start, 00331 angle_int.angle_end ) == false ) { 00332 00333 // 00334 // adjust the tracked points array: 00335 // 00336 // | P0 | P1 | P2 | P3 | X | P5 | P6 | P7 | 00337 // ^--- deleted point 00338 // 00339 // | P0 | P1 | P2 | P3 | P5 | P6 | P7 | 00340 // 00341 00342 for ( int h = curr_face + 1; h < _tracked_points_count; h++ ) { 00343 _tracked_points[1][h - 1] = _tracked_points[1][h]; 00344 _tracked_points_status[h - 1] = _tracked_points_status[h]; 00345 } 00346 00347 #ifdef SHOW_TRACKED_FACES 00348 char title[30]; 00349 // remove the last window: their number is decreased by 1 00350 sprintf( title, "Tracking face id #%d", _tracked_points_count -1 ); 00351 destroy_image_context( title, _callback_data_on_image_cb ); 00352 #endif 00353 00354 // remove the tracking window. We don't need it anymore 00355 // This perform also a decrement in windows ids that stays 00356 // after the one we had just deleted 00357 this->remove_tracking_window( curr_face ); 00358 00359 _tracked_points_count--; 00360 continue; 00361 } 00362 00363 // curr_face++ is incremented only here: this is the tracked face index 00364 curr_face++; 00365 00366 00367 #ifdef SHOW_TRACKED_FACES 00368 IplImage *face; 00369 00370 #ifdef SHOW_GREEN_MARKERS 00371 // paint a circle on the tracked point 00372 cvCircle( next_frame, cvPointFrom32f( _tracked_points[1][i] ), 3, CV_RGB(0,255,0), -1, 8,0); 00373 #endif 00374 face = this->m_conv_table->omni2pano( next_frame, angle_int ); 00375 00376 // as from header 00377 // So !*DO NOT FREE*! the image when you call this function. 00378 image_show( face, curr_face -1, title, _callback_data_on_image_cb ); 00379 #endif 00380 } 00381 00382 // ok all done with the update. Signal it's ending and let the windows re-write their 00383 // status on the degree_table 00384 this->update_end(); 00385 00386 // set the correct number of tracked faces 00387 _tracked_points_count = curr_face; 00388 } 00389 00390 // process the new point added: we can parse the following "if" only if the requested 00391 // adding point is a good one. 00392 if( _add_remove_pt && _tracked_points_count < _max_tracked_points_count ) { 00393 list<added_point_data_t*>::iterator iter; 00394 00395 for ( iter = _points_added_list.begin(); iter != _points_added_list.end(); iter++ ) { 00396 added_point_data_t* data = (added_point_data_t*)(*iter); 00397 00398 // do this for all the points added [with motion they can be more than one at 00399 // the same time] 00400 _tracked_points[1][_tracked_points_count] = cvPointTo32f( data->point ); 00401 00402 // increment the total number of points 00403 _tracked_points_count++; 00404 00405 // let's add a tracking window! 00406 this->add_tracking_window( _tracked_points_count - 1, 00407 data->angle_int.angle_start, data->angle_int.angle_end ); 00408 00409 cvFindCornerSubPix( _grey, _tracked_points[1] + _tracked_points_count - 1, 1, 00410 cvSize( _win_size, _win_size ), cvSize( -1, -1 ), 00411 cvTermCriteria( CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.03) ); 00412 00413 // remove the point freeing its data 00414 delete data; 00415 } 00416 00417 // remove the elements 00418 // we don't need the data* anymore. So throw it away 00419 _points_added_list.erase( _points_added_list.begin(), _points_added_list.end() ); 00420 00421 // ok, switch off 00422 _add_remove_pt = false; 00423 } 00424 00425 CV_SWAP( _prev_grey, _grey, _swap_temp ); 00426 CV_SWAP( _prev_pyramid, _pyramid, _swap_temp ); 00427 CV_SWAP( _tracked_points[0], _tracked_points[1], _swap_points ); 00428 } 00429 00430 00431 00432 //------------------------------------------------------------------------- 00433 // perform an add of one or more points. These will be stored in a linked list 00434 // and processed in the next tracking loop 00435 00436 template <class ConverterT, class AngleT, class ImageT, class PointT> 00437 bool OmniTracking<ConverterT, AngleT, ImageT, PointT>::add_point_to_track( CvPoint &point_added ) { 00438 00439 if ( _tracked_points_count < _max_tracked_points_count ) { 00440 00441 // create a new list object 00442 added_point_data_t *data = new added_point_data_t; 00443 00444 // set the point 00445 data->point.x = point_added.x; 00446 data->point.y = point_added.y; 00447 00448 // and calculate the point angle: it's relative to the omni image center. 00449 float rad_angle = 00450 atan2f( (float)point_added.y - (float)(this->m_conv_table->get_omni_center_y()), 00451 (float)point_added.x - (float)(this->m_conv_table->get_omni_center_x())); 00452 00453 OmniAngle<double> angle_double (rad_angle - FACE_TRACK_ANGLE_WINDOW, 00454 rad_angle + FACE_TRACK_ANGLE_WINDOW, 00455 2 * FACE_TRACK_ANGLE_WINDOW); 00456 this->m_conv_table->angle_type_conversion( angle_double, data->angle_int ); 00457 00458 00459 // add it to the list 00460 _points_added_list.push_back( data ); 00461 00462 // add a wannabe trackable window 00463 this->add_wannabe_trackable_marks( data->angle_int.angle_start, 00464 data->angle_int.angle_end ); 00465 00466 // 00467 // we will check in the next loop if this point is a suitable one for tracking: 00468 // i.e. it has a minimum distance between the others tracked points 00469 // So here we just signal that a point has been added, a 00470 // _tracked_points_count++ will eventually happen later. 00471 // 00472 00473 _add_remove_pt = true; 00474 } 00475 00476 return true; 00477 } 00478 00479 00480 template <class ConverterT, class AngleT, class ImageT, class PointT> 00481 void OmniTracking<ConverterT, AngleT, ImageT, PointT>::register_image_callbacks( 00482 on_create_image_context_cb* create_image_context, 00483 on_image_do_action_cb* image_show, 00484 on_destroy_image_context_cb* destroy_image_context, 00485 void* callback_data 00486 ) 00487 { 00488 this->create_image_context = create_image_context; 00489 this->image_show = image_show; 00490 this->destroy_image_context = destroy_image_context; 00491 this->_callback_data_on_image_cb = callback_data; 00492 } 00493 00494 00495 #endif