OmniTracking.hh

Go to the documentation of this file.
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

Generated on Tue Dec 26 10:32:38 2006 for Omnimeeting by  doxygen 1.4.7