surf.cpp

00001
00002 /***************************************************************************
00003  *  surf.cpp - SURF based classifier
00004  *
00005  *  Created: Tue Apr 01 10:15:23 2008
00006  *  Copyright 2008 Stefan Schiffer [stefanschiffer.de]
00007  *
00008  ****************************************************************************/
00009
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023
00024 #include <iostream>
00025 #include <vector>
00026
00027 #include <classifiers/surf.h>
00028 #include <math.h>
00029 //#ifdef SURF_TIMETRACKER
00030 #include <utils/time/clock.h>
00031 #include <utils/time/tracker.h>
00032 //#endif
00033 #include <fstream>
00034
00035 #include <string>
00036
00037 #include <surf/surflib.h>
00038 //#include <surf/ipoint.h>
00039 //#include <surf/image.h>
00040 //#include <surf/imload.h>
00041
00042 #include <core/exception.h>
00043 #include <core/exceptions/software.h>
00044 #include <fvutils/color/colorspaces.h>
00045 #include <fvutils/color/conversions.h>
00046 #include <fvutils/readers/png.h>
00047 #include <utils/system/console_colors.h>
00048 #include <dirent.h>
00049 #include <utils/logging/liblogger.h>
00050
00051 #define BVERBOSE true
00052 
00053 //#include <fvutils/writers/pnm.h>
00054 //#include <fvutils/writers/png.h>
00055 
00056 /** @class SurfClassifier <classifiers/surf.h>
00057  * SURF classifier.
00058  *
00059  * This class provides a classifier that uses SURF to detect objects in a given
00060  * image by matching features. The objects are reported back as regions of interest.
00061  * Each ROI contains an object. ROIs with 11x11 are matched features.
00062  *
00063  * This code uses libSurf from http://www.vision.ee.ethz.ch/~surf/
00064  * and is partly based on code from their package.
00065  *
00066  * @author Stefan Schiffer
00067  */
00068
00069 
00070 /** saveIpoints
00071  * save Surf points
00072  * @param sFileName surf file name
00073  * @param ipts surf ipoints (surf::iPoint)
00074  * @param bVerbose verbose mode
00075  * @param bLaplacian laplacian mode
00076  */
00077
00078 void saveIpoints(std::string sFileName, const std::vector< surf::Ipoint >& ipts, bool bVerbose, bool bLaplacian, int VLength)
00079 {
00080   std::cout<<"Attempting to save interest points" << std::endl;
00081
00082   std::ofstream ipfile(sFileName.c_str());
00083   if( !ipfile ) {
00084     std::cerr << "ERROR in loadIpoints(): "
00085               << "Couldn't open file '" << sFileName.c_str() << "'!" << std::endl; //STS
00086     return;
00087   }
00088
00089   double sc;
00090   unsigned count = ipts.size();
00091
00092   // Write the file header
00093   if (bLaplacian)
00094      ipfile << VLength + 1 << std::endl << count << std::endl;
00095    else
00096      ipfile << VLength << std::endl << count << std::endl;
00097   // In order to just save the interest points without descriptor, comment
00098   // the above and uncomment the following command.
00099   //  ipfile << 1.0 << std::endl << count << std::endl;
00100   // Save interest point with descriptor in the format of Krystian Mikolajczyk
00101   // for reasons of comparison with other descriptors. As our interest points
00102   // are circular in any case, we use the second component of the ellipse to
00103   // provide some information about the strength of the interest point. This is
00104   // important for 3D reconstruction as only the strongest interest points are
00105   // considered. Replace the strength with 0.0 in order to perform Krystian's
00106   // comparisons.
00107   for (unsigned n=0; n<ipts.size(); n++){
00108     // circular regions with diameter 5 x scale
00109     sc = 2.5 * ipts[n].scale; sc*=sc;
00110     ipfile  << ipts[n].x /* x-location of the interest point */
00111             << " " << ipts[n].y /* y-location of the interest point */
00112             << " " << 1.0/sc /* 1/r^2 */
00113             << " " << 0.0     //(*ipts)[n]->strength /* 0.0 */
00114             << " " << 1.0/sc; /* 1/r^2 */
00115
00116     if (bLaplacian)
00117       ipfile << " " << ipts[n].laplace;
00118
00119     // Here comes the descriptor
00120     for (int i = 0; i < VLength; i++) {
00121       ipfile << " " << ipts[n].ivec[i];
00122     }
00123     ipfile << std::endl;
00124   }
00125
00126   // Write message to terminal.
00127   if( bVerbose )
00128     std::cout << count << " interest points found" << std::endl;
00129 }
00130 
00131 /** loadIpoints
00132  * load interest points
00133  * @param sFileName location of the interest points
00134  * @param ipts vector to store interest points
00135  * @param bVerbose if the saveIpoints was carried out with verbose mode
00136  */
00137 void loadIpoints( std::string sFileName, std::vector< surf::Ipoint >& ipts, bool bVerbose, int& __vlen )
00138 {
00139   std::ifstream ipfile(sFileName.c_str());
00140
00141   if( !ipfile ) {
00142   std::cerr << "ERROR in loadIpoints(): "
00143             << "Couldn't open file '" << sFileName.c_str() << "'!" << std::endl; //STS
00144     return;
00145   }
00146
00147   // Load the file header
00148
00149   unsigned count;
00150   ipfile >> __vlen >> count;
00151
00152   __vlen--;
00153
00154   // create a new interest point vector
00155   ipts.clear();
00156   ipts.resize(count);
00157
00158   // Load the interest points in Mikolajczyk's format
00159   for (unsigned n=0; n<count; n++){
00160     // circular regions with diameter 5 x scale
00161     float x, y, a, b, c;
00162     ipfile >> x >> y >> a >> b >> c;
00163
00164     float det = sqrt((a-c)*(a-c) + 4.0*b*b);
00165     float e1 = 0.5*(a+c + det);
00166     float e2 = 0.5*(a+c - det);
00167     float l1 = (1.0/sqrt(e1));
00168     float l2 = (1.0/sqrt(e2));
00169     float sc = sqrt( l1*l2 );
00170
00171     ipts[n].x     = x;
00172     ipts[n].y     = y;
00173     ipts[n].scale = sc/2.5;
00174     ipfile >> ipts[n].laplace;
00175
00176     //ipts[n].allocIvec( VLength );
00177     ipts[n].ivec = new double[ __vlen];
00178
00179     for( int j = 0 ; j < __vlen; j++ )
00180       {
00181
00182         ipfile >> ipts[n].ivec[j];
00183
00184         //      std::cout << ipts[n].ivec[j] << " ";
00185       }
00186
00187   }
00188
00189   // close the interest point file again
00190   ipfile.close();
00191
00192   // Write message to terminal.
00193   if( bVerbose )
00194     std::cout << "read in " << count << " interest points." << std::endl;
00195 }
00196 
00197 /** Constructor.
00198  * @param keypoints_dir location of the keypoints (descriptor file as a txt file) for the reference objects
00199  * @param samplingStep Initial sampling step
00200  * @param min_match minimum number of features that have to be matched per ROI
00201  * @param min_match_ratio  minimum ratio of features matched per object to be matched per ROI
00202  * @param octaves Number of analysed octaves
00203  * @param thres Blob response treshold
00204  * @param doubleImageSize true to double the image size, false to keep original
00205  * @param initLobe Initial lobe size, default 3 and 5 (with double image size)
00206  * @param upright rotation invariance (fasle) or upright (true)
00207  * @param extended true to use the extended descriptor (SURF 128)
00208  * @param indexSize Spatial size of the descriptor window (default 4)
00209 
00210  */
00211 SurfClassifier::SurfClassifier( std::string keypoints_dir, unsigned int min_match, float min_match_ratio,
00212                                 int samplingStep, int octaves, double thres,
00213                                 bool doubleImageSize, int initLobe,
00214                                 bool upright, bool extended, int indexSize ): Classifier("SurfClassifier")
00215 {
00216   __obj_features.clear();
00217   __obj_features.reserve(1000);
00218   // matching constraints
00219   __min_match = min_match;
00220   __min_match_ratio = min_match_ratio;
00221   // params for FastHessian
00222   __samplingStep = samplingStep;
00223   __octaves = octaves;
00224   __thres = thres;
00225   __doubleImageSize = doubleImageSize;
00226   __initLobe = initLobe;
00227   // params for Descriptors
00228   __upright = upright;
00229   __extended = extended;
00230   __indexSize = indexSize;
00231
00232   // descriptor vector length
00233   __vlen = 0;
00234
00235   //#ifdef SURF_TIMETRACKER
00236   __tt = new fawkes::TimeTracker();
00237   __loop_count = 0;
00238   __ttc_objconv = __tt->add_class("ObjectConvert");
00239   __ttc_objfeat = __tt->add_class("ObjectFeatures");
00240   __ttc_imgconv = __tt->add_class("ImageConvert");
00241   __ttc_imgfeat = __tt->add_class("ImageFeatures");
00242   __ttc_matchin = __tt->add_class("Matching");
00243   __ttc_roimerg = __tt->add_class("MergeROIs");
00244   //#endif
00245
00246   //#ifdef SURF_TIMETRACKER
00247   __tt->ping_start(__ttc_objconv);
00248   //#endif
00249
00250   DIR *dir = 0;
00251
00252   if( (dir = opendir( keypoints_dir.c_str() ) ) == NULL ) {
00253           char* buffer = new char[256];
00254       sprintf(buffer, "The directory %s does not exist!", keypoints_dir.c_str() );
00255       fawkes::LibLogger::log_error("SurfClassifier",buffer);
00256   }
00257
00258   struct dirent* ent;
00259   std::string object_file;
00260   int num_obj_index = 0;
00261
00262
00263   while( (ent = readdir(dir)) != NULL ) {
00264
00265       if ( strcmp( ent->d_name, ".") == 0 || strcmp( ent->d_name,"..") == 0 || strcmp( ent->d_name,".svn") == 0 )
00266           continue;
00267
00268       object_file = keypoints_dir + ent->d_name;
00269       std:: cout<<"SurfClassifier: reading the following descriptor file" << object_file << std::endl;
00270
00271       __obj_names.push_back(object_file);
00272
00273
00274       bool b_verbose = BVERBOSE;
00275       loadIpoints( object_file, __obj_features[num_obj_index], b_verbose, __vlen);
00276       num_obj_index++;
00277
00278     }
00279
00280   closedir(dir);
00281   delete ent;
00282
00283   __num_obj = num_obj_index;
00284
00285   if( num_obj_index != 0 ) {
00286           std::cout<< "SurfClassifier: Reading successful"<< std::endl;
00287           //#ifdef SURF_TIMETRACKER
00288           __tt->ping_end(__ttc_objconv);
00289           //#endif
00290   }
00291   else {
00292 // if no objects were read, then the descriptor files were probably not created still. We can create them now!
00293           std::cout <<"SurfClassifier: The descriptor directory is probably empty since no objects were read off. Will instantiate a Surfclassifier with the png images directory") << std::endl;
00294           return new SurfClassifier( "../res/opx/objects/", 5 );
00295   }
00296
00297
00298   // save object image for debugging
00299   ///surf::ImLoad::saveImage( "obj.pgm", __obj_img);
00300 
00301   //#ifdef SURF_TIMETRACKER
00302   __tt->ping_start(__ttc_objfeat);
00303   //#endif
00304  //#ifdef SURF_TIMETRACKER
00305   __tt->ping_end(__ttc_objfeat);
00306   //#endif
00307
00308
00309 }
00310
00311
00312
00313
00314 
00315 /** Constructor.
00316  * @param object_dir file that contains an image of the object to detect
00317  * @param samplingStep Initial sampling step
00318  * @param min_match minimum number of features that have to be matched per ROI
00319  * @param min_match_ratio  minimum ratio of features matched per object to be matched per ROI
00320  * @param octaves Number of analysed octaves
00321  * @param thres Blob response treshold
00322  * @param doubleImageSize true to double the image size, false to keep original
00323  * @param initLobe Initial lobe size, default 3 and 5 (with double image size)
00324  * @param upright rotation invariance (fasle) or upright (true)
00325  * @param extended true to use the extended descriptor (SURF 128)
00326  * @param indexSize Spatial size of the descriptor window (default 4)
00327  */
00328
00329
00330 SurfClassifier::SurfClassifier( const char * object_dir,
00331                                 unsigned int min_match, float min_match_ratio,
00332                                 int samplingStep, int octaves, double thres,
00333                                 bool doubleImageSize, int initLobe,
00334                                 bool upright, bool extended, int indexSize)
00335   : Classifier("SurfClassifier")
00336 {
00337
00338   __obj_features.clear();
00339   __obj_features.reserve(1000);
00340   // matching constraints
00341   __min_match = min_match;
00342   __min_match_ratio = min_match_ratio;
00343   // params for FastHessian
00344   __samplingStep = samplingStep;
00345   __octaves = octaves;
00346   __thres = thres;
00347   __doubleImageSize = doubleImageSize;
00348   __initLobe = initLobe;
00349   // params for Descriptors
00350   __upright = upright;
00351   __extended = extended;
00352   __indexSize = indexSize;
00353
00354   // descriptor vector length
00355   __vlen = 0;
00356
00357
00358   //#ifdef SURF_TIMETRACKER
00359   __tt = new fawkes::TimeTracker();
00360   __loop_count = 0;
00361   __ttc_objconv = __tt->add_class("ObjectConvert");
00362   __ttc_objfeat = __tt->add_class("ObjectFeatures");
00363   __ttc_imgconv = __tt->add_class("ImageConvert");
00364   __ttc_imgfeat = __tt->add_class("ImageFeatures");
00365   __ttc_matchin = __tt->add_class("Matching");
00366   __ttc_roimerg = __tt->add_class("MergeROIs");
00367   //#endif
00368
00369   //#ifdef SURF_TIMETRACKER
00370   __tt->ping_start(__ttc_objconv);
00371   //#endif
00372
00373
00374   DIR *dir = 0;
00375
00376   std::string dir_path = object_dir;
00377
00378   if( (dir = opendir( dir_path.c_str() ) ) == NULL )
00379     {
00380       char* buffer = new char[256];
00381       sprintf(buffer, "The directory %s does not exist!", dir_path.c_str() );
00382
00383       fawkes::LibLogger::log_error("SurfClassifier",buffer);
00384     }
00385
00386   struct dirent* ent;
00387   std::string object_file;
00388   int num_obj_index = 0;
00389
00390   while( (ent = readdir(dir)) != NULL ) {
00391
00392           if ( strcmp( ent->d_name, ".") == 0 || strcmp( ent->d_name,"..") == 0 || strcmp( ent->d_name,".svn") == 0)
00393                   continue;
00394
00395           object_file = dir_path + ent->d_name;
00396
00397 //       if( !object_file && strcmp( object_file, "" ) == 0 ) {
00398 //     throw fawkes::Exception("empty object file");
00399 //   }
00400
00401   std::cout << "SurfClassifier(classify): opening object image file '" << object_file << "'" << std::endl;
00402
00403   PNGReader pngr( object_file.c_str() );
00404   unsigned char* buf = malloc_buffer( pngr.colorspace(), pngr.pixel_width(), pngr.pixel_height() );
00405   pngr.set_buffer( buf );
00406   pngr.read();
00407
00408   unsigned int lwidth = pngr.pixel_width();
00409   unsigned int lheight = pngr.pixel_height();
00410   surf::Image * __simage = new surf::Image( lwidth, lheight );
00411   for (unsigned int h = 0; h < lheight; ++h) {
00412     for (unsigned int w = 0; w < lwidth ; ++w) {
00413       __simage->setPix(w, h, (double)buf[h * lwidth + w] / 255.f);
00414     }
00415   }
00416   // make integral image
00417   __obj_img = new surf::Image(__simage, __doubleImageSize);
00418
00419   // NOT WORKING
00420   //__obj_img = new surf::Image( pngr.pixel_width(), pngr.pixel_height());
00421   //__obj_img->setFrame( buf );
00422
00423   if ( ! __obj_img ) {
00424     throw fawkes::Exception("Could not load object file '%s'", object_file.c_str());
00425   }
00426
00427   //#ifdef SURF_TIMETRACKER
00428   __tt->ping_end(__ttc_objconv);
00429   //#endif
00430
00431   // save object image for debugging
00432   ///surf::ImLoad::saveImage( "obj.pgm", __obj_img);
00433 
00434   //#ifdef SURF_TIMETRACKER
00435   __tt->ping_start(__ttc_objfeat);
00436   //#endif
00437
00438   // COMPUTE OBJECT FEATURES
00439
00440   std::vector<surf::Ipoint> obj_feature;
00441   __obj_features.push_back( obj_feature );
00442   __obj_features[num_obj_index].clear();
00443   __obj_features[num_obj_index].reserve(1000);
00444   __obj_num_features = 0;
00445   // Extract interest points with Fast-Hessian
00446   surf::FastHessian fh(__obj_img, /* pointer to integral image */
00447                        __obj_features[num_obj_index],
00448                        __thres, /* blob response threshold */
00449                        __doubleImageSize, /* double image size flag */
00450                        __initLobe * 3 /* 3 times lobe size equals the mask size */,
00451                        __samplingStep, /* subsample the blob response map */
00452                        __octaves /* number of octaves to be analysed */);
00453   // Extract them and get their pointer
00454   fh.getInterestPoints();
00455   // Initialise the SURF descriptor
00456   surf::Surf des(__obj_img, /* pointer to integral image */
00457                  __doubleImageSize, /* double image size flag */
00458                  __upright, /* rotation invariance or upright */
00459                  __extended, /* use the extended descriptor */
00460                  __indexSize /* square size of the descriptor window (default 4x4)*/);
00461   // Get the length of the descriptor vector
00462   // resulting from the parameters
00463   __vlen = des.getVectLength();
00464
00465   //printf("vlen=%i\n", __vlen);
00466
00467   // Compute the orientation and the descriptor for every interest point
00468   for (unsigned n=0; n < __obj_features[num_obj_index].size(); n++){
00469     // set the current interest point
00470     des.setIpoint(&(__obj_features.at(num_obj_index).at(n)));
00471     // assign reproducible orientation
00472     des.assignOrientation();
00473     // make the SURF descriptor
00474     des.makeDescriptor();
00475
00476   }
00477
00478
00479   __obj_num_features = __obj_features[num_obj_index].size();
00480   if ( ! __obj_num_features > 0 ) {
00481     throw fawkes::Exception("Could not compute object features");
00482   }
00483   std::cout << "SurfClassifier(classify): computed '" << __obj_num_features << "' features from object" << std::endl;
00484
00485   char buffer[256];
00486   sprintf( buffer, "descriptors/%s-%d.surf", ent->d_name, num_obj_index );
00487   std::string des_file_name = buffer;
00488
00489   bool b_verbose = BVERBOSE;
00490   bool b_laplacian = true;
00491
00492   __obj_names.push_back( des_file_name );
00493
00494
00495   // save descriptor
00496   saveIpoints( des_file_name, __obj_features[num_obj_index], b_verbose, b_laplacian, __vlen );
00497
00498
00499   // CleanUp
00500   delete __simage;
00501
00502   //#ifdef SURF_TIMETRACKER
00503   __tt->ping_end(__ttc_objfeat);
00504   //#endif
00505
00506   num_obj_index++;
00507     }
00508
00509   __num_obj = num_obj_index;
00510
00511 }
00512
00513 
00514 /** Destructor. */
00515 SurfClassifier::~SurfClassifier()
00516 {
00517   //
00518 }
00519
00520
00521 std::list< ROI > *
00522 SurfClassifier::classify()
00523 {
00524
00525   //  std::cout<<"SurfClassifier: Entering classification:-"<< std::endl;
00526   //#ifdef SURF_TIMETRACKER
00527   __tt->ping_start(0);
00528   //#endif
00529
00530   // list of ROIs to return
00531
00532   std::list<ROI> rv[__num_obj];
00533   float match_ratios[__num_obj];
00534
00535
00536   //  std::list< ROI > *rv = new std::list< ROI >();
00537
00538   // for ROI calculation
00539   int x_min = _width;
00540   int y_min = _height;
00541   int x_max = 0;
00542   int y_max = 0;
00543
00544   //#ifdef SURF_TIMETRACKER
00545   __tt->ping_start(__ttc_imgconv);
00546   //#endif
00547   std::cout << "SurfClassifier(classify): copy imgdat to SURF Image" << std::endl;
00548
00549   /*
00550     // NOT WOKRING ALTERNATIVE
00551   double *tmpb = (double *)malloc(_width * _height * sizeof(double));
00552   for (unsigned int h = 0; h < _height; ++h) {
00553     for (unsigned int w = 0; w < _width; ++w) {
00554       tmpb[h * _width + w] = (double)_src[h * _width + w] / 255;
00555     }
00556   }
00557   __simage->setFrame( (unsigned char*)tmpb );
00558   //surf::ImLoad::saveImage( "stst.pgm", __simage);
00559   __image = new surf::Image(__simage, __doubleImageSize);
00560   //__image = new surf::Image( _width, _height);
00561   //__image->setFrame( (unsigned char *)tmpb );
00562   */
00563
00564   surf::Image * __simage = new surf::Image( _width, _height);
00565   for (unsigned int h = 0; h < _height; ++h) {
00566     for (unsigned int w = 0; w < _width; ++w) {
00567       __simage->setPix(w, h, (double)_src[h * _width + w] / 255.f);
00568     }
00569   }
00570   // create integral image
00571   __image = new surf::Image(__simage, __doubleImageSize);
00572
00573   //#ifdef SURF_TIMETRACKER
00574   __tt->ping_end(__ttc_imgconv);
00575   //#endif
00576
00577
00578   /*
00579     /// write pnm (with surf-routine) for debugging
00580     //surf::ImLoad::saveImage( "tst.pgm", __simage);
00581     /// write integral pnm (with surf-routine) for debugging
00582     //surf::ImLoad::saveImage( "tst.pgm", __image);
00583     /// write pgm (with fv-routine) for debugging
00584     PNMWriter pnm(PNM_PGM, "fvimg.pgm", _width, _height);
00585     pnm.set_buffer(YUV422_PLANAR, _src );
00586     pnm.write();
00587     /// write png (with fv-routine) for debugging
00588     PNGWriter pngw("fvimg.png", _width, _height);
00589     pngw.set_buffer(YUV422_PLANAR, _src );
00590     pngw.write();
00591   */
00592
00593   //#ifdef SURF_TIMETRACKER
00594   __tt->ping_start(__ttc_imgfeat);
00595   //#endif
00596
00597   // COMPUTE OBJECT FEATURES
00598   __img_features.clear();
00599   __img_features.reserve(1000);
00600   __img_num_features = 0;
00601   // Extract interest points with Fast-Hessian
00602   surf::FastHessian fh(__image, /* pointer to integral image */
00603                        __img_features,
00604                        __thres, /* blob response threshold */
00605                        __doubleImageSize, /* double image size flag */
00606                        __initLobe * 3 /* 3 times lobe size equals the mask size */,
00607                        __samplingStep, /* subsample the blob response map */
00608                        __octaves /* number of octaves to be analysed */);
00609   // Extract them and get their pointer
00610   std::cout<<"surfclassifer/classify : getting interest points"<<std::endl;
00611   fh.getInterestPoints();
00612   // Initialise the SURF descriptor
00613   surf::Surf des(__image, /* pointer to integral image */
00614                  __doubleImageSize, /* double image size flag */
00615                  __upright, /* rotation invariance or upright */
00616                  __extended, /* use the extended descriptor */
00617                  __indexSize /* square size of the descriptor window (default 4x4)*/);
00618   // Get the length of the descriptor vector
00619   // resulting from the parameters
00620   // NOT NEEDED HERE!
00621   //__vlen = des.getVectLength();
00622   //printf("img vlen=%i\n", __vlen);
00623
00624   // Compute the orientation and the descriptor for every interest point
00625   for (unsigned n=0; n < __img_features.size(); n++){
00626     //for (Ipoint *k = ipts; k != NULL; k = k->next){
00627     // set the current interest point
00628     des.setIpoint(&__img_features[n]);
00629     // assign reproducible orientation
00630     des.assignOrientation();
00631     // make the SURF descriptor
00632     des.makeDescriptor();
00633   }
00634   __img_num_features = __img_features.size();
00635   //#ifdef SURF_TIMETRACKER
00636   __tt->ping_end(__ttc_imgfeat);
00637   //#endif
00638
00639   std::cout << "Extracted '" << __img_num_features << "' image features" << std::endl;
00640
00641
00642   //#ifdef SURF_TIMETRACKER
00643   __tt->ping_start(__ttc_matchin);
00644   //#endif
00645   std::cout << "SurfClassifier(classify): matching ..." << std::endl;
00646
00647   for( unsigned j = 0; j < __num_obj; j++ )
00648     {
00649       std::vector< int > matches(__obj_features[j].size());
00650       //      std::cout<< "SurfClassifier; _debug_ : " << __obj_features[j].size()  << "and" << __img_features.size() << std::endl;
00651       int c = 0;
00652       for (unsigned i = 0; i < __obj_features[j].size(); i++) {
00653         int match = findMatch((__obj_features[j])[i], __img_features);
00654         matches[i] = match;
00655         if (match != -1) {
00656           // std::cout << " Matched feature " << i << " in object image with feature " << match << " in image." << std::endl;
00657           /// adding feature-ROI
00658           ROI r( (int)(__img_features[matches[i]].x)-5, (int)(__img_features[matches[i]].y )-5, 11, 11, _width, _height);
00659           r.num_hint_points = 0;
00660           rv[j].push_back(r);
00661           /// increment feature-match-count
00662           ++c;
00663         }
00664       }
00665       //#ifdef SURF_TIMETRACKER
00666   __tt->ping_end(__ttc_matchin);
00667   //#endif
00668   if( c == 0 )
00669   std::cout << "SurfClassifier(classify) matched '" << c << fawkes::cnormal <<"' of '" << __obj_features[j].size() << "' features in scene. (for supplied object = " << j << std::endl ;
00670   else
00671   std::cout << "SurfClassifier(classify) matched '" << fawkes::cblue << c << fawkes::cnormal <<"' of '" << __obj_features[j].size() << "' features in scene. (for supplied object = " << j << std::endl ;
00672
00673
00674   float match_ratio = ((float)c / (float)__obj_features[j].size());
00675   match_ratios[j] = match_ratio;
00676
00677   std::cout << "SurfClassifier(classify): match_ratio is '" << match_ratio << "' and min_match_ratio is" << __min_match_ratio << std::endl;
00678
00679   std::cout << "SurfClassifier(classify): computing ROI" << std::endl;
00680   //#ifdef SURF_TIMETRACKER
00681   __tt->ping_start(__ttc_roimerg);
00682   //#endif
00683   for (unsigned i = 0; i < matches.size(); i++) {
00684     if (matches[i] != -1) {
00685       // //(int)__obj_features[i].x, (int)__obj_features[i].y
00686       //(int)__img_features[matches[i]].x, (int)(__img_features[matches[i]].y );
00687       if( (int)__img_features[matches[i]].x < x_min )
00688         x_min = (int)__img_features[matches[i]].x;
00689       if( (int)__img_features[matches[i]].y < y_min )
00690         y_min = (int)__img_features[matches[i]].y;
00691       if( (int)__img_features[matches[i]].x > x_max )
00692         x_max = (int)__img_features[matches[i]].x;
00693       if( (int)__img_features[matches[i]].y > y_max )
00694         y_max = (int)__img_features[matches[i]].y;
00695     }
00696   }
00697   if( (c != 0) && ((unsigned)c > __min_match) &&
00698       (match_ratio > __min_match_ratio) &&
00699       (x_max - x_min != 0 ) && (y_max - y_min != 0) ) {
00700
00701     std::cout << "SurfClassifier(classify): c='" << c << "' __min_match='" << __min_match << "'." << std::endl;
00702
00703     ROI r(x_min, y_min, x_max-x_min, y_max-y_min, _width, _height);
00704     r.num_hint_points = c;
00705     rv[j].push_back(r);
00706    } else {
00707     std::cout << " clearing ROI-list (no or too few matches or [0,0]-roi!)" << std::endl;
00708     rv[j].clear();
00709   }
00710     }
00711   //#ifdef SURF_TIMETRACKER
00712   __tt->ping_end(__ttc_roimerg);
00713   //#endif
00714
00715   // CleanUp
00716   delete __image;
00717   delete __simage;
00718
00719   //#ifdef SURF_TIMETRACKER
00720   __tt->ping_end(0);
00721   //#endif
00722
00723   //#ifdef SURF_TIMETRACKER
00724   // print timetracker statistics
00725   //__tt->print_to_stdout();
00726   //#endif
00727
00728
00729   // histogram comparison of all rois and features detected
00730   float min_ratio_tmp = -1.0;
00731   int min_ratio_index = -1;
00732   for( unsigned int i = 0; i < __num_obj; i++ )
00733     {
00734       if( match_ratios[i] > min_ratio_tmp )
00735         {
00736           min_ratio_tmp = match_ratios[i];
00737           min_ratio_index = i;
00738         }
00739     }
00740
00741   std::list<ROI> *final_rv = new std::list<ROI>;
00742
00743   final_rv->assign( rv[min_ratio_index].begin(), rv[min_ratio_index].end() );
00744
00745
00746   std::string first_not(".-");
00747   int first_not_index = __obj_names[ min_ratio_index ].find_first_of( first_not );
00748   std::string obj_name_tmp( __obj_names[ min_ratio_index ] );
00749   obj_name_tmp.erase( first_not_index );
00750
00751
00752   std::cout << "SurfClassifier(classify): done,  ... returning '" << rv->size() << "' ROIs. The object class is " << min_ratio_index << "and object name is " << fawkes::cgreen << obj_name_tmp << fawkes::cnormal << std::endl;
00753   return final_rv;
00754 }
00755
00756 int
00757 SurfClassifier::findMatch(const surf::Ipoint& ip1, const std::vector< surf::Ipoint >& ipts) {
00758   double mind = 1e100, second = 1e100;
00759   int match = -1;
00760
00761   //  std::cout<< "SurfClassifier/findMatch: " << ipts.size() <<" " << __vlen << std::endl;
00762
00763   for (unsigned i = 0; i < ipts.size(); i++) {
00764     // Take advantage of Laplacian to speed up matching
00765     if (ipts[i].laplace != ip1.laplace)
00766       continue;
00767
00768     double d = distSquare(ipts[i].ivec, ip1.ivec, __vlen);
00769
00770     if (d < mind) {
00771       second = mind;
00772       mind = d;
00773       match = i;
00774     } else if (d < second) {
00775       second = d;
00776     }
00777   }
00778
00779   if (mind < 0.5 * second)
00780     return match;
00781
00782   return -1;
00783 }
00784
00785
00786 double
00787 SurfClassifier::distSquare(double *v1, double *v2, int n) {
00788   double dsq = 0.;
00789   //  std::cout<< fawkes::cblue << (*v1) << fawkes::cred << (*v2);
00790
00791   while (n--) {
00792
00793     dsq += (*v1 - *v2) * (*v1 - *v2);
00794     v1++;
00795     v2++;
00796   }
00797
00798   //  std::cout << fawkes::cgreen << " "<<dsq << std::endl;
00799
00800   return dsq;
00801 }