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