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