contrib/rpl/rgrl/rgrl_matcher_pseudo.txx
Go to the documentation of this file.
00001 #ifndef rgrl_matcher_pseudo_txx_
00002 #define rgrl_matcher_pseudo_txx_
00003 
00004 #include "rgrl_matcher_pseudo.h"
00005 #include <rgrl/rgrl_feature_face_region.h>
00006 #include <rgrl/rgrl_feature_trace_region.h>
00007 #include <rgrl/rgrl_feature_point_region.h>
00008 #include <rgrl/rgrl_match_set.h>
00009 #include <rgrl/rgrl_cast.h>
00010 #include <rgrl/rgrl_macros.h>
00011 #include <vnl/vnl_matrix.h>
00012 #include <vnl/vnl_math.h>
00013 #include <vnl/vnl_inverse.h>
00014 #include <vil/vil_bilin_interp.h>
00015 #include <vcl_cassert.h>
00016 
00017 static const unsigned int verbose_ = 2;
00018 static const double rgrl_matcher_pseudo_max_response_value = 1.0e30;
00019 
00020 
00021 template <class PixelType>
00022 inline bool
00023 in_range( vil_image_view< PixelType > const& image,
00024           rgrl_mask_sptr const& mask,
00025           vnl_double_2 const& location )
00026 {
00027   if ( mask && !mask->inside( location.as_ref() ) )
00028       return false;
00029 
00030   if ( location[ 0 ] < 0 || location[ 0 ] > image.ni()-1 ||
00031        location[ 1 ] < 0 || location[ 1 ] > image.nj()-1 )
00032     return false;
00033 
00034   return true;
00035 }
00036 
00037 template <class PixelType>
00038 inline bool
00039 in_range( vil_image_view< PixelType > const& image, // FIXME: unused
00040           rgrl_mask_sptr const& mask,
00041           vnl_vector< double > const& location )
00042 {
00043   if ( mask && !mask->inside( location ) )
00044       return false;
00045 
00046   return true;
00047 }
00048 
00049 template <class PixelType>
00050 rgrl_matcher_pseudo< PixelType > ::
00051 rgrl_matcher_pseudo( vil_image_view<PixelType> from_image,
00052                      vil_image_view<PixelType> to_image,
00053                      rgrl_evaluator_sptr      evaluator,
00054                      rgrl_mask_sptr from_mask,
00055                      rgrl_mask_sptr to_mask )
00056   : from_image_( from_image ),
00057     to_image_( to_image ),
00058     from_mask_( from_mask ),
00059     to_mask_ ( to_mask ),
00060     evaluator_( evaluator )
00061 {}
00062 
00063 
00064 template <class PixelType>
00065 rgrl_match_set_sptr
00066 rgrl_matcher_pseudo< PixelType > ::
00067 compute_matches( rgrl_feature_set const&    from_set,
00068                  rgrl_feature_set const&    to_set,
00069                  rgrl_view const&           current_view,
00070                  rgrl_transformation const& current_xform,
00071                  rgrl_scale const&          current_scale,
00072                  rgrl_match_set_sptr const& /*old_matches*/ )
00073 {
00074   typedef vcl_vector<rgrl_feature_sptr> f_vector_type;
00075   typedef f_vector_type::iterator f_iterator_type;
00076 
00077   //  Build an empty match set
00078   rgrl_match_set_sptr matches_sptr
00079     = new rgrl_match_set( from_set.type(), to_set.type(), from_set.label(), to_set.label() );
00080 
00081   //  Get the from image features in the current view
00082   f_vector_type from;
00083   from_set.features_in_region( from, current_view.region() );
00084   DebugMacro (1,  " compute_matches : from.size() = " << from.size() << '\n' );
00085   DebugMacro_abv(1,"geometric scale = " << current_scale.geometric_scale()<< '\n' );
00086 
00087   //  Vector for mapped pixels
00088   rgrl_mapped_pixel_vector_type  mapped_pixels;
00089   mapped_pixels.reserve( from.size() );
00090 
00091   //  Vectors for matched features and weights.
00092   f_vector_type matched_to_features;
00093   vcl_vector<double> match_weights;
00094 
00095   // Match each feature...
00096   for ( f_iterator_type fitr = from.begin(); fitr != from.end(); ++fitr )
00097   {
00098     // Match by searching in the tangent space of the
00099     // transformed from image feature.  The match_weights are to be
00100     // treated later as similarity weights
00101     matched_to_features.clear();
00102     match_weights.clear();
00103 
00104     // for debug purpose
00105     // double x = (*fitr)->location()[0];
00106     // double y = (*fitr)->location()[1];
00107 
00108     // Map the feature location using the current transformation
00109     rgrl_feature_sptr mapped_feature = (*fitr)->transform( current_xform );
00110 
00111     { // Begin debugging
00112       if ( (*fitr)->is_type( rgrl_feature_trace_region::type_id() ) )
00113         DebugMacro_abv(1, "\n\nfrom :\n" << (*fitr)->location() << " normal: "
00114                        << rgrl_cast<rgrl_feature_trace_region *> ( *fitr )->normal_subspace().get_column(0)
00115                        << "\nmapped :\n" << mapped_feature->location()
00116                        << rgrl_cast<rgrl_feature_trace_region *> ( mapped_feature )->normal_subspace().get_column(0) <<'\n' );
00117       else if ( (*fitr)->is_type( rgrl_feature_face_region::type_id() ) )
00118         DebugMacro_abv(1, "\n\nfrom :\n" << (*fitr)->location() << " normal: "
00119                        << rgrl_cast<rgrl_feature_face_region *> ( *fitr )->normal()
00120                        << "\nmapped :\n" << mapped_feature->location() << " normal: "
00121                        << rgrl_cast<rgrl_feature_face_region *> ( mapped_feature )->normal()<<'\n');
00122       else if ( (*fitr)->is_type( rgrl_feature_point_region::type_id() ) )
00123         DebugMacro_abv(1, "\n\nfrom :\n" << (*fitr)->location()
00124                        << "\nmapped :\n" << mapped_feature->location()<<'\n');
00125     } // End debugging
00126 
00127     // Check if the mapped feature is inside the valid region.
00128     // If the location is not inside the valid region
00129     // set the weight = 0
00130     if ( !in_range( to_image_, to_mask_, mapped_feature->location() ) ) {
00131       //  Make a dummy vector of intensity weights.
00132       // vcl_vector< double > dummy_intensity_weights( 0 ) //CT: not needed now;
00133       vcl_vector< double > match_weights( 0 );
00134 
00135       //  Add the feature and its matches and weights to the match set
00136       matches_sptr
00137         -> add_feature_matches_and_weights( *fitr, mapped_feature, matched_to_features,
00138                                             match_weights );
00139 
00140      DebugMacro_abv(1, " skip match from: " << (*fitr)->location()
00141                     << ", to: " << mapped_feature->location() << '\n' );
00142 
00143       continue;
00144     }
00145 
00146     // Map the intensities of the pixels in the from image
00147     // surrounding the from image feature.  Form a vector of pairs,
00148     // with each pair containing a mapped location and the
00149     // associated intensity.
00150     mapped_pixels.clear();
00151 
00152     this -> map_region_intensities( current_xform, (*fitr), mapped_pixels );
00153 
00154     // if there is no mapped pixels in the valid region, no matcher is created
00155     if ( mapped_pixels.size() == 0 ) {
00156       //  Make a dummy vector of intensity weights.
00157       // vcl_vector< double > dummy_intensity_weights( 0 ); //CT: not needed now
00158       vcl_vector< double > match_weights( 0 );
00159 
00160       //  Add the feature and its matches and weights to the match set
00161       matches_sptr
00162         -> add_feature_matches_and_weights( *fitr, mapped_feature, matched_to_features,
00163                                             match_weights );
00164       DebugMacro(3, " from point : " << (*fitr)->location()
00165                  << " to point : " << mapped_feature->location() << " doesn't have proper matches\n");
00166       continue;
00167     }
00168 
00169     this -> match_mapped_region( mapped_feature, mapped_pixels, current_scale,
00170                                  matched_to_features, match_weights );
00171 
00172     //  Make a dummy vector of intensity weights.
00173     // vcl_vector< double > dummy_intensity_weights( match_weights.size(), 1.0 );
00174 
00175     //  Add the feature and its matches and weights to the match set
00176     matches_sptr -> add_feature_matches_and_weights( *fitr, mapped_feature, matched_to_features,
00177                                                      match_weights );
00178   }
00179 
00180   DebugMacro(3, "matches set from size = " << matches_sptr->from_size() <<'\n');
00181 
00182   typedef rgrl_match_set::from_iterator       from_iter;
00183   typedef from_iter::to_iterator              to_iter;
00184   for ( from_iter fitr = matches_sptr->from_begin(); fitr !=  matches_sptr->from_end(); ++fitr ) {
00185     if ( fitr.size() == 0 )  continue;
00186 
00187     rgrl_feature_sptr mapped_from = fitr.mapped_from_feature();
00188     for ( to_iter titr = fitr.begin(); titr != fitr.end(); ++titr ) {
00189       rgrl_feature_sptr to = titr.to_feature();
00190       DebugMacro(3, mapped_from->location()[0]<<' '<<mapped_from->location()[1]
00191                  <<' '<< to->location()[0]<<' '<<to->location()[1]<<'\n');
00192     }
00193   }
00194 
00195   return matches_sptr;
00196 }
00197 
00198 
00199 template <class PixelType>
00200 void
00201 rgrl_matcher_pseudo<PixelType> ::
00202 map_region_intensities( rgrl_transformation      const& trans,
00203                         rgrl_feature_sptr               feature_sptr,
00204                         rgrl_mapped_pixel_vector_type & mapped_pixels) const
00205 {
00206   static vnl_vector<double> spacing_ratio( 2, 1.0 );
00207 
00208   if ( feature_sptr -> is_type( rgrl_feature_face_region::type_id() ) )
00209   {
00210     rgrl_feature_face_region * face_ptr =
00211         rgrl_cast<rgrl_feature_face_region *> ( feature_sptr );
00212     this -> map_region_intensities( face_ptr -> pixel_coordinates_ratio( spacing_ratio ), trans,
00213                                     feature_sptr, mapped_pixels );
00214   }
00215   else if ( feature_sptr -> is_type( rgrl_feature_trace_region::type_id() ))
00216   {
00217     rgrl_feature_trace_region * trace_ptr =
00218       rgrl_cast<rgrl_feature_trace_region *> ( feature_sptr );
00219     this -> map_region_intensities( trace_ptr -> pixel_coordinates_ratio( spacing_ratio ), trans,
00220                                     feature_sptr, mapped_pixels );
00221   }
00222   else // Assuming feature_point_region
00223   {
00224     rgrl_feature_point_region * point_ptr =
00225       rgrl_cast<rgrl_feature_point_region *> ( feature_sptr );
00226     this -> map_region_intensities( point_ptr -> pixel_coordinates_ratio( spacing_ratio ), trans,
00227                                     feature_sptr, mapped_pixels );
00228   }
00229 }
00230 
00231 
00232 template <class PixelType>
00233 void
00234 rgrl_matcher_pseudo<PixelType> ::
00235 map_region_intensities( vcl_vector< vnl_vector<int> > const& pixel_locations,
00236                         rgrl_transformation           const& trans,
00237                         rgrl_feature_sptr                    feature_sptr,
00238                         rgrl_mapped_pixel_vector_type   & mapped_pixels) const
00239 {
00240   DebugMacro(3,"   number of points around the from point: " << pixel_locations.size() <<'\n');
00241   // check # of pixels
00242   if ( pixel_locations.empty() )  return;
00243 
00244   unsigned dim = feature_sptr -> location() . size();
00245   assert ( dim == 2 ); // so far vil force it to be 2D
00246   vnl_double_2 pixel_loc_dbl;
00247   rgrl_mapped_pixel_type  mapped_pixel;
00248   mapped_pixel . weight = 1.0;
00249 
00250   // reserve space
00251   mapped_pixels.reserve( pixel_locations.size() );
00252 
00253   double intensity;
00254   for ( unsigned int i=0; i<pixel_locations.size(); ++i )
00255   {
00256     //  Copy the int pixel locations to doubles.  Yuck.
00257     for ( unsigned int j=0; j<dim; ++j )  pixel_loc_dbl[j] = pixel_locations[i][j];
00258     // Check if the location is inside the valid region
00259     if ( !in_range( from_image_, from_mask_, pixel_loc_dbl ) )
00260       continue;
00261 
00262     trans.map_location( pixel_loc_dbl.as_ref(), mapped_pixel.location.as_ref().non_const() );
00263     // Check if the location is inside the valid region
00264     if ( !in_range( to_image_, to_mask_, mapped_pixel.location ) )
00265       continue;
00266 
00267     //  Extract the intensity.  This is where we need ITK.
00268     // only work for one plane so far
00269     assert ( from_image_.nplanes() == 1 );
00270 
00271     intensity = from_image_( pixel_locations[i][0], pixel_locations[i][1] );
00272     //PixelType intensity; //  =  SOMETHING from ITK
00273     mapped_pixel . intensity = intensity; // trans . map_intensity( pixel_loc_dbl, intensity );
00274     mapped_pixels . push_back( mapped_pixel );
00275   }
00276 }
00277 
00278 #if 0
00279 inline double
00280 est_sub_offset( vnl_matrix< double > const& A, vnl_matrix< double > const& S )
00281 {
00282   // A S = X, where X = [ a b c ]
00283   vnl_matrix< double > inv = vnl_inverse(A);
00284   vnl_matrix< double > X = inv * S;
00285   assert( X.columns() == 1 );
00286 
00287   // if it fit a line, instead of a parabola
00288   // then return the original best index
00289   if ( X[ 0 ][ 0 ] == 0 )
00290     return 0.0;
00291 
00292   // find r that minimizes s
00293   // ds = 2ar + b = 0
00294   // r = -b / 2a
00295   return - X[ 1 ][ 0 ] / ( 2 * X[ 0 ][ 0 ] ) ;
00296 }
00297 #endif // 0
00298 
00299 #if 0
00300 inline vnl_vector< double >
00301 sub_pixel_2d( vcl_vector< vcl_vector< double > > const& responses,
00302               int idx1, int idx2 )
00303 {
00304   assert( responses.size() >= 3 );
00305   assert( responses[ 0 ].size() >= 3 );
00306   for ( unsigned i = 1; i < responses.size(); ++i ) {
00307     assert( responses[ i ].size() == responses[ 0 ].size() );
00308   }
00309 
00310   if ( idx1 == 0 ) idx1 = 1;
00311   if ( idx2 == 0 ) idx2 = 1;
00312 
00313   if ( idx1 == (int)responses.size() - 1 )
00314     idx1 = responses.size() - 2;
00315   if ( idx2 == (int)responses.size() - 1 )
00316     idx2 = responses.size() - 2;
00317 
00318   // In 2D, I treat it as fitting a parabola in each direction ( d1
00319   // and d2 ), since we use the curvature along both direction to
00320   // approximate the principal curvature anyway.
00321 
00322   // let s be the similarity error, s = a r^2 + b r + c.
00323   // Use points index-1, index, index+1 to model the
00324   // parameters X = [a, b, c].
00325   vnl_matrix < double > A ( 3, 3 );
00326   vnl_matrix< double > S1 ( 3, 1 ) ;
00327   vnl_matrix< double > S2 ( 3, 1 ) ;
00328 
00329   for ( int r = -1; r <= 1; ++r ) {
00330     A( r+1, 0 ) = r * r;
00331     A( r+1, 1 ) = r;
00332     A( r+1, 2 ) = 1;
00333     S1( r+1, 0 ) = responses[ idx1 + r][ idx2 ];
00334     S2( r+1, 0 ) = responses[ idx1 ][ idx2 + r ];
00335   }
00336 
00337   vnl_vector< double > best_index( 2 );
00338   best_index[ 0 ] = est_sub_offset( A, S1 ) + idx1;
00339   best_index[ 1 ] = est_sub_offset( A, S2 ) + idx2;
00340 
00341   return best_index;
00342 }
00343 #endif // 0
00344 
00345 inline double
00346 rgrl_matcher_pseudo_sub_pixel( vcl_vector< double > const& responses )
00347 {
00348   assert( responses.size() == 3 );
00349 
00350   // let s be the similarity error, s = a r^2 + b r + c.
00351   // Use points index-1, index, index+1 to model the
00352   // parameters X = [a, b, c].
00353   vnl_matrix < double > A ( 3, 3 );
00354   vnl_matrix< double > S ( 3, 1 ) ;
00355 
00356   for ( unsigned i = 0; i < 3; ++i ) {
00357     int r = i - 1;
00358     A( r+1, 0 ) = r * r;
00359     A( r+1, 1 ) = r;
00360     A( r+1, 2 ) = 1;
00361     S( r+1, 0 ) = responses[ i ];
00362   }
00363 
00364   vnl_matrix< double > inv = vnl_inverse(A);
00365   vnl_matrix< double > X = inv * S;
00366   assert( X.columns() == 1 );
00367 
00368   // if it fit a line, instead of a parabola
00369   // then return the original best index
00370   if ( X[ 0 ][ 0 ] <= 1.0e-5 ) {
00371     return 0;
00372   }
00373 
00374   // find r that minimizes s
00375   // ds = 2ar + b = 0
00376   // r = -b / 2a
00377   double best_index =  -X[ 1 ][ 0 ] / ( 2 * X[ 0 ][ 0 ] );
00378 
00379   assert( best_index <= 1 && best_index >= -1 );
00380 
00381   return best_index;
00382 }
00383 
00384 template <class PixelType>
00385 void
00386 rgrl_matcher_pseudo<PixelType> ::
00387 match_mapped_region( rgrl_feature_sptr                     mapped_feature,
00388                      rgrl_mapped_pixel_vector_type const & mapped_pixels,
00389                      rgrl_scale                    const & current_scale,
00390                      vcl_vector< rgrl_feature_sptr >     & matched_to_features,
00391                      vcl_vector< double >                & match_weights ) const
00392 {
00393   //  At this point, find the most similar feature within the given
00394   //  scale.
00395   unsigned int dim = mapped_feature -> location() . size();
00396 
00397   const double scale_multiplier = 4;   // magic number.  frown.
00398 
00399   vnl_matrix< double > normal_space;
00400 
00401   if ( mapped_feature -> is_type( rgrl_feature_face_region::type_id() ) )
00402   {
00403     rgrl_feature_face_region * face_ptr =
00404     rgrl_cast<rgrl_feature_face_region *> ( mapped_feature );
00405     normal_space = vnl_matrix< double > ( dim, 1 );
00406     normal_space . set_column ( 0, face_ptr -> normal() );
00407   }
00408   else if ( mapped_feature->is_type( rgrl_feature_trace_region::type_id()) ) // RGRL_FEATURE_TRACE_REGION
00409   {
00410     rgrl_feature_trace_region * trace_ptr =
00411     rgrl_cast<rgrl_feature_trace_region *> ( mapped_feature );
00412     normal_space = trace_ptr -> normal_subspace();
00413   }
00414   else { // RGRL_FEATURE_POINT_REGION
00415     normal_space = vnl_matrix<double>(2,2,vnl_matrix_identity);
00416   }
00417 
00418   vnl_double_2 match_location;
00419   double min_response = 0.0;
00420   double second_derivative = 0.0;
00421   int max_offset = vnl_math_rnd( scale_multiplier * current_scale . geometric_scale() );
00422   if ( max_offset == 0 ) max_offset = 1;
00423 
00424   //  DO THE REST DEPENDING ON IF THE NORMAL SUBSPACE IS 1D or 2D.
00425   //  IN THE FUTURE, IF WE WANT TO JUMP TO N-D, THIS WILL NEED TO BE
00426   //  CHANGED, PERHAPS JUST BY ADDING EACH DIMENSION WE WANT.
00427 
00428   // 3D case should use rgrl_matcher_pseudo_3d
00429   //assert( normal_space.columns() == 1 );
00430   if ( normal_space.columns() == 1 ) {
00431     vnl_double_2 basis = normal_space.get_column(0);
00432 
00433     DebugMacro_abv(2, "normal basis :\n" << basis << '\n');
00434 
00435     vcl_vector<double> responses( 2*max_offset+1, 0.0 );
00436     bool is_best_initialized = false;
00437     int best_offset = 0;
00438 
00439     //  Find the offset along the basis direction giving the best
00440     //  response.
00441 
00442     vnl_double_2 mapped_location = mapped_feature -> location();
00443 
00444     // Don't favor the max_offset_range. sometimes the region is
00445     // homogeneous, the responses might have same value
00446     for ( int abs_offset = 0; abs_offset <= max_offset; ++abs_offset )
00447     {
00448       int offset = abs_offset;
00449       do {
00450         int i = offset + max_offset;
00451         responses[i] = this -> compute_response( mapped_location, mapped_pixels, basis * double(offset) );
00452 
00453         DebugMacro_abv(2," response at offset " << offset
00454                        << " ( i = " << i << " ) : " << responses[ i ] <<'\n' );
00455 
00456         // We don't want to use the responses of the offsets that shift
00457         // the box across the boundary.
00458         if ( (!is_best_initialized || responses[i] < min_response ) &&
00459              responses[ i ] != rgrl_matcher_pseudo_max_response_value )
00460           {
00461             is_best_initialized = true;
00462             min_response = responses[i];
00463             best_offset = offset;
00464           }
00465         offset = -offset;
00466       } while ( offset < 0 );
00467     }
00468 
00469     DebugMacro_abv(2," the best offset = " << best_offset << '\n' );
00470 
00471     assert( is_best_initialized );
00472 
00473     //  Evaluate the second derivative at the peak.  If the
00474     //  peak occurrence is on the boundary, compute the second
00475     //  derivative based on one step in from the boundary.
00476     int deriv_loc = best_offset;
00477     if ( deriv_loc == -max_offset ) ++ deriv_loc;
00478     else if ( deriv_loc == max_offset ) -- deriv_loc;
00479     int index = deriv_loc + max_offset;
00480 
00481     // The best_offset so far is constrained on the discrete space.
00482     // Now we use a parabola to model the similarity error
00483     // (responses) and find the position of the minimum response.
00484 
00485     // If the best offset is at the (+/-)max_offset, no need to
00486     // calculate the sub_offset.
00487     double sub_offset = 0;
00488     if ( best_offset != max_offset &&
00489          best_offset != -max_offset )
00490       {
00491         // If one neighbor's response is not valid, calculate the second
00492         // derivative value of the other neighbor and sub_pixel is not necessary.
00493         if ( responses[ index - 1 ] == rgrl_matcher_pseudo_max_response_value )
00494           index ++;
00495         else if ( responses[ index + 1 ] == rgrl_matcher_pseudo_max_response_value )
00496           index--;
00497         else
00498         {
00499           vcl_vector< double > responses_for_sub_pixel( 3 );
00500           responses_for_sub_pixel[ 0 ] = responses[ index - 1 ];
00501           responses_for_sub_pixel[ 1 ] = responses[ index ];
00502           responses_for_sub_pixel[ 2 ] = responses[ index + 1 ];
00503           sub_offset = rgrl_matcher_pseudo_sub_pixel( responses_for_sub_pixel );
00504           assert( sub_offset + best_offset >= -max_offset );
00505           assert( sub_offset + best_offset <= max_offset );
00506 //        if ( sub_offset + best_offset < -max_offset ) best_offset = -max_offset;
00507 //        if ( sub_offset + best_offset > max_offset ) best_offset = max_offset;
00508         }
00509       }
00510 
00511     match_location = mapped_location + basis * ( best_offset + sub_offset );
00512 
00513     DebugMacro_abv(2,"best match :\n" << match_location << '\n' );
00514 
00515     // Here I use the second derivative at index to approximate the
00516     // second derivative at sub_pixel.
00517     // We need at least three valid values for calculating second derivatives.
00518     //
00519     if ( index >0 && index+1 < (int)responses.size() &&
00520          responses[ index ] != rgrl_matcher_pseudo_max_response_value &&
00521          index + 1 <= 2*max_offset &&
00522          index - 1 >= -2*max_offset &&
00523          responses[ index + 1 ] != rgrl_matcher_pseudo_max_response_value &&
00524          responses[ index - 1 ] != rgrl_matcher_pseudo_max_response_value )
00525          second_derivative = vnl_math_abs( responses[ index-1 ] + responses[ index+1 ]
00526                                            - 2 * responses[ index ] ); // should be positive
00527     // If one neighbor's response is not valid, calculate the second
00528     // derivative value of the other neighbor
00529     else {
00530       second_derivative = 0;
00531       DebugMacro_abv(2, "index=" << index << ", max_offset="
00532                      << max_offset << ", responses[index-1]=" << responses[index-1]
00533                      << ", responses[index+1]=" << responses[index+1] << '\n'
00534                      << "   neighbors' responses are not valid. Set the second_derivative = 0\n");
00535     }
00536   }
00537   else if ( normal_space.columns() == 2 ) { //For feature_point_region
00538     vnl_vector<double> basis1 = normal_space . get_column(0);
00539     vnl_vector<double> basis2 = normal_space . get_column(1);
00540 
00541     vcl_vector<double> temp( 2*max_offset+1, 0.0 );
00542     vcl_vector< vcl_vector<double> > responses( 2*max_offset+1, temp );
00543 
00544     bool is_best_initialized = false;
00545     int best_off1 = 0, best_off2 = 0;
00546 
00547     //  Find the offset along the basis direction giving the best
00548     //  response.
00549 
00550     vnl_vector<double> mapped_location = mapped_feature -> location();
00551     for ( int off1 = -max_offset, i=0; off1 <= max_offset; ++off1, ++i )
00552       for ( int off2 = -max_offset, j=0; off2 <= max_offset; ++off2, ++j )
00553       {
00554         responses[i][j] = this -> compute_response( mapped_location, mapped_pixels,
00555                                                     basis1 * off1 + basis2 * off2 );
00556 
00557         if ( ( !is_best_initialized || responses[i][j] < min_response )
00558              && responses[i][j] != rgrl_matcher_pseudo_max_response_value )
00559           {
00560             is_best_initialized = true;
00561             min_response = responses[i][j];
00562             best_off1 = off1;
00563             best_off2 = off2;
00564           }
00565       }
00566 
00567     assert( is_best_initialized );
00568 
00569     //  Evaluate the second derivative at the peak.  If the
00570     //  peak occurrence is on the boundary, compute the second
00571     //  derivative based on one step in from the boundary.
00572 
00573     int deriv_loc1 = best_off1;
00574     if ( deriv_loc1 == -max_offset ) ++deriv_loc1;
00575     else if ( deriv_loc1 == max_offset ) --deriv_loc1;
00576     int idx1 = deriv_loc1 + max_offset;   // indices into the array of responses
00577     int idx2 = best_off2 + max_offset;
00578 
00579     // The best_offset so far is constrained on the discrete space.
00580     // Now we use a parabola to model the similarity error
00581     // (responses) and find the position of the minimum response.
00582     // Here I calculate sub_pixel in each dimension separately just for
00583     // the convenience. Since it's only an approximation in one grid,
00584     // I assume this approximation is good enough.
00585     double sub_offset1;
00586 
00587     if ( best_off1 == max_offset || best_off1 == -max_offset )
00588       sub_offset1 = best_off1;
00589     else if ( responses[ idx1 - 1 ][ idx2 ] == rgrl_matcher_pseudo_max_response_value ||
00590               responses[ idx1 + 1 ][ idx2 ] == rgrl_matcher_pseudo_max_response_value )
00591     {
00592       sub_offset1 = idx1 - max_offset;
00593     }
00594     else
00595     {
00596       vcl_vector< double > responses_for_sub_pixel( 3 );
00597       responses_for_sub_pixel[ 0 ] = responses[ idx1 - 1 ][ idx2 ];
00598       responses_for_sub_pixel[ 1 ] = responses[ idx1 ][ idx2 ];
00599       responses_for_sub_pixel[ 2 ] = responses[ idx1 + 1 ][ idx2 ];
00600       sub_offset1 = rgrl_matcher_pseudo_sub_pixel( responses_for_sub_pixel ) + idx1 - max_offset;
00601       // the sub_pixel here is used only for interpolation
00602       // if it's outside
00603       if ( sub_offset1 < -max_offset ) sub_offset1 = -max_offset;
00604       if ( sub_offset1 > max_offset ) sub_offset1 = max_offset;
00605       DebugMacro_abv(2, " sub_offset1 = " << sub_offset1 << " in [ "
00606                      << -max_offset << " , " << max_offset << " ]\n");
00607     }
00608 
00609     double second_d1 = vnl_math_abs( responses[ idx1-1 ][ idx2 ] + responses[ idx1+1 ][ idx2 ]
00610                                      - 2 * responses[ idx1 ][ idx2 ] );
00611 
00612     int deriv_loc2 = best_off2;
00613     if ( deriv_loc2 == -max_offset ) ++deriv_loc2;
00614     else if ( deriv_loc2 == max_offset ) --deriv_loc2;
00615     idx2 = deriv_loc2 + max_offset;
00616     idx1 = best_off1 + max_offset;
00617     double sub_offset2;
00618     if ( best_off2 == max_offset || best_off2 == -max_offset )
00619       sub_offset2 = best_off2;
00620     else if ( responses[ idx1 ][ idx2 - 1 ] == rgrl_matcher_pseudo_max_response_value ||
00621               responses[ idx1 ][ idx2 + 1 ] == rgrl_matcher_pseudo_max_response_value )
00622     {
00623       sub_offset2 = idx2 - max_offset;
00624     }
00625     else
00626     {
00627       vcl_vector< double > responses_for_sub_pixel( 3 );
00628       responses_for_sub_pixel[ 0 ] = responses[ idx1 ][ idx2 - 1 ];
00629       responses_for_sub_pixel[ 1 ] = responses[ idx1 ][ idx2 ];
00630       responses_for_sub_pixel[ 2 ] = responses[ idx1 ][ idx2 + 1 ];
00631       sub_offset2 = rgrl_matcher_pseudo_sub_pixel( responses_for_sub_pixel ) + idx2 - max_offset;
00632       if ( sub_offset2 < -max_offset ) sub_offset2 = -max_offset;
00633       if ( sub_offset2 > max_offset ) sub_offset2 = max_offset;
00634 
00635       DebugMacro_abv(2, " sub_offset2 = " << sub_offset2 << " in [ "
00636                      << -max_offset << " , " << max_offset << " ]\n");
00637     }
00638 
00639     double second_d2 = vnl_math_abs( responses[ idx1 ][ idx2-1 ] + responses[ idx1 ][ idx2+1 ]
00640                                      - 2 * responses[ idx1 ][ idx2 ] );
00641 
00642     second_derivative = vnl_math_min( second_d1, second_d2 );
00643     match_location = mapped_location + basis1 * sub_offset1 + basis2 * sub_offset2;
00644     DebugMacro_abv(1, "best match :\n" << match_location << '\n' );
00645   }
00646   else {
00647     vcl_cerr << "Code doesn't handle a normal subspace of greater than two dimenions.\n";
00648     assert( false );
00649   }
00650 
00651   matched_to_features . clear();
00652   match_weights . clear();
00653   rgrl_feature_sptr mf_ptr;
00654   if ( mapped_feature -> is_type( rgrl_feature_face_region::type_id() ) )
00655   {
00656     rgrl_feature_face_region * face_ptr =
00657       rgrl_cast<rgrl_feature_face_region *> ( mapped_feature );
00658     mf_ptr = new rgrl_feature_face_region( match_location.as_ref(), face_ptr -> normal() );
00659   }
00660   else if (  mapped_feature -> is_type( rgrl_feature_trace_region::type_id() ) )
00661   {
00662     rgrl_feature_trace_region * trace_ptr =
00663       rgrl_cast<rgrl_feature_trace_region *> ( mapped_feature );
00664     mf_ptr = new rgrl_feature_trace_region( match_location.as_ref(), trace_ptr -> tangent() );
00665   }
00666   else { //rgrl_feature_point_region
00667     mf_ptr = new rgrl_feature_point_region( match_location.as_ref() );
00668   }
00669 
00670   matched_to_features.push_back( mf_ptr );
00671   double weight = second_derivative / (1.0 + min_response);
00672 #if 0 // other options for weights
00673   double epsilon = 1e-16;
00674   double weight = min_response/(second_derivative + epsilon);
00675   double weight = 1;
00676 #endif // 0
00677 
00678   match_weights.push_back( weight );
00679   DebugMacro(3, "Signature error for best match :\n" << weight
00680              << ", second_derivative = "<< second_derivative
00681              << " min_response = "<<min_response
00682              << " old wgt = "<<second_derivative / (1.0 + min_response)<<'\n');
00683 }
00684 
00685 template <class PixelType>
00686 double
00687 rgrl_matcher_pseudo<PixelType> ::
00688 compute_response( vnl_double_2                  const& mapped_location, // FIXME: unused
00689                   rgrl_mapped_pixel_vector_type const& mapped_pixels,
00690                   vnl_double_2                  const& shift ) const
00691 {
00692   //  Extract the intensities at the mapped locations.  Make sure
00693   //  they are inside the image.
00694 
00695   vcl_vector< double > a;
00696   vcl_vector< double > b;
00697   vcl_vector< double > weights;
00698   double intensity;
00699 
00700   // reserve space
00701   a.reserve( mapped_pixels.size() );
00702   b.reserve( mapped_pixels.size() );
00703   weights.reserve( mapped_pixels.size() );
00704   for ( unsigned i = 0; i < mapped_pixels.size(); ++i ) {
00705     vnl_double_2 location = mapped_pixels[i].location + shift;
00706     //vcl_cout << " position : " << mapped_pixels[ i ].location << "  shift " << shift << vcl_endl;
00707     // Check if the location is inside the valid region
00708     if ( !in_range( to_image_, to_mask_, location ) )
00709       return rgrl_matcher_pseudo_max_response_value;
00710 
00711     intensity = vil_bilin_interp(to_image_,  location[0], location[1] );
00712     a.push_back( (double)(mapped_pixels[i].intensity) );
00713     b.push_back( intensity );
00714     weights.push_back( mapped_pixels[i].weight );
00715   }
00716 
00717   //  call the response function
00718   return evaluator_->evaluate( a, b, weights );
00719 }
00720 
00721 #endif // rgrl_matcher_pseudo_txx_