contrib/rpl/rgrl/rgrl_util.cxx
Go to the documentation of this file.
00001 //:
00002 // \file
00003 // \author Charlene Tsai
00004 
00005 #include "rgrl_util.h"
00006 #include <vcl_vector.h>
00007 #include <vcl_string.h>
00008 #include <vcl_cassert.h>
00009 #include <vcl_algorithm.h>
00010 
00011 #include <vnl/vnl_vector.h>
00012 #include <vnl/algo/vnl_svd.h>
00013 #include <vnl/vnl_math.h>
00014 
00015 #include <rgrl/rgrl_converge_status.h>
00016 #include <rgrl/rgrl_converge_status_sptr.h>
00017 #include <rgrl/rgrl_convergence_tester.h>
00018 #include <rgrl/rgrl_weighter.h>
00019 #include <rgrl/rgrl_estimator.h>
00020 #include <rgrl/rgrl_match_set.h>
00021 #include <rgrl/rgrl_mask.h>
00022 
00023 
00024 static
00025 rgrl_mask_box
00026 global_region_from_inv_xformed_points( 
00027           vcl_vector< vnl_vector<double> > const& inv_mapped_pts,
00028           rgrl_mask_sptr                   const& from_image_roi,
00029           rgrl_mask_box                    const& current_region,
00030           bool                                    union_with_curr,
00031           double                                  drastic_change_ratio)
00032 {
00033   typedef vcl_vector<vnl_vector<double> > pt_vector;
00034   typedef pt_vector::const_iterator pt_iter;
00035 
00036   const vnl_vector<double> from_x0 =  from_image_roi->x0();
00037   const vnl_vector<double> from_x1 =  from_image_roi->x1();
00038   const unsigned m = from_x0.size();
00039   const int debug_flag = 0;
00040   
00041   vnl_vector<double> inv_mapped_x0 = from_image_roi->x1();
00042   vnl_vector<double> inv_mapped_x1 = from_image_roi->x0();
00043   for ( pt_iter pitr = inv_mapped_pts.begin();  pitr != inv_mapped_pts.end(); ++pitr ) {
00044 
00045       vnl_vector<double> const& inv_mapped_pt = *pitr;
00046       
00047       //update the inv_mapped bounding box
00048       for ( unsigned d=0; d < m; ++d ) {
00049         if (inv_mapped_pt[d] < inv_mapped_x0[d]) inv_mapped_x0[d] = inv_mapped_pt[d];
00050         if (inv_mapped_pt[d] > inv_mapped_x1[d]) inv_mapped_x1[d] = inv_mapped_pt[d];
00051       }
00052   }
00053   
00054   DebugFuncMacro( debug_flag, 1, "Global Region after inv-mapping: " 
00055                   << inv_mapped_x0 << " - " << inv_mapped_x1 << vcl_endl );
00056 
00057   //3. Take the intersection of the from_image_roi and the inverse_xformed to_image_roi
00058   //   as the maximum region.
00059   vnl_vector<double> region_x0(m);
00060   vnl_vector<double> region_x1(m);
00061 
00062   for ( unsigned d=0; d < m; ++d ) {
00063     region_x0[d] = (inv_mapped_x0[d] > from_x0[d])? inv_mapped_x0[d]:from_x0[d];
00064     region_x1[d] = (inv_mapped_x1[d] < from_x1[d])? inv_mapped_x1[d]:from_x1[d];
00065     if ( region_x0[d] > from_x1[d] )  region_x0[d] = from_x1[d];
00066     if ( region_x1[d] < from_x0[d] )  region_x1[d] = from_x0[d];
00067   }
00068   if (region_x0 == region_x1) //no overlap
00069     return current_region;
00070 
00071   DebugFuncMacro( debug_flag, 1, "Global Region after intersecting with ROI: " 
00072                   << region_x0 << " - " << region_x1 << vcl_endl );
00073 
00074   //4. If union_with_curr set, union region and current_region to prevent oscillation
00075   if (union_with_curr) {
00076     for ( unsigned d=0; d < m; ++d ) {
00077       if (region_x0[d] > current_region.x0()[d]) region_x0[d] = current_region.x0()[d];
00078       if (region_x1[d] < current_region.x1()[d]) region_x1[d] = current_region.x1()[d];
00079     }
00080   }
00081 
00082   DebugFuncMacro( debug_flag, 1, "Global Region after union with prev region: " 
00083                   << region_x0 << " - " << region_x1 << vcl_endl );
00084 
00085   //5. If the changes from current_region is insignificant, or the change is too
00086   //   drastic, set region to be same ascurrent_region
00087   bool changed =
00088     ( (region_x0 - current_region.x0()).inf_norm() > 1 ||
00089       (region_x1 - current_region.x1()).inf_norm() > 1);
00090   bool drastic_changed = false;
00091   double prev_space = 1, new_space = 1;
00092   vnl_vector<double>  prev_space_dim = current_region.x1() - current_region.x0();
00093   vnl_vector<double>  new_space_dim = region_x1 - region_x0;
00094   for ( unsigned d=0; d < m; ++d ) {
00095     prev_space *= prev_space_dim[d];
00096     new_space  *= new_space_dim[d];
00097   }
00098   if ( union_with_curr && new_space/prev_space > drastic_change_ratio ) {
00099     drastic_changed = true;
00100   }
00101 
00102   rgrl_mask_box region = current_region;
00103   if ( changed && !drastic_changed) {
00104     region.set_x0(region_x0);
00105     region.set_x1(region_x1);
00106   }
00107 
00108   DebugFuncMacro( debug_flag, 1, "Global Region finalized: " 
00109                   << region_x0 << " - " << region_x1 << vcl_endl );
00110 
00111   return region;
00112   
00113 }
00114 
00115 rgrl_mask_box
00116 rgrl_util_estimate_global_region_with_inverse_xform( 
00117                   rgrl_mask_sptr const&        from_image_roi,
00118                   rgrl_mask_sptr const&        to_image_roi,
00119                   rgrl_mask_box const&         current_region,
00120                   rgrl_transformation const&   inv_xform,
00121                   bool                         union_with_curr,
00122                   double                       drastic_change_ratio)
00123 {
00124   // Forward map boundary points every 20 pixels 
00125   // of the from_image_roi. For each boundary q of the to_image_roi,
00126   // find the closest forward_xformed point for initialized inverse_map for q.
00127   // Take the intersection of the from_image_roi and the inverse_xformed to_image_roi
00128   // as the maximum region. If any q failed to converge or the region is null,
00129   // this procedure fails.
00130   //
00131 
00132   typedef vcl_vector<vnl_vector<double> > pt_vector;
00133   typedef pt_vector::iterator pt_iter;
00134 
00135   vnl_vector<double> const& to_x0 =  to_image_roi->x0();
00136   vnl_vector<double> const& to_x1 =  to_image_roi->x1();
00137   const unsigned m = to_x0.size();
00138   assert( 2 <= m && m <= 3 );
00139   
00140   // dimension/axis index
00141   vcl_vector<int> ind( m );
00142   for( unsigned i=0; i<m; ++i )
00143     ind[i] = i;
00144 
00145   //1. Place the boundary points of to_image_roi into a list
00146   //
00147   pt_vector to_boun_pts;
00148   pt_vector inv_mapped_pts;
00149   vnl_vector<double> pt( m );
00150 
00151   // reserve space
00152   to_boun_pts.reserve( 500 );
00153   inv_mapped_pts.reserve( 500 );
00154 
00155   // apply permutation on ind
00156   // the position of 1 will change for each iteration
00157   do{
00158   
00159     const double step = 30;
00160     for (double i = to_x0[ind[0]]; i<= to_x1[ind[0]]; i+=step) {
00161       for (double j = to_x0[ind[1]]; j<= to_x1[ind[1]]; j+=step) {
00162       
00163         if (m == 3) {
00164           for (double k = to_x0[ind[2]]; k<= to_x1[ind[2]]; k+=step) {
00165             pt[ind[0]] = i;
00166             pt[ind[1]] = j;
00167             pt[ind[2]] = k;
00168             to_boun_pts.push_back(pt);
00169           }
00170         } else {
00171           pt[ind[0]] = i;
00172           pt[ind[1]] = j;
00173           to_boun_pts.push_back(pt);
00174         }
00175 
00176       }
00177     }
00178 
00179     // Only the 1st element keeps the boundary dimension
00180     // The others can exchange wo/ affecting the boundary
00181     while(vcl_next_permutation(ind.begin()+1, ind.end() ) )    ;
00182     
00183   }while( vcl_next_permutation(ind.begin(), ind.end() ) );
00184 
00185     
00186   //2. For each boundary point q of the to_image_roi, inverse map it to 
00187   //   From image
00188   //
00189   vnl_vector<double> inv_mapped_pt( m );
00190   for ( pt_iter pitr = to_boun_pts.begin();  pitr != to_boun_pts.end(); ++pitr ) {
00191 
00192     inv_xform.map_location( *pitr,  inv_mapped_pt );
00193     inv_mapped_pts.push_back( inv_mapped_pt );      
00194   }        
00195 
00196   //3. form global region
00197   //
00198   return global_region_from_inv_xformed_points( inv_mapped_pts, 
00199                                                 from_image_roi,
00200                                                 current_region,
00201                                                 union_with_curr,
00202                                                 drastic_change_ratio );
00203 }
00204 
00205 
00206 rgrl_mask_box
00207 rgrl_util_estimate_global_region( rgrl_mask_sptr const&        from_image_roi,
00208                                   rgrl_mask_sptr const&        to_image_roi,
00209                                   rgrl_mask_box const&         current_region,
00210                                   rgrl_transformation const&   curr_xform,
00211                                   bool                         union_with_curr,
00212                                   double                       drastic_change_ratio)
00213 {
00214   // Forward map boundary points every 20 pixels 
00215   // of the from_image_roi. For each boundary q of the to_image_roi,
00216   // find the closest forward_xformed point for initialized inverse_map for q.
00217   // Take the intersection of the from_image_roi and the inverse_xformed to_image_roi
00218   // as the maximum region. If any q failed to converge or the region is null,
00219   // this procedure fails.
00220   //
00221 
00222   typedef vcl_vector<vnl_vector<double> > pt_vector;
00223   typedef pt_vector::iterator pt_iter;
00224   const double epsilon = 1;
00225   const double eps_squared = epsilon*epsilon;
00226 
00227   vnl_vector<double> const& from_x0 =  from_image_roi->x0();
00228   vnl_vector<double> const& from_x1 =  from_image_roi->x1();
00229   const unsigned m = from_x0.size();
00230   assert( 2 <= m && m <= 3 );
00231   
00232   // dimension/axis index
00233   vcl_vector<int> ind( m );
00234   for( unsigned i=0; i<m; ++i )
00235     ind[i] = i;
00236 
00237 
00238   //1. Place the boundary points of to_image_roi into a list
00239   //
00240   vnl_vector<double> const& to_x0 =  to_image_roi->x0();
00241   vnl_vector<double> const& to_x1 =  to_image_roi->x1();
00242   pt_vector to_boun_pts;
00243   pt_vector inv_mapped_pts;
00244   vnl_vector<double> pt( m );
00245 
00246   // reserve space
00247   to_boun_pts.reserve( 500 );
00248   inv_mapped_pts.reserve( 500 );
00249 
00250   // apply permutation on ind
00251   // the position of 1 will change for each iteration
00252   do{
00253   
00254     const double step = 30;
00255     for (double i = to_x0[ind[0]]; i<= to_x1[ind[0]]; i+=step) {
00256       for (double j = to_x0[ind[1]]; j<= to_x1[ind[1]]&&j>= to_x0[ind[1]]; j+=(to_x1[ind[1]]-to_x0[ind[1]])) {
00257       
00258         if (m == 3) {
00259           for (double k = to_x0[ind[2]]; k<= to_x1[ind[2]]&&k>= to_x0[ind[2]]; k+=(to_x1[ind[2]]-to_x0[ind[2]])) {
00260             pt[ind[0]] = i;
00261             pt[ind[1]] = j;
00262             pt[ind[2]] = k;
00263             to_boun_pts.push_back(pt);
00264           }
00265         } else {
00266           pt[ind[0]] = i;
00267           pt[ind[1]] = j;
00268           to_boun_pts.push_back(pt);
00269         }
00270 
00271       }
00272     }
00273 
00274     // Only the 1st element keeps the boundary dimension
00275     // The others can exchange wo/ affecting the boundary
00276     while(vcl_next_permutation(ind.begin()+1, ind.end() ) )    ;
00277     
00278   }while( vcl_next_permutation(ind.begin(), ind.end() ) );
00279 
00280     
00281   //2. For each boundary point q of the to_image_roi, inverse map it to 
00282   //   From image
00283   //
00284   if( curr_xform.is_invertible() ) {
00285     
00286     rgrl_transformation_sptr inv_xform = curr_xform.inverse_transform();
00287     vnl_vector<double> inv_mapped_pt( m );
00288     for ( pt_iter pitr = to_boun_pts.begin();  pitr != to_boun_pts.end(); ++pitr ) {
00289 
00290       inv_xform->map_location( *pitr,  inv_mapped_pt );
00291       inv_mapped_pts.push_back( inv_mapped_pt );      
00292     }        
00293 
00294   } else {
00295 
00296     //(1). compute the set of points from from_image_roi for forward mapping
00297     //
00298     pt_vector from_pts;
00299     const double step_eps = 1e-10;
00300     for (double i = from_x0[0]; 
00301          i<= from_x1[0]&&i>= from_x0[0]; 
00302          i+= (from_x1[0]-from_x0[0])/10-step_eps) {
00303       
00304       for (double j = from_x0[1]; 
00305            j<= from_x1[1]&&j>=from_x0[1]; 
00306            j+= (from_x1[1]-from_x0[1])/10-step_eps) {
00307         if (m == 3) {
00308           for (double k = from_x0[2]; 
00309                k<= from_x1[2]&&k>=from_x0[2]; 
00310                k+= (from_x1[2]-from_x0[2])/10-step_eps) {
00311             vnl_vector<double> pt(3);
00312             pt[0] = i;
00313             pt[1] = j;
00314             pt[2] = k;
00315             from_pts.push_back(pt);
00316           }
00317         }
00318         else {
00319           vnl_vector<double> pt(2);
00320           pt[0] = i;
00321           pt[1] = j;
00322           from_pts.push_back(pt);
00323         }
00324       }
00325     }
00326   
00327     //(2). Forward map all the points in from_pts
00328     pt_vector to_mapped_pts;
00329     vnl_vector<double> to_pt;
00330     for (pt_iter pitr = from_pts.begin();  pitr != from_pts.end(); ++pitr) {
00331       curr_xform.map_location(*pitr, to_pt);
00332       to_mapped_pts.push_back(to_pt);
00333     }
00334     
00335     //(3). For each corner point q of the to_image_roi, find the closest
00336     //     forward_xformed point for initialized inverse_map for q.
00337     //
00338     for ( pt_iter pitr = to_boun_pts.begin();  pitr != to_boun_pts.end(); ++pitr ) {
00339       double min_sqr_dist =  vnl_vector_ssd(to_mapped_pts[0], (*pitr));
00340       unsigned int min_index = 0;
00341       for (unsigned int i = 1; i<to_mapped_pts.size(); ++i) {
00342         double sqr_dist = vnl_vector_ssd(to_mapped_pts[i], (*pitr));
00343         if (sqr_dist < min_sqr_dist) {
00344           min_sqr_dist = sqr_dist;
00345           min_index = i;
00346         }
00347       }
00348       // use from_pts[min_index] as the initial guess for the inverse_mapped q
00349       vnl_vector<double> to_delta, from_next_est; //not used;
00350       vnl_vector<double> inv_mapped_pt = from_pts[min_index];
00351       curr_xform.inv_map(*pitr, false, to_delta, inv_mapped_pt, from_next_est);
00352       vnl_vector<double> fwd_mapp_pt = curr_xform.map_location(inv_mapped_pt);
00353       if (vnl_vector_ssd(fwd_mapp_pt, *pitr) > eps_squared) //didn't converge
00354         return current_region;
00355   
00356       inv_mapped_pts.push_back( inv_mapped_pt );
00357   
00358     }
00359   }
00360   
00361   //3. form global region
00362   //
00363   return global_region_from_inv_xformed_points( inv_mapped_pts, 
00364                                                 from_image_roi,
00365                                                 current_region,
00366                                                 union_with_curr,
00367                                                 drastic_change_ratio );
00368 }
00369 
00370 double
00371 rgrl_util_geometric_error_scaling( rgrl_match_set const& match_set )
00372 {
00373   vnl_vector<double> factors;
00374   bool success = rgrl_util_geometric_scaling_factors( match_set, factors );
00375   if ( !success )
00376     return 0.0;
00377 
00378   // Estimate the change in the spread of the feature set
00379   //
00380   double change_in_fst = vnl_math_max( factors[0],
00381                                        1/factors[0] );
00382   double change_in_snd = vnl_math_max( factors[1],
00383                                        1/factors[1] );
00384 
00385   double scaling = vnl_math_max( change_in_fst, change_in_snd );
00386   //double scaling = vcl_sqrt(scaling_sqr);
00387 
00388   return scaling;
00389 
00390 #if 0 // commented out
00391   // The new formulation, measures the changes of the ratio of te 2nd
00392   // moments
00393   double ratio_from = ev_fst_from/ev_snd_from;
00394   double ratio_mapped = ev_fst_mapped/ev_snd_mapped;
00395   double distortion = vnl_math_max( ratio_from/ratio_mapped,
00396                                     ratio_mapped/ratio_from );
00397 
00398   return distortion;
00399 #endif // 0
00400 }
00401 
00402 double
00403 rgrl_util_geometric_error_scaling( rgrl_set_of<rgrl_match_set_sptr> const& current_match_sets )
00404 {
00405   vnl_vector<double> factors;
00406   bool success = rgrl_util_geometric_scaling_factors( current_match_sets, factors );
00407   if ( !success )
00408     return 0.0;
00409 
00410   // Estimate the change in the spread of the feature set
00411   //
00412   double change_in_fst = vnl_math_max( factors[0],
00413                                        1/factors[0] );
00414   double change_in_snd = vnl_math_max( factors[1],
00415                                        1/factors[1] );
00416 
00417   double scaling = vnl_math_max( change_in_fst, change_in_snd );
00418 
00419   return scaling;
00420 }
00421 
00422 bool
00423 rgrl_util_geometric_scaling_factors( rgrl_match_set const& match_set,
00424                                      vnl_vector<double>& factors )
00425 {
00426   typedef rgrl_match_set::const_from_iterator FIter;
00427 
00428   if ( match_set.from_size() == 0 ) return false; //geometric scaling not significant
00429 
00430   // The dimensionality of the space we are working in. Find it by
00431   // looking at the dimension of one of the data points.
00432   //
00433   unsigned int m = match_set.from_begin().from_feature()->location().size();
00434 
00435   // Compute the centers of the from_feature_set and the mapped_feature_set
00436   //
00437   vnl_vector<double> from_centre( m, 0.0 );
00438   vnl_vector<double> mapped_centre( m, 0.0 );
00439   for ( FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi ) {
00440     from_centre += fi.from_feature()->location();
00441     mapped_centre += fi.mapped_from_feature()->location();
00442   }
00443   from_centre /= match_set.from_size();
00444   mapped_centre /=  match_set.from_size();
00445 
00446   // Compute the covariance matrices
00447   //
00448   vnl_matrix<double> cov_matrix_from(m,m,0.0);
00449   vnl_matrix<double> cov_matrix_mapped(m,m,0.0);
00450   for ( FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi ) {
00451     cov_matrix_from +=
00452       outer_product(fi.from_feature()->location() - from_centre,
00453                     fi.from_feature()->location() - from_centre);
00454     cov_matrix_mapped +=
00455       outer_product(fi.mapped_from_feature()->location() - mapped_centre,
00456                     fi.mapped_from_feature()->location() - mapped_centre);
00457   }
00458   if( match_set.from_size()<m+1 ) 
00459     return false;
00460 
00461   cov_matrix_from /= match_set.from_size();
00462   cov_matrix_mapped /=  match_set.from_size();
00463 
00464   // Perform SVD to get the 1st and 2nd eigenvalues.
00465   //
00466   vnl_svd<double> svd_from (cov_matrix_from );
00467   vnl_svd<double> svd_mapped (cov_matrix_mapped );
00468 
00469   double sv_from, sv_mapped;
00470   factors.set_size( m );
00471   for ( unsigned i=0; i<m; ++i ) {
00472     sv_from = vcl_sqrt( vnl_math_max( svd_from.W(i), 1e-16 ) );
00473     sv_mapped = vcl_sqrt( vnl_math_max( svd_mapped.W(i), 1e-16 ) );
00474     factors[i] = sv_mapped / sv_from;
00475   }
00476 
00477   return true;
00478 }
00479 
00480 bool
00481 rgrl_util_geometric_scaling_factors( rgrl_set_of<rgrl_match_set_sptr> const& current_match_sets,
00482                                      vnl_vector<double>& factors )
00483 {
00484   typedef rgrl_match_set::const_from_iterator FIter;
00485 
00486   if ( current_match_sets.size() == 0 ) return false; //geometric scaling not significant
00487 
00488   // The dimensionality of the space we are working in. Find it by
00489   // looking at the dimension of one of the data points.
00490   //
00491   unsigned int i=0;
00492   while ( current_match_sets[i]->from_size() == 0 && i<current_match_sets.size() )
00493     ++i;
00494   if ( i==current_match_sets.size() )  return false;
00495 
00496   // get the dimension
00497   const unsigned int from_dim = current_match_sets[i]->from_begin().from_feature()->location().size();
00498   const unsigned int mapped_dim = current_match_sets[i]->from_begin().mapped_from_feature()->location().size();
00499   
00500   if( from_dim != mapped_dim ) {
00501 
00502     // cannot compute scaling factors between two sets of data that have different dimensions
00503     factors.set_size(0);
00504     return true;  // pretend it is a success
00505   }
00506 
00507   // now, start computing the scatter matrix
00508   const unsigned int m = from_dim;
00509 
00510   // Compute the centers of the from_feature_set and the mapped_feature_set
00511   //
00512   vnl_vector<double> from_centre( m, 0.0 );
00513   vnl_vector<double> mapped_centre( m, 0.0 );
00514   unsigned num = 0;
00515   for (unsigned int i=0; i<current_match_sets.size(); ++i) {
00516     rgrl_match_set const& match_set = *(current_match_sets[i]);
00517     num += match_set.from_size();
00518     for ( FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi ) {
00519       from_centre += fi.from_feature()->location();
00520       mapped_centre += fi.mapped_from_feature()->location();
00521     }
00522   }
00523   if( num<m+1 ) 
00524     return false;
00525   
00526   from_centre /= double(num);
00527   mapped_centre /=  double(num);
00528 
00529   // Compute the covariance matrices
00530   //
00531   vnl_matrix<double> cov_matrix_from(m,m,0.0);
00532   vnl_matrix<double> cov_matrix_mapped(m,m,0.0);
00533   for (unsigned int i=0; i<current_match_sets.size(); ++i) {
00534     rgrl_match_set const& match_set = *(current_match_sets[i]);
00535     for ( FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi ) {
00536       cov_matrix_from +=
00537         outer_product(fi.from_feature()->location() - from_centre,
00538                       fi.from_feature()->location() - from_centre);
00539       cov_matrix_mapped +=
00540         outer_product(fi.mapped_from_feature()->location() - mapped_centre,
00541                       fi.mapped_from_feature()->location() - mapped_centre);
00542     }
00543   }
00544   cov_matrix_from /= double(num);
00545   cov_matrix_mapped /=  double(num);
00546 
00547   // Perform SVD to get the 1st and 2nd eigenvalues.
00548   //
00549   vnl_svd<double> svd_from (cov_matrix_from );
00550   vnl_svd<double> svd_mapped (cov_matrix_mapped );
00551 
00552   double sv_from, sv_mapped;
00553   factors.set_size( m );
00554   for ( unsigned i=0; i<m; ++i ) {
00555     sv_from = vnl_math_max( svd_from.W(i), 1e-16 );
00556     sv_mapped = vnl_math_max( svd_mapped.W(i), 1e-16 );
00557     // As the scatter matrix essentially squared the 
00558     // underlying scaling factors, 
00559     // take square-root to get the real factor
00560     factors[i] = vcl_sqrt( sv_mapped / sv_from );
00561   }
00562 
00563   return true;
00564 }
00565 
00566 
00567 void
00568 rgrl_util_extract_region_locations( vnl_vector< double >             const& center,
00569                                     vcl_vector< vnl_vector<double> > const& basis_dirs,
00570                                     vnl_vector< double >             const& basis_radii,
00571                                     vcl_vector< vnl_vector<int> >         & pixel_locations )
00572 {
00573   //  0. Get the image dimension, clear the pixels, and make sure
00574   //  everyone agrees on the sizes.
00575   unsigned int dimension = center.size();
00576   pixel_locations.clear();
00577   assert( center.size() == basis_dirs.size() );
00578   assert( center.size() == basis_dirs[0].size() );
00579   assert( center.size() == basis_radii.size() );
00580 
00581   //  1. We need to get the bounding box in axis-aligned coordinates.  We
00582   //  therefore need to get the corners of the rotated box in these
00583   //  coordinates...
00584 
00585   //  1a. We start by forming a vector of corner points.  There will be
00586   //  2^dimension corners.
00587 
00588   vcl_vector< vnl_vector<double> > corner_points;
00589   int num_corners = vnl_math_rnd( vcl_exp( dimension * vcl_log(2.0) ));
00590   corner_points.reserve( num_corners );
00591 
00592   //  1b. Since the dimension is computed dynamically, we can't do the
00593   //  obvious thing of forming for loops, with one loop per dimension.
00594   //  Instead we build up the corner vectors one dimension at a time.
00595   //  Each dimension is built by taking the partially-built vectors
00596   //  from the previous dimensions and adding +/- r * dir to each
00597   //  vector.  This doubles the number of vectors stored in each
00598   //  iteration of the outer loop.  This is an iterative version of
00599   //  what would normally be at least designed as a recursive
00600   //  procedure.
00601 
00602 //   vnl_vector<double> zero_vector( dimension, 0.0 );
00603 //   corner_points.push_back( zero_vector );
00604   corner_points.push_back( center );
00605 
00606   for ( unsigned int i=0; i<dimension; ++i )
00607   {
00608     // 1b(i). For each current / partially built corner vector, create a
00609     // new corner by adding r * dir, then add -r * dir to the
00610     // current vector.
00611 
00612     vnl_vector<double> offset = basis_dirs[i] * basis_radii[i];
00613     unsigned curr_size = corner_points.size();
00614     for ( unsigned int j=0; j<curr_size; ++j )
00615     {
00616       corner_points.push_back( corner_points[j] + offset );
00617       corner_points[j] -= offset;
00618     }
00619   }
00620 
00621   //  1c. Form a bounding box by taking the min and max over the
00622   //  corners.  At this point, the bounding box is in centered
00623   //  coordinates, not in absolute coordinates.
00624 
00625   vnl_vector<double> lower = center;
00626   vnl_vector<double> upper = center;
00627 
00628   for ( unsigned int i=0; i<corner_points.size(); ++i )
00629   {
00630     for ( unsigned int j=0; j<dimension; ++j )
00631     {
00632       if ( lower[j] > corner_points[i][j] )
00633         lower[j] = corner_points[i][j];
00634       if ( upper[j] < corner_points[i][j] )
00635         upper[j] = corner_points[i][j];
00636     }
00637   }
00638 
00639   //  2. Ok, folks, time to build the intervals of possible points.
00640   //  How to do this generalized over dimension?  An interval is an
00641   //  axis-aligned line segment.  Each line segments has dimension-1
00642   //  coordinates fixed.  Arbitrarily, we'll decide that these
00643   //  coordinates are the first dimension-1 coordinates.  Therefore,
00644   //  our first goal in building the intervals is to enumerate all
00645   //  possible combinations of dimension-1 coordinates from the
00646   //  bounding boxes.  As examples, in 2d this  will contain just the
00647   //  possible x values (lower[0] .. lower[1]), in 3d this will
00648   //  contain all possible x,y values.
00649 
00650   //  2a. Find an approximate upper bound on the number of
00651   //  intervals.  This is used for nothing more than to reserve space
00652   //  in a vector.
00653 
00654   int max_num_intervals = 1;
00655   for ( unsigned int i=0; i < dimension-1; ++i )
00656     max_num_intervals *= (int)vcl_floor( upper[i] - lower[i] + 1 );
00657 
00658   //  2b. Allocate a vector of interval indices.  Note that in each
00659   //  of these, the last component will be 0.0.  It is important that
00660   //  it stay this way for the computation below (step 3).  Also the
00661   //  interval_indices will be stored as doubles even though they are
00662   //  really integers.  This just simplifies some computation.
00663 
00664   vcl_vector< vnl_vector<double> > interval_indices;
00665   interval_indices.reserve( max_num_intervals );
00666 
00667   //  2c. The procedure is similar to 1b above.  We are after all
00668   //  combinations of the possible indices from the first dimension-1
00669   //  axis-aligned coordinates.  Once we have these, in step 3 we will
00670   //  build the intervals by finding the min and max bounds on the
00671   //  line segments as they intersect the rotated rectangular
00672   //  structure.  (Some will not.)  In the actual procedure, the set
00673   //  of coordinate combinations is built up one dimension at a time.
00674   //  In each step, the integer values in the bounding box (remember,
00675   //  it is axis-aligned) are combined with each in the previous
00676   //  dimension.
00677 
00678   //   vnl_vector< double > zero_vect( dimension, 0.0 );  // basis
00679   //   interval_indices.push_back( zero );
00680   interval_indices.push_back( center );
00681 
00682   for ( unsigned int i=0; i < dimension-1; ++i )
00683   {
00684     // round or floor/ceil?
00685     //       int lower_index = vnl_math_rnd( lower[i] );
00686     //       int upper_index = vnl_math_rnd( upper[i] );
00687     int lower_index = (int)vcl_ceil( lower[i] );
00688     int upper_index = (int)vcl_floor( upper[i] );
00689     int prev_size = interval_indices.size();
00690 
00691     //  2c(i).  For the current dimension, expand the set of
00692     //  coordinates of the axis-aligned line segments.  Go through
00693     //  each
00694     for ( int prev=0; prev < prev_size; ++prev )
00695     {
00696       vnl_vector< double > partial_interval = interval_indices[prev];
00697       interval_indices[ prev ][ i ] = double(lower_index);
00698       for ( int index = lower_index + 1; index <= upper_index; ++index )
00699       {
00700         partial_interval[ i ] = double(index);  // double for convenience
00701         interval_indices.push_back( partial_interval );
00702       }
00703     }
00704   }
00705 
00706   //  3. Now, we are ready to get the region and the pixel locations
00707   //  within the region.  For each interval, compute the min and max
00708   //  value of the last coordinate.  If it includes any pixels, record
00709   //  these pixel locations and their associated intensities.
00710 
00711   for ( unsigned int i=0; i < interval_indices . size(); ++i )
00712   {
00713     vnl_vector<double> & interval = interval_indices[i];
00714 
00715     //  3a. Initialize the bounds to the outer bounds of the
00716     //  rectangle.  The computation will tighten these bounds.
00717     double min_z = lower[ dimension-1 ], max_z = upper[ dimension-1 ];
00718 
00719     //  3b. Go through each of the basis vectors for the region
00720     //  (oriented).  For each of these, compute the bounds and use
00721     //  them to narrow the bounds on the line segment.  In effect,
00722     //  we are intersecting the line with the two infinite planes in
00723     //  direction + and - the basis vector, at distance r.  Overall,
00724     //  remember that we are computing the intersection between the
00725     //  infinite line aligned with the first dimension-1 axes at
00726     //  location "interval" with the oriented rectangular solid.
00727 
00728     for ( unsigned int basis_index=0; basis_index<dimension; ++basis_index )
00729     {
00730       // for calculating max_z and min_z correctly, let bdir[ dimension - 1 ] >= 0.
00731       vnl_vector< double > bdir =
00732         ( basis_dirs[ basis_index ][ dimension - 1 ] < 0 ) ?
00733         basis_dirs[ basis_index ] / -1 : basis_dirs[ basis_index ];
00734       double bradius = basis_radii[ basis_index ];
00735 
00736       //  3b(i). If the last dimension of this basis vector is
00737       //  nearly 0, then this basis vector will have little effect
00738       //  on the interval.  Ignore it to avoid problems caused by
00739       //  numerical issues.
00740 
00741       if ( vnl_math_abs( bdir[ dimension-1 ] ) <= 1.0E-6 ) continue;
00742 
00743       //  3b(ii).  Project the interval, which has a 0 in the last
00744       //  dimension, onto the basis vector.
00745 
00746       double projection = dot_product( interval - center, bdir );
00747 
00748       //  3c(iii). Solve for the min_end based on this basis vector.  If
00749       //  this is larger than the current min_z, then update the
00750       //  min.  Do the same for the max_end / max_z.
00751 
00752       double basis_min_end = ( -bradius - projection ) / bdir[ dimension-1 ] + center[ dimension - 1 ] ;
00753       if ( min_z < basis_min_end ) min_z = basis_min_end;
00754       double basis_max_end = ( bradius - projection ) / bdir[ dimension-1 ] + center[ dimension - 1 ] ;
00755       if ( max_z > basis_max_end ) max_z = basis_max_end;
00756     }
00757 
00758     //  3d. If the interval can not contain any points (even after
00759     //  rounding), skip the rest of the computation and go on to the
00760     //  next interval.
00761     //       if ( max_z < min_z-1 ) continue;
00762 
00763     //  3e. Form the first dimension-1 components of the pixel index by
00764     //  adding the center location.
00765 
00766     vnl_vector< int > pixel_index( dimension, 0 );
00767     // change to integer
00768     for ( unsigned int d=0; d<dimension-1; ++d ) {
00769       pixel_index[d] = (int)interval[d] ;
00770       assert( pixel_index[ d ] == interval[ d ] );
00771     }
00772 
00773     //  3f. Go through the interval specified by the range of last
00774     //  components to form the final, complete set of indices for
00775     //  this line segment.
00776 
00777     // round or floor/ceil?
00778     //       int last_lower_bound = vnl_math_rnd( min_z + center[ dimension-1 ] );
00779     //       int last_upper_bound = vnl_math_rnd( max_z + center[ dimension-1 ] );
00780     int last_lower_bound = (int)vcl_ceil( min_z );
00781     int last_upper_bound = (int)vcl_floor( max_z );
00782     for ( int last_component = last_lower_bound; last_component <= last_upper_bound;
00783           ++ last_component )
00784     {
00785       pixel_index[ dimension - 1 ] = last_component;
00786       pixel_locations.push_back( pixel_index );
00787     }
00788   }
00789 }
00790 
00791 bool
00792 rgrl_util_irls( rgrl_match_set_sptr              match_set,
00793                 rgrl_scale_sptr                  scale,
00794                 rgrl_weighter_sptr               weighter,
00795                 rgrl_convergence_tester   const& conv_tester,
00796                 rgrl_estimator_sptr              estimator,
00797                 rgrl_transformation_sptr       & estimate,
00798                 const bool                       fast_remapping,
00799                 unsigned int                     debug_flag )
00800 {
00801   rgrl_set_of<rgrl_match_set_sptr> match_sets;
00802   match_sets.push_back( match_set );
00803   rgrl_set_of<rgrl_scale_sptr> scales;
00804   scales.push_back( scale );
00805   vcl_vector<rgrl_weighter_sptr> weighters;
00806   weighters.push_back(weighter);
00807 
00808   return rgrl_util_irls(match_sets, scales, weighters,
00809                         conv_tester, estimator, estimate, false, // no fast mapping
00810                         debug_flag);
00811 }
00812 
00813 bool
00814 rgrl_util_irls( rgrl_set_of<rgrl_match_set_sptr> const& match_sets,
00815                 rgrl_set_of<rgrl_scale_sptr>     const& scales,
00816                 vcl_vector<rgrl_weighter_sptr>   const& weighters,
00817                 rgrl_convergence_tester          const& conv_tester,
00818                 rgrl_estimator_sptr              estimator,
00819                 rgrl_transformation_sptr&        estimate,
00820                 const bool                       fast_remapping,
00821                 unsigned int                     debug_flag )
00822 {
00823   DebugFuncMacro( debug_flag, 1, " In irls for model "<<estimator->transformation_type().name()<<'\n' );
00824 
00825   typedef rgrl_match_set::from_iterator  from_iter;
00826   typedef from_iter::to_iterator         to_iter;
00827 
00828   unsigned int iteration = 0;
00829   unsigned int max_iterations = 25;
00830   bool failed = false;
00831   vnl_vector<double> scaling;
00832 
00833   // for iterative method, set relative thres to be 1/10 of the value
00834   // for IRLS
00835   if( estimator->is_iterative_method() ) {
00836     
00837     rgrl_nonlinear_estimator* nonlinear_est 
00838       = dynamic_cast<rgrl_nonlinear_estimator*>( estimator.as_pointer() );
00839     if( nonlinear_est ) {
00840       
00841       // set tolerance to be 1/10th of the current value
00842       nonlinear_est->set_rel_thres( conv_tester.rel_tol() / 10.0 );
00843       // also not too use too many iterations, 
00844       // as the geometric weights change accordingly.
00845       nonlinear_est->set_max_num_iter( 15 );  
00846     }
00847   }
00848 
00849   //  Basic loop:
00850   //  1. Calculate new estimate
00851   //  2. Map matches and calculate weights
00852   //  3. Test for convergence.
00853   //
00854 
00855   // initialize the weights for the first estimation
00856   rgrl_converge_status_sptr current_status = conv_tester.initialize_status( estimate, estimator, scales[0], false );
00857 
00858   for ( unsigned ms=0; ms < match_sets.size(); ++ms ) {
00859     rgrl_match_set_sptr match_set = match_sets[ms];
00860     if ( match_set && match_set->from_size() > 0) {
00861       
00862       match_set->remap_from_features( *estimate );
00863       
00864       weighters[ms]->compute_weights( *scales[ms], *match_set );
00865     }
00866   }
00867   do {
00868     // Step 1.  Calculate new estimate.
00869     //
00870     rgrl_transformation_sptr
00871       new_estimate = estimator->estimate(match_sets, *estimate);
00872     if ( !new_estimate ) {
00873       estimate = 0;
00874       DebugFuncMacro( debug_flag, 1, "*** irls failed!\n" );
00875       return failed;
00876     }
00877 
00878     // Step 1.5 Update scaling factors in transformation
00879     if ( rgrl_util_geometric_scaling_factors( match_sets, scaling ) )
00880       new_estimate->set_scaling_factors( scaling );
00881     else {
00882       // use the previous ones
00883       new_estimate->set_scaling_factors( estimate->scaling_factors() );
00884       vcl_cout << "WARNING in " << __FILE__ << __LINE__ << "cannot compute scaling factors!!!" << vcl_endl;
00885     }
00886     
00887     //  Step 2.  Map matches and calculate weights
00888     //
00889     for ( unsigned ms=0; ms < match_sets.size(); ++ms ) {
00890       rgrl_match_set_sptr match_set = match_sets[ms];
00891       if ( match_set && match_set->from_size() > 0) {
00892 
00893         // if fast mapping is on, map only the locations
00894         // 
00895         if( fast_remapping )
00896           match_set->remap_only_location( *new_estimate );
00897         else
00898           match_set->remap_from_features( *new_estimate );
00899         
00900         weighters[ms]->compute_weights( *scales[ms], *match_set );
00901       }
00902     }
00903 
00904     //  Step 3.  Test for convergence. The alignment error is not
00905     //  scaled by the distortion.
00906     //
00907     current_status = conv_tester.compute_status( current_status,
00908                                                  new_estimate, estimator,
00909                                                  match_sets, scales, false);
00910 
00911     DebugFuncMacro_abv(debug_flag, 2, "irls: (iteration = " << iteration
00912                        << ") oscillation count = " << current_status->oscillation_count() << '\n' );
00913     DebugFuncMacro_abv(debug_flag, 2, "irls: error = " << current_status->error() << vcl_endl );
00914     DebugFuncMacro_abv(debug_flag, 2, "irls: error_diff = " << current_status->error_diff() << vcl_endl );
00915     DebugFuncMacro_abv(debug_flag, 2, "irls: converged = " << current_status->has_converged() << vcl_endl );
00916 
00917     estimate = new_estimate;
00918     ++ iteration;
00919   } while ( !current_status->has_converged() &&
00920             !current_status->has_stagnated() &&
00921             !current_status->is_failed() &&
00922             iteration < max_iterations );
00923 
00924   DebugFuncMacro_abv(debug_flag, 1, "irls status: " <<
00925                      ( current_status->has_converged() ?
00926                        "converged\n" : current_status->has_stagnated() ?
00927                                        "stagnated\n" : current_status->is_failed() ?
00928                                                        "failed\n" :"reaches max iteration\n" ) );
00929   
00930   // re-map the features if fast mapping is on,
00931   // to ensure expected behavior in other components
00932   if( fast_remapping )
00933     for ( unsigned ms=0; ms < match_sets.size(); ++ms ) {
00934 
00935       rgrl_match_set_sptr match_set = match_sets[ms];
00936       if ( match_set && match_set->from_size() ) {
00937         
00938         match_set->remap_from_features( *estimate );
00939       }
00940     }
00941 
00942   return !failed;
00943 }
00944 
00945 //: skip empty lines in input stream
00946 void
00947 rgrl_util_skip_empty_lines( vcl_istream& is )
00948 {
00949   vcl_streampos pos;
00950   vcl_string str;
00951   static const vcl_string white_chars = " \t\r";
00952   vcl_string::size_type non_empty_pos;
00953 
00954   // skip any empty lines
00955   do {
00956     // store current reading position
00957     pos = is.tellg();
00958     str = "";
00959     vcl_getline( is, str );
00960 
00961     non_empty_pos = str.find_first_not_of( white_chars );
00962   } while ( is.good() && !is.eof() && (str.empty() || non_empty_pos == std::string::npos ||
00963             (str[non_empty_pos]=='#') ) );
00964 
00965   // back to the beginning of non-empty line
00966   is.seekg( pos );
00967 }