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