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