contrib/rpl/rgrl/rgrl_matcher_pseudo_int_3d.txx
Go to the documentation of this file.
00001 #ifndef rgrl_matcher_pseudo_int_3d_txx_
00002 #define rgrl_matcher_pseudo_int_3d_txx_
00003 
00004 #include "rgrl_matcher_pseudo_int_3d.h"
00005 #include "rgrl_feature_face_region.h"
00006 #include "rgrl_feature_trace_region.h"
00007 #include <rgrl/rgrl_cast.h>
00008 #include <rgrl/rgrl_match_set.h>
00009 #include <rgrl/rgrl_macros.h>
00010 #include <vnl/vnl_matrix.h>
00011 #include <vnl/vnl_math.h>
00012 #include <vnl/vnl_vector.h>
00013 #include <vnl/vnl_int_3.h>
00014 #include <vnl/vnl_inverse.h>
00015 #include <vil3d/vil3d_trilin_interp.h>
00016 #include <vbl/vbl_bounding_box.h>
00017 #include <vbl/vbl_array_2d.h>
00018 #include <vbl/vbl_array_3d.h>
00019 #include <vcl_cassert.h>
00020 
00021 static const double max_response_value = 1.0e30;
00022 
00023 //#define MY_DEBUG
00024 #if defined ( MY_DEBUG )
00025 #  include <vcl_iostream.h>
00026 // not used? #  include <vcl_fstream.h>
00027 // not used? #  include <vcl_sstream.h>
00028 #  define DBG(x) x
00029 #else
00030 #  define DBG(x)
00031 #endif
00032 
00033 // convert pixel points to physical points
00034 inline
00035 void
00036 pixel_to_physical( vnl_int_3    const& pixel_loc,
00037                    vnl_double_3      & point,
00038                    vnl_double_3 const& spacing_ratio )
00039 {
00040   for ( unsigned i = 0; i < 3; ++i )
00041     point[ i ] = spacing_ratio[ i ] * double(pixel_loc[ i ]);
00042 }
00043 
00044 // convert physical points to pixel points
00045 inline
00046 void
00047 physical_to_pixel( vnl_double_3 const& point,
00048                    vnl_int_3         & pixel_loc,
00049                    vnl_double_3 const& spacing_ratio )
00050 {
00051   for ( unsigned i = 0; i < 3; ++i )
00052     pixel_loc[ i ] = (int) vnl_math_rnd( point[ i ] / spacing_ratio[ i ] );
00053 }
00054 
00055 // convert physical points to pixel points
00056 inline
00057 void
00058 physical_to_pixel( vnl_double_3 const& point,
00059                    vnl_double_3      & pixel_loc,
00060                    vnl_double_3 const& spacing_ratio )
00061 {
00062   for ( unsigned i = 0; i < 3; ++i )
00063     pixel_loc[ i ] = point[ i ] / spacing_ratio[ i ] ;
00064 }
00065 
00066 // check if the location is inside the mask and the image.
00067 template <class PixelType>
00068 inline
00069 bool
00070 pixel_in_range( vil3d_image_view< PixelType > const& image,
00071                 rgrl_mask_sptr const& mask,
00072                 vnl_int_3 const& location )
00073 {
00074 //    vnl_vector< double > loc_dbl( location.size() );
00075 //    for ( unsigned i = 0; i < location.size(); ++i )
00076 //      loc_dbl[ i ] = location[ i ];
00077 
00078   // mask is only 2D,
00079   // to be sure, check all dim
00080   if ( location[ 0 ] < 0 || location[ 0 ] > (int)image.ni()-1 ||
00081        location[ 1 ] < 0 || location[ 1 ] > (int)image.nj()-1 ||
00082        location[ 2 ] < 0 || location[ 2 ] > (int)image.nk()-1 )
00083   return false;
00084 
00085   if ( mask ) {
00086     // So far, 3D CT images can use one 2D mask image for each slices.
00087     static vnl_vector< double > loc_dbl( 2 );
00088     for ( unsigned i = 0; i < 2; ++i )
00089       loc_dbl[ i ] = double(location[ i ]);
00090 
00091 //    vcl_cout << "mask pixel loc: " << loc_dbl << '\n';
00092     if (  !mask->inside( loc_dbl ) )
00093       return false;
00094   }
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_double_3 pixel_loc;
00107   physical_to_pixel( location, pixel_loc, spacing_ratio );
00108   // cannot just call pixel_in_range, because the coordinate here is double type
00109 
00110   // mask is only 2D,
00111   // to be sure, check all dim
00112   if ( pixel_loc[ 0 ] < 0 || pixel_loc[ 0 ] > (double)(image.ni()-1) ||
00113        pixel_loc[ 1 ] < 0 || pixel_loc[ 1 ] > (double)(image.nj()-1) ||
00114        pixel_loc[ 2 ] < 0 || pixel_loc[ 2 ] > (double)(image.nk()-1) )
00115   return false;
00116 
00117   if ( mask ) {
00118     // So far, 3D CT images can use one 2D mask image for each slices.
00119     static vnl_vector< double > loc_dbl( 2 );
00120     loc_dbl [0] = pixel_loc[0];
00121     loc_dbl [1] = pixel_loc[1];
00122 
00123     if (  !mask->inside( loc_dbl ) )
00124       return false;
00125   }
00126   return true;
00127 }
00128 
00129 template <class PixelType>
00130 rgrl_matcher_pseudo_int_3d< PixelType > ::
00131 rgrl_matcher_pseudo_int_3d( vil3d_image_view<PixelType> const& from_image,
00132                             vil3d_image_view<PixelType> const& to_image,
00133                             vnl_vector< double >        const& from_spacing_ratio,
00134                             vnl_vector< double >        const& to_spacing_ratio,
00135                             rgrl_evaluator_sptr                evaluator,
00136                             rgrl_mask_sptr                     mask )
00137   : from_image_( from_image ),
00138     to_image_( to_image ),
00139     mask_( mask ),
00140     evaluator_( evaluator ),
00141     from_spacing_ratio_( from_spacing_ratio ),
00142     to_spacing_ratio_( to_spacing_ratio )
00143 {
00144   assert( from_spacing_ratio.size() == 3 );
00145   assert( to_spacing_ratio.size() == 3 );
00146 }
00147 
00148 
00149 template <class PixelType>
00150 rgrl_match_set_sptr
00151 rgrl_matcher_pseudo_int_3d< PixelType > ::
00152 compute_matches( rgrl_feature_set const&    from_set,
00153                  rgrl_feature_set const&    to_set,
00154                  rgrl_view        const&    current_view,
00155                  rgrl_transformation const& current_xform,
00156                  rgrl_scale          const& current_scale,
00157                  rgrl_match_set_sptr const& /*old_matches*/ )
00158 {
00159   vcl_cerr << "compute_matches()\n";
00160 
00161   typedef vcl_vector<rgrl_feature_sptr> f_vector_type;
00162   typedef f_vector_type::iterator f_iterator_type;
00163 
00164   //  Build an empty match set
00165   rgrl_match_set_sptr matches_sptr 
00166     = new rgrl_match_set( from_set.type(), to_set.type(), from_set.label(), to_set.label() );
00167 
00168   //  Get the from image features in the current view
00169   f_vector_type from;
00170   from_set.features_in_region( from, current_view.region() );
00171 
00172   //  Vector for mapped pixels
00173   rgrl_mapped_pixel_vector_type  mapped_pixels;
00174 
00175   //  Vectors for matched features and weights.
00176   f_vector_type matched_to_features;
00177   vcl_vector<double> match_weights;
00178 
00179   // reserve space
00180   matches_sptr->reserve( from.size() );
00181   // Match each feature...
00182    for ( f_iterator_type fitr = from.begin(); fitr != from.end(); ++fitr )
00183    {
00184      // Match by searching in the tangent space of the
00185      // transformed from image feature.  The match_weights are to be
00186      // treated later as similarity weights
00187      matched_to_features.clear();
00188      match_weights.clear();
00189 
00190      // Map the feature location using the current transformation
00191      rgrl_feature_sptr mapped_feature = (*fitr)->transform( current_xform );
00192 
00193      // if the location is not inside the valid region
00194      // set the weight = 0
00195      if ( !physical_in_range( to_image_, mask_, mapped_feature->location(), to_spacing_ratio_ ) ) {
00196        //  Make a dummy vector of intensity weights.
00197        // vcl_vector< double > dummy_intensity_weights( 0 ); //CT: not needed now
00198        // vcl_vector< double > match_weights( 0 );
00199 
00200        //  Add the feature and its matches and weights to the match set
00201        matches_sptr
00202          -> add_feature_matches_and_weights( *fitr, mapped_feature, matched_to_features,
00203                                              match_weights );
00204        DebugMacro(3, " skip match from: " << (*fitr)->location() << ", to: " << mapped_feature->location() << '\n' );
00205        continue;
00206      }
00207 
00208      // Map the intensities of the pixels in the from image
00209      // surrounding the from image feature.  Form a vector of pairs,
00210      // with each pair containing a mapped location and the
00211      // associated intensity.
00212      mapped_pixels.clear();
00213 
00214      DBG(
00215        if ( (*fitr)->is_type( rgrl_feature_trace_region::type_id() ) ) {
00216          vcl_cout << "\nfrom :\n" << (*fitr)->location()
00217                   << " normal: "
00218                   << rgrl_cast<rgrl_feature_trace_region *> ( *fitr )->normal_subspace().get_column(0)
00219                   << "\nto :\n" << mapped_feature->location()
00220                   << " normal: "
00221                   << rgrl_cast<rgrl_feature_trace_region *> ( mapped_feature )->normal_subspace().get_column(0)
00222                   << vcl_endl;
00223        }
00224        else if ( (*fitr)->is_type( rgrl_feature_face_region::type_id() ) ) {
00225          vcl_cout << "\nfrom :\n" << (*fitr)->location()
00226                   << " normal: "
00227                   << rgrl_cast<rgrl_feature_face_region *> ( *fitr )->normal()
00228                   << "\nto :\n" << mapped_feature->location()
00229                   << " normal: "
00230                   << rgrl_cast<rgrl_feature_face_region *> ( mapped_feature )->normal()
00231                   << vcl_endl;
00232        }
00233      );
00234 
00235      this -> map_region_intensities( current_xform, (*fitr), mapped_pixels );
00236 
00237      // if there is no mapped pixels in the valid region, no matcher is created
00238      if ( mapped_pixels.size() == 0 ) {
00239        //  Make a dummy vector of intensity weights.
00240        // vcl_vector< double > dummy_intensity_weights( 0 ); //CT: not needed now
00241        // vcl_vector< double > match_weights( 0 );
00242 
00243        //  Add the feature and its matches and weights to the match set
00244        matches_sptr
00245          -> add_feature_matches_and_weights( *fitr, mapped_feature, matched_to_features,
00246                                              match_weights );
00247        vcl_cout << " from point : " << (*fitr)->location()
00248                 << " to point : " << mapped_feature->location()
00249                 << " doesn't have proper matches\n" << vcl_endl;
00250        continue;
00251      }
00252 
00253      this -> slide_window( mapped_feature, mapped_pixels, current_scale,
00254                                   matched_to_features, match_weights );
00255 
00256      //  Make a dummy vector of intensity weights.
00257      //vcl_vector< double > dummy_intensity_weights( match_weights.size(), 1.0 );
00258 
00259      //  Add the feature and its matches and weights to the match set
00260      matches_sptr
00261        -> add_feature_matches_and_weights( *fitr, mapped_feature, matched_to_features, match_weights );
00262    }
00263 
00264   vcl_cout << " number of from points : " << matches_sptr->from_size() << vcl_endl;
00265   assert( matches_sptr->from_size() == from.size() );
00266   return matches_sptr;
00267 }
00268 
00269 
00270 template <class PixelType>
00271 void
00272 rgrl_matcher_pseudo_int_3d<PixelType> ::
00273 map_region_intensities( rgrl_transformation      const& trans,
00274                         rgrl_feature_sptr               feature_sptr,
00275                         rgrl_mapped_pixel_vector_type & mapped_pixels) const
00276 {
00277   if ( feature_sptr -> is_type( rgrl_feature_face_region::type_id() ) )
00278   {
00279     rgrl_feature_face_region * face_ptr =
00280       rgrl_cast<rgrl_feature_face_region *> ( feature_sptr );
00281     this -> map_region_intensities( face_ptr -> pixel_coordinates_ratio( from_spacing_ratio_.as_ref() ), trans,
00282             feature_sptr, mapped_pixels );
00283   }
00284   else
00285   {
00286     rgrl_feature_trace_region * trace_ptr =
00287     rgrl_cast<rgrl_feature_trace_region *> ( feature_sptr );
00288     this -> map_region_intensities( trace_ptr -> pixel_coordinates_ratio( from_spacing_ratio_.as_ref() ), trans,
00289             feature_sptr, mapped_pixels );
00290   }
00291 }
00292 
00293 // pixel_locations are neighboring pixels in "pixel coordinates".
00294 template <class PixelType>
00295 void
00296 rgrl_matcher_pseudo_int_3d<PixelType> ::
00297 map_region_intensities( vcl_vector< vnl_vector<int> > const& pixel_locations,
00298                         rgrl_transformation           const& trans,
00299                         rgrl_feature_sptr                    feature_sptr,
00300                         rgrl_mapped_pixel_vector_type      & mapped_pixels) const
00301 {
00302   DebugMacro( 1, "   number of pixel coorindates: " << pixel_locations.size() << vcl_endl );
00303   // check
00304   if ( pixel_locations.empty() ) return;
00305 
00306   //unsigned dim = feature_sptr -> location() . size();
00307   assert ( feature_sptr -> location() . size() == 3 ); // so far vil3d force it to be 3D
00308   vnl_double_3 physical_loc;
00309   vnl_int_3    current_pixel_loc;
00310   //const unsigned int size = pixel_locations.size();
00311 
00312   // transform all the pixels and store their locations in a vector
00313   vbl_bounding_box<double,3> box;
00314   mapped_info   mapped_pt;
00315   mapped_pt.pixel_ = vnl_double_3( 0.0, 0.0, 0.0 );   // not used field
00316   vcl_vector< mapped_info >  direct_mapped_pts;
00317   direct_mapped_pts.reserve( pixel_locations.size() );
00318   for ( unsigned int i=0; i<pixel_locations.size(); ++i )
00319   {
00320     current_pixel_loc = pixel_locations[i];
00321     // Check if the location is inside the valid region
00322     if ( !pixel_in_range( from_image_, mask_, current_pixel_loc ) )
00323       continue;
00324 
00325     //  Copy the int pixel locations to doubles.  Yuck.
00326     pixel_to_physical( current_pixel_loc, physical_loc, from_spacing_ratio_ );
00327 
00328     // map the pixel, in the physical coordinates, and then convert
00329     // it to the pixel cooridinates.
00330     vnl_double_3 mapped_physical_pt;
00331     trans.map_location( physical_loc, mapped_physical_pt.as_ref().non_const() );
00332     // Check if the mapped location is inside the valid region
00333     if ( !physical_in_range( to_image_, mask_, mapped_physical_pt, to_spacing_ratio_ ) )
00334       continue;
00335 
00336     // store
00337     mapped_pt.physical_ = mapped_physical_pt;
00338     physical_to_pixel( mapped_physical_pt, mapped_pt.pixel_, to_spacing_ratio_ );
00339     // only use the first plane/channel
00340     mapped_pt.in_ = from_image_( current_pixel_loc[0], current_pixel_loc[1], current_pixel_loc[2], 0 );
00341     direct_mapped_pts.push_back( mapped_pt );
00342 
00343     // update bounding box
00344     box.update( mapped_pt.pixel_[0], mapped_pt.pixel_[1], mapped_pt.pixel_[2] );
00345   }
00346 
00347   // check for empty bounding box
00348   if ( box.empty() )   return;
00349   // the dimension of a smallest image that encasulate the bounding box
00350   vnl_int_3 origin, dim;
00351   origin[0] = (int)vcl_floor(box.xmin());
00352   origin[1] = (int)vcl_floor(box.ymin());
00353   origin[2] = (int)vcl_floor(box.zmin());
00354   dim[0] = 1+(int)vcl_ceil(box.xmax()) - (int)vcl_floor(box.xmin());
00355   dim[1] = 1+(int)vcl_ceil(box.ymax()) - (int)vcl_floor(box.ymin());
00356   dim[2] = 1+(int)vcl_ceil(box.zmax()) - (int)vcl_floor(box.zmin());
00357 
00358   DebugMacro( 1, "Origin: " << origin << " Dim: " << dim << vcl_endl );
00359   // create a 3D image with this dim
00360   vbl_array_3d<double> wgted_sum( dim[0], dim[1], dim[2] ); // 1 plane
00361   vbl_array_3d<double> wgts( dim[0], dim[1], dim[2] ); // 1 plane
00362   wgted_sum.fill( 0.0 );
00363   wgts.fill( 0.0 );
00364 
00365   // Distribute each mapped point to their integer neighbors
00366   // The intensity of each integer point is a weighted sum of
00367   // closest points.
00368   // NOTE:
00369   // the weight could depend on either physical coordinate or
00370   // pixel coordinate.
00371   // The choice I made is to use pixel coordinate, because I want the weight
00372   // to have smooth transition between 1.0(coincide with the point) and 0.0
00373   // (one pixel away).
00374   for (typename vcl_vector<mapped_info>::const_iterator it=direct_mapped_pts.begin();
00375       it!=direct_mapped_pts.end(); ++it) {
00376     vnl_int_3 ceil, floor;
00377     vnl_double_3 diff_floor;
00378     for (unsigned i=0; i<3; i++) {
00379       floor[i] = (int)vcl_floor( it->pixel_[i] ) - origin[i];
00380       ceil [i] = (int)vcl_ceil ( it->pixel_[i] ) - origin[i];
00381       diff_floor[i] = it->pixel_[i] - origin[i] - floor[i];
00382     }
00383 
00384     double wgt;
00385     for (int i=floor[0]; i<=ceil[0]; i++)
00386       for (int j=floor[1]; j<=ceil[1]; j++)
00387         for (int k=floor[2]; k<=ceil[2]; k++) {
00388           // ceil(x) is not equivalent to floor(x)+1, try x=4,
00389           // or any integer pos
00390           wgt = vcl_abs( floor[0]+1-i-diff_floor(0) ) *
00391                 vcl_abs( floor[1]+1-j-diff_floor(1) ) *
00392                 vcl_abs( floor[2]+1-k-diff_floor(2) );
00393           wgts(i,j,k) += wgt;
00394           wgted_sum(i,j,k) += wgt* double(it->in_);
00395         }
00396   }
00397 
00398   // Count all pixels have positive weights
00399   mapped_pixels.reserve( pixel_locations.size() );
00400   rgrl_mapped_pixel_type  mapped_pixel;
00401   mapped_pixel . weight = 1.0;
00402   for (int i=0; i<dim[0]; ++i)
00403     for (int j=0; j<dim[1]; ++j)
00404       for (int k=0; k<dim[2]; ++k)
00405         if ( wgts(i,j,k) >= 1e-8 ) {  // artitrary threshold for numerical stability
00406           mapped_pixel.intensity = wgted_sum(i,j,k) / wgts(i,j,k);
00407           mapped_pixel.location[0] = i + origin[0];
00408           mapped_pixel.location[1] = j + origin[1];
00409           mapped_pixel.location[2] = k + origin[2];
00410 
00411           //DebugMacro(2, "mapped pixel loc: " << mapped_pixel.location << " intensity: " << mapped_pixel.intensity <<vcl_endl )
00412           mapped_pixels.push_back( mapped_pixel );
00413         }
00414   DebugMacro(1, "Total mapped pixels at integer locations: " << mapped_pixels.size() << vcl_endl );
00415 }
00416 
00417 inline
00418 double
00419 sub_pixel( vcl_vector< double > const& responses )
00420 {
00421   assert( 0 ); // no need to use SVD
00422   assert( responses.size() == 3 );
00423 
00424   // let s be the similarity error, s = a r^2 + b r + c.
00425   // Use points index-1, index, index+1 to model the
00426   // parameters X = [a, b, c].
00427   vnl_matrix < double > A ( 3, 3 );
00428   vnl_matrix < double > S ( 3, 1 ) ;
00429 
00430   for ( unsigned i = 0; i < 3; ++i ) {
00431     // the middle point is at r = 0
00432     int r = i - 1;
00433     A( i, 0 ) = r * r;
00434     A( i, 1 ) = r;
00435     A( i, 2 ) = 1;
00436     S( i, 0 ) = responses[ i ];
00437   }
00438 
00439   vnl_matrix< double > inv = vnl_inverse(A);
00440   vnl_matrix< double > X = inv * S;
00441   assert( X.columns() == 1 );
00442 
00443   // if it fit a line, instead of a parabola
00444   // then return the original best index
00445   if ( X[ 0 ][ 0 ] <= 1.0e-5 )
00446     return 0;
00447 
00448   // find r that minimizes s
00449   // ds = 2ar + b = 0
00450   // r = -b / 2a
00451   double best_index =  -X[ 1 ][ 0 ] / ( 2 * X[ 0 ][ 0 ] );
00452 
00453   DBG( vcl_cout << " best_index = " << best_index << '\n' ) ;
00454 
00455   assert( best_index <= 1 && best_index >= -1 );
00456 
00457   return best_index;
00458 }
00459 
00460 // slide the window in normal direction(s)
00461 // to find the optimum response
00462 template <class PixelType>
00463 void
00464 rgrl_matcher_pseudo_int_3d<PixelType> ::
00465 slide_window(rgrl_feature_sptr         mapped_feature,
00466              rgrl_mapped_pixel_vector_type const & mapped_pixels,
00467              rgrl_scale                    const & current_scale,
00468              vcl_vector< rgrl_feature_sptr >     & matched_to_features,
00469              vcl_vector< double >                & match_weights ) const
00470 {
00471   //  At this point, find the most similar region within the sliding window
00472   unsigned int dim = mapped_feature -> location().size();
00473 
00474   const double scale_multiplier = 4;   // magic number.  frown.
00475 
00476   DebugMacro(2, " geometric scale = " << current_scale.geometric_scale() << vcl_endl );
00477 
00478   vnl_matrix< double > normal_space;
00479 
00480   if ( mapped_feature -> is_type( rgrl_feature_face_region::type_id() ) )
00481   {
00482     rgrl_feature_face_region * face_ptr =
00483       rgrl_cast<rgrl_feature_face_region *> ( mapped_feature );
00484     normal_space.set_size( dim, 1 );
00485     normal_space.set_column ( 0, face_ptr -> normal() );
00486   }
00487   else // RGRL_TRACE_REGION
00488   {
00489     rgrl_feature_trace_region * trace_ptr =
00490     rgrl_cast<rgrl_feature_trace_region *> ( mapped_feature );
00491     normal_space = trace_ptr -> normal_subspace();
00492   }
00493 
00494   vnl_vector<double> match_location(3, 0.0);
00495   double min_response = 0.0;
00496   double second_derivative = 0.0;
00497   double max_length = scale_multiplier * current_scale.geometric_scale();
00498   if ( max_length < 1 ) max_length = 1;
00499 
00500   //  DO THE REST DEPENDING ON IF THE NORMAL SUBSPACE IS 1D or 2D.
00501   //  IN THE FUTURE, IF WE WANT TO JUMP TO N-D, THIS WILL NEED TO BE
00502   //  CHANGED, PERHAPS JUST BY ADDING EACH DIMENSION WE WANT.
00503 
00504   if ( normal_space . columns() == 1 )
00505   {
00506     vnl_double_3 physical_basis = normal_space.get_column(0);
00507     vnl_double_3 pixel_basis;
00508     physical_to_pixel( physical_basis, pixel_basis, to_spacing_ratio_ );
00509 
00510     // sample pixel(integer) locations
00511     vcl_vector< discrete_shift_node > discrete_offsets;
00512     // ASSUME known structure of the return offsets,
00513     // that is symmetric around origin
00514     // such as:  -4 -3 -2 -1 0 1 2 3 4
00515     sample_pixels_along_direction( discrete_offsets, pixel_basis, max_length );
00516     // This assertion should never fail,
00517     // unless geometric scale becomes too small
00518     assert( discrete_offsets.size() > 1 );
00519     DebugMacro(2, "  shift vector length: " << discrete_offsets.size() );
00520 
00521     // the shifts are symmetric around origin
00522     const int max_offset = (discrete_offsets.size()-1)/2;
00523 
00524     vcl_vector<double> responses( 2*max_offset+1, 0.0 );
00525     bool is_best_initialized = false;
00526     int best_offset = 0;
00527 
00528     // Don't favor the max_offset_range. sometimes the region is
00529     // homogeneous, the responses might have same value
00530     for ( int abs_offset = 0; abs_offset <= max_offset; ++abs_offset )
00531     {
00532       int offset = abs_offset;
00533       do {
00534         int i = offset + max_offset;
00535         responses[i] = this -> compute_response( mapped_pixels, discrete_offsets[i].shift_ );
00536         DebugMacro(2, " response at offset " << discrete_offsets[i].shift_
00537                    << " ( i = " << i << " ) : " << responses[ i ] << vcl_endl );
00538 
00539         // We don't want to use the responses of the offsets that shift
00540         // the box across the boundary.
00541         if ( (!is_best_initialized || responses[i] < min_response ) &&
00542              responses[ i ] != max_response_value )
00543         {
00544           is_best_initialized = true;
00545           min_response = responses[i];
00546           best_offset = offset;
00547         }
00548         offset = -offset;
00549       } while ( offset < 0 );
00550     }
00551 
00552     DebugMacro(2, " the best offset = " << discrete_offsets[best_offset+max_offset].shift_ << vcl_endl );
00553     if ( !is_best_initialized )
00554     {
00555       DebugMacro(1, "For mapped feature: " << mapped_feature->location()
00556                  << ", the slide window is invalid." << vcl_endl );
00557       return;
00558     }
00559 
00560     //  Evaluate the second derivative at the peak.  If the
00561     //  peak occurrence is on the boundary, compute the second
00562     //  derivative based on one step in from the boundary.
00563     //  Get back to the representation: origin is 0
00564     int deriv_loc = best_offset;
00565     if ( deriv_loc == -max_offset ) ++ deriv_loc;
00566     else if ( deriv_loc == max_offset ) -- deriv_loc;
00567     int index = deriv_loc + max_offset;
00568     DebugMacro(3, " the proper offset = " << deriv_loc << vcl_endl );
00569 
00570     // update the best matched location
00571     const vnl_int_3& best = discrete_offsets[best_offset+max_offset].shift_;
00572     match_location = mapped_feature->location();
00573     for (unsigned int i=0; i<3; i++)
00574       match_location[i] += double(best[i]);
00575     DebugMacro(2, "best match :\n" << match_location << vcl_endl );
00576 
00577     // Now compute the second derivative
00578     // Note that these descrete points are not evenly distributed.
00579     // Solution:
00580     // Use Taylor expansion on f(x0-d1), f(x0), and f(x0+d2)
00581     // It is easy to show that in order to get f''(x) = a1*f(x0-d1) + a2*f(x0) + a3*f(x0+d2)
00582     // a1 = 2/(d1*(d1+d2))
00583     // a2 = - 2/(d1*d2)
00584     // a3 = 2/(d2*(d1+d2))
00585     // If one neighbor's response is not valid, calculate the second
00586     // derivative value of the other neighbor
00587     if ( responses[ index - 1 ] == max_response_value )
00588       index ++;
00589     else if ( responses[ index + 1 ] == max_response_value )
00590       index--;
00591 
00592     double a1, a2, a3, d1, d2, sumd;
00593     //assert( responses[ index ] != max_response_value );
00594     if ( index > 0 && index+1 < (int)responses.size() &&
00595          responses[ index ] != max_response_value &&
00596          index + 1 <= 2*max_offset &&
00597          index - 1 >= -2*max_offset &&
00598          responses[ index + 1 ] != max_response_value &&
00599          responses[ index - 1 ] != max_response_value )
00600     {
00601       d2 = discrete_offsets[ index+1 ].step_ - discrete_offsets[ index ].step_;
00602       d1 = discrete_offsets[ index ].step_ - discrete_offsets[ index-1 ].step_;
00603       sumd = d1+d2;
00604       a1 = 2.0/(d1*sumd);
00605       a3 = 2.0/(d2*sumd);
00606       a2 = -2.0/(d1*d2);
00607       // take abs value, for it can be shifted for the boundary response,
00608       // or invalid points
00609       second_derivative = vcl_abs( a1*responses[ index-1 ] +
00610                                    a2*responses[ index ] +
00611                                    a3*responses[ index + 1] );
00612       DebugMacro(3, "  2nd Derivative(at " << index
00613                  << "): d1=" << d1 << " d2=" << d2
00614                  << "\n       a1=" << a1 <<" a2=" << a2
00615                  << " a3=" << a3
00616                  << "\n        res1 " << responses[ index-1 ]
00617                  << "  res2 " << responses[ index ]
00618                  << "  res3 " << responses[ index+1 ]
00619                  << "\n        deriv=" << second_derivative << vcl_endl ) ;
00620     }
00621     else
00622     {
00623       second_derivative = 0;
00624       DebugMacro(2, "index=" << index << ", max_offset=" << max_offset
00625                  << ", responses[index-1]=" << responses[index-1]
00626                  << ", responses[index+1]=" << responses[index+1] << '\n'
00627                  << "   neighbors' responses are not valid. Set the second_derivative = 0\n" );
00628     }
00629   }
00630   else if ( normal_space . columns() == 2 )
00631   {
00632     //int max_offset = int(max_length);
00633     vnl_vector<double> basis1 = normal_space . get_column(0);
00634     vnl_vector<double> basis2 = normal_space . get_column(1);
00635 
00636     DebugMacro(2, "normal basis :\n" << basis1 << " and " << basis2 << vcl_endl );
00637 
00638     // sample pixels along basis directions.
00639     vcl_vector< discrete_shift_node > offset1, offset2;
00640     // NOTE: the returned shift vector is symmetric around origin
00641     // for details, look at the face session(above)
00642     sample_pixels_along_direction( offset1, basis1, max_length );
00643     sample_pixels_along_direction( offset2, basis2, max_length );
00644     const int max_offset1 = (offset1.size()-1) / 2;
00645     const int max_offset2 = (offset2.size()-1) / 2;
00646     //vcl_vector<double> temp( 2*max_offset+1, 0.0 );
00647     //vcl_vector< vcl_vector<double> > responses( 2*max_offset+1, temp );
00648     vbl_array_2d<double> responses( 2*max_offset1+1, 2*max_offset2+1, 0.0 );
00649     bool is_best_initialized = false;
00650     int best_off1 = 0, best_off2 = 0;
00651 
00652     //  Find the offset along the basis direction giving the best
00653     //  response.
00654 
00655     for ( int off1 = -max_offset1, i=0; off1 <= max_offset1; ++off1, ++i )
00656     {
00657       for ( int off2 = -max_offset2, j=0; off2 <= max_offset2; ++off2, ++j )
00658       {
00659         responses(i,j) = this -> compute_response( mapped_pixels, offset1[i].shift_ + offset2[j].shift_ );
00660 
00661         if ( ( !is_best_initialized || responses(i,j) < min_response )
00662              && responses(i,j) != max_response_value )
00663         {
00664                 is_best_initialized = true;
00665                 min_response = responses(i,j);
00666                 best_off1 = off1;
00667                 best_off2 = off2;
00668         }
00669       }
00670     }
00671     if ( !is_best_initialized )
00672     {
00673       DebugMacro(1, "For mapped feature: " << mapped_feature->location()
00674                  << ", the slide window is invalid." << vcl_endl );
00675       return;
00676     }
00677 
00678     const vnl_int_3& best1 = offset1[best_off1+max_offset1].shift_;
00679     const vnl_int_3& best2 = offset2[best_off2+max_offset2].shift_;
00680     match_location = mapped_feature->location();
00681     for (unsigned int i=0; i<3; i++)
00682       match_location[i] += double(best1[i]) + double(best2[i]);
00683 
00684     // TODO
00685     // compute the second drivative
00686     assert( 0 );
00687 #if 0 // commented out
00688     //  Evaluate the second derivative at the peak.  If the
00689     //  peak occurrence is on the boundary, compute the second
00690     //  derivative based on one step in from the boundary.
00691 
00692     int idx1 = 0, idx2 = 0;   // indices into the array of responses
00693     int deriv_loc1 = best_off1;
00694     if ( deriv_loc1 == -max_offset ) ++deriv_loc1;
00695     else if ( deriv_loc1 == max_offset ) --deriv_loc1;
00696     idx1 = deriv_loc1 + max_offset;
00697     idx2 = best_off2 + max_offset;
00698 
00699     // The best_offset so far is constrained on the discrete space.
00700     // Now we use a parabola to model the similarity error
00701     // (responses) and find the position of the minimum response.
00702     // Here I calculate sub_pixel in each dimension seperately just for
00703     // the convenience. Since it's only an approximation in one grid,
00704     // I assume this approximation is good enough.
00705     double sub_offset1;
00706 
00707     if ( best_off1 == max_offset || best_off1 == -max_offset )
00708   sub_offset1 = best_off1;
00709     else if ( responses[ idx1 - 1 ][ idx2 ] == max_response_value ||
00710         responses[ idx1 + 1 ][ idx2 ] == max_response_value )
00711     {
00712       sub_offset1 = idx1 - max_offset;
00713     }
00714     else
00715     {
00716       vcl_vector< double > responses_for_sub_pixel( 3 );
00717       responses_for_sub_pixel[ 0 ] = responses[ idx1 - 1 ][ idx2 ];
00718       responses_for_sub_pixel[ 1 ] = responses[ idx1 ][ idx2 ];
00719       responses_for_sub_pixel[ 2 ] = responses[ idx1 + 1 ][ idx2 ];
00720       sub_offset1 = sub_pixel( responses_for_sub_pixel ) + idx1 - max_offset;
00721       // the sub_pixel here is used only for interpolation
00722       // if it's outside
00723       if ( sub_offset1 < -max_offset ) sub_offset1 = -max_offset;
00724       if ( sub_offset1 > max_offset ) sub_offset1 = max_offset;
00725       DBG( vcl_cout << " sub_offset1 = " << sub_offset1 << " in [ "
00726                     << -max_offset << " , " << max_offset << " ] " << vcl_endl );
00727     }
00728 
00729     double second_d1 = vnl_math_abs( responses[ idx1-1 ][ idx2 ] + responses[ idx1+1 ][ idx2 ]
00730                                      - 2 * responses[ idx1 ][ idx2 ] );
00731 
00732     int deriv_loc2 = best_off2;
00733     if ( deriv_loc2 == -max_offset ) ++deriv_loc2;
00734     else if ( deriv_loc2 == max_offset ) --deriv_loc2;
00735     idx2 = deriv_loc2 + max_offset;
00736     idx1 = best_off1 + max_offset;
00737     double sub_offset2;
00738     if ( best_off2 == max_offset || best_off2 == -max_offset )
00739   sub_offset2 = best_off2;
00740     else if ( responses[ idx1 ][ idx2 - 1 ] == max_response_value ||
00741         responses[ idx1 ][ idx2 + 1 ] == max_response_value )
00742     {
00743       sub_offset2 = idx2 - max_offset;
00744     }
00745     else
00746     {
00747       vcl_vector< double > responses_for_sub_pixel( 3 );
00748       responses_for_sub_pixel[ 0 ] = responses[ idx1 ][ idx2 - 1 ];
00749       responses_for_sub_pixel[ 1 ] = responses[ idx1 ][ idx2 ];
00750       responses_for_sub_pixel[ 2 ] = responses[ idx1 ][ idx2 + 1 ];
00751       sub_offset2 = sub_pixel( responses_for_sub_pixel ) + idx2 - max_offset;
00752       if ( sub_offset2 < -max_offset ) sub_offset2 = -max_offset;
00753       if ( sub_offset2 > max_offset ) sub_offset2 = max_offset;
00754       DBG( vcl_cout << " sub_offset2 = " << sub_offset2 << " in [ "
00755                     << -max_offset << " , " << max_offset << " ] " << vcl_endl; );
00756     }
00757 
00758     double second_d2 = vnl_math_abs( responses[ idx1 ][ idx2-1 ] + responses[ idx1 ][ idx2+1 ]
00759                                      - 2 * responses[ idx1 ][ idx2 ] );
00760 
00761     second_derivative = vnl_math_min( second_d1, second_d2 );
00762     match_location = mapped_location + basis1 * sub_offset1 + basis2 * sub_offset2;
00763     DBG( vcl_cout << "best match :\n" << match_location << vcl_endl );
00764 #endif // 0
00765   } else {
00766     vcl_cerr << "Code doesn't handle a normal subspace of greater than two dimenions.\n";
00767     assert( false );
00768   }
00769   matched_to_features . clear();
00770   match_weights . clear();
00771   rgrl_feature_sptr mf_ptr;
00772   if ( mapped_feature -> is_type( rgrl_feature_face_region::type_id() ) )
00773   {
00774     rgrl_feature_face_region * face_ptr =
00775     rgrl_cast<rgrl_feature_face_region *> ( mapped_feature );
00776     mf_ptr = new rgrl_feature_face_region( match_location, face_ptr -> normal() );
00777   } else
00778   {
00779     rgrl_feature_trace_region * trace_ptr =
00780     rgrl_cast<rgrl_feature_trace_region *> ( mapped_feature );
00781     mf_ptr = new rgrl_feature_trace_region( match_location, trace_ptr -> tangent() );
00782   }
00783 
00784 
00785   matched_to_features . push_back( mf_ptr );
00786   double weight = second_derivative / (1.0 + min_response);
00787   assert( weight >= 0.0 );
00788 
00789   DebugMacro(2, "second derivative: " << second_derivative
00790              << "\nmin_response: " << min_response << "\nweight : " << weight << vcl_endl );
00791   match_weights.push_back( weight );
00792 }
00793 
00794 template <class PixelType>
00795 double
00796 rgrl_matcher_pseudo_int_3d<PixelType> ::
00797 compute_response( rgrl_mapped_pixel_vector_type const& mapped_pixels,
00798                   vnl_int_3                  const& shift ) const
00799 {
00800   const unsigned size = mapped_pixels.size();
00801 
00802   //  Extract the intensities at the mapped locations.  Make sure
00803   //  they are inside the image.
00804 
00805   vcl_vector< double > a;
00806   vcl_vector< double > b;
00807   vcl_vector< double > weights;
00808   double intensity;
00809   vnl_int_3 loc;
00810 
00811   // reserve space
00812   a.reserve( size );
00813   b.reserve( size );
00814   weights.reserve( size );
00815 
00816   for ( unsigned i = 0; i < size; ++i )
00817   {
00818     loc = mapped_pixels[i].location + shift;
00819     // Check if the location is inside the valid region,
00820     // if not, we don't use the response of this shift
00821     if ( !pixel_in_range( to_image_, mask_, loc ) ) {
00822       DebugMacro(2, "out of range: " << loc << " ( shift: " << shift << " )\n" );
00823       return max_response_value;
00824     }
00825 
00826     // from mapped_pixels
00827     a.push_back( (double)(mapped_pixels[i].intensity) );
00828     // intensity on "to"(fixed) image
00829     intensity = to_image_( loc[0], loc[1], loc[2] );
00830     b.push_back( intensity );
00831 
00832     weights.push_back( mapped_pixels[i].weight );
00833   }
00834 
00835   //  call the response function
00836   double val = evaluator_->evaluate( a, b, weights );
00837 
00838   return val;
00839 }
00840 
00841 // ASSUME symmetric around origin
00842 // such as:  -4 -3 -2 -1 0 1 2 3 4
00843 template<class PixelType>
00844 void
00845 rgrl_matcher_pseudo_int_3d<PixelType>::
00846 sample_pixels_along_direction( vcl_vector<discrete_shift_node>& two_dir_shifts,
00847                               vnl_double_3 dir,
00848                               double max_length ) const
00849 {
00850   // make sure any element in normal vector/basis is less than 1.
00851   // Thus, divided by 2*mag, and double max_length;
00852   // I try to avoid directions like [1, 0, 0], in which 1 gives arise to problems.
00853   // The situation is different for 1 or 0.9999...
00854   // Therefore, I would rather have [0.5, 0, 0]
00855   dir /= 2.0 * dir.magnitude();
00856   max_length *= 2.0;
00857 
00858   DebugMacro(2, "normal basis :\n" << dir << vcl_endl );
00859 
00860   // the idea is to find the smallest delta length added to the current one,
00861   // in order to get to the nearest pixel location
00862   vnl_int_3 prev, current;
00863   double len;
00864   double abs_ele;
00865   vcl_vector<discrete_shift_node> locs;
00866   locs.reserve( int(2*max_length) );
00867 
00868   // init
00869   const double epsilon = 1.0/(100*max_length);  // 100 is arbitrary
00870   const double min_step_size = 1.0/(2*max_length);
00871   prev=vnl_int_3(0, 0, 0);
00872   locs.push_back( discrete_shift_node(prev, 0) );
00873   len = 1e-10; // has to be larger than zero, otherwise, vcl_ceil(0) = 0
00874   while ( len < max_length )
00875   {
00876     double delta_len, min_delta_len = 1e10;
00877     int min_index = 0;
00878     for (unsigned i=0; i<3; i++) {
00879       abs_ele = vcl_abs( dir[i] );
00880       // don't want to divide by too small a number
00881       if ( abs_ele < min_step_size )
00882         continue;
00883 
00884       // find the smallest step to next integer location
00885       delta_len = vcl_ceil( len * abs_ele ) / abs_ele - len;
00886       if ( delta_len < min_delta_len && delta_len > 0 ) //prevent delta_len==0
00887       {
00888         min_index = i;
00889         min_delta_len = delta_len;
00890       }
00891     }
00892 
00893     assert( min_delta_len > 0.0 );
00894     // keep it within max_length
00895     if ( len+min_delta_len > max_length )
00896       break;
00897 
00898     // find out the pixel location
00899     current = prev;
00900     if ( dir[min_index] < 0 )
00901       current[min_index] -= 1;
00902     else
00903       current[min_index] += 1;
00904 
00905     // store the length, instead of delta_len
00906     // It is required, as it is different case(left neighbor or right neighbor)
00907     // for positive leng and negative len
00908     locs.push_back( discrete_shift_node( current, (len+min_delta_len)/2 ) );
00909     prev = current;
00910     // update length
00911     // to prevent numerical rounding, e.g., 1.999999 instead of 2
00912     // add a small padding to make it larger
00913     len += min_delta_len+epsilon;
00914   }
00915 
00916   // copy
00917   two_dir_shifts.clear();
00918   two_dir_shifts.reserve( locs.size()*2-1 );
00919   // negative direction
00920   for (int i=locs.size()-1; i>0; i--)
00921     two_dir_shifts.push_back( - locs[i] );
00922 
00923   // positive direction, starting at origin
00924   for (unsigned int i=0; i<locs.size(); i++)
00925     two_dir_shifts.push_back( locs[i] );
00926 }
00927 
00928 #endif // rgrl_matcher_pseudo_int_3d_txx_