1 //:
2 // \file
3 // \author Lee, Ying-Lin (Bess)
4 // \date   Sept 2003
5 
6 #include <iostream>
7 #include <utility>
8 #include "rgrl_est_spline.h"
9 #include "rgrl_cast.h"
10 #include "rgrl_match_set.h"
11 #include "rgrl_spline.h"
12 #include "rgrl_trans_spline.h"
13 #include <cassert>
14 #ifdef _MSC_VER
15 #  include "vcl_msvc_warnings.h"
16 #endif
17 #include <vnl/algo/vnl_amoeba.h>
18 #include <vnl/algo/vnl_conjugate_gradient.h>
19 #include <vnl/algo/vnl_lbfgs.h>
20 #include <vnl/algo/vnl_levenberg_marquardt.h>
21 #include <vnl/algo/vnl_powell.h>
22 #include <vnl/algo/vnl_svd.h>
23 #include "vnl/vnl_cost_function.h"
24 #include "vnl/vnl_least_squares_function.h"
25 #include "vnl/vnl_math.h"
26 #include "vnl/vnl_vector.h"
27 #include "vul/vul_timer.h"
28 
29 namespace{
30   // for Levenberg Marquardt
31   struct spline_least_squares_func : public vnl_least_squares_function
32   {
spline_least_squares_func__anon1f16c7c80111::spline_least_squares_func33     spline_least_squares_func(
34         const rgrl_spline_sptr &spline,
35         std::vector<vnl_vector<double>> const &pts,
36         vnl_diag_matrix<double>
37             wgt, // ( num of residuals ) x ( num of residuals )
38         vnl_vector<double> displacement, // ( num of residuals ) x 1
39         std::vector<unsigned> const &free_control_pt_index)
40         : vnl_least_squares_function(free_control_pt_index.size(), pts.size(),
41                                      use_gradient),
42           // number of unknowns, number of residuals, has gradient function or
43           // not
44           spline_(spline), pts_(pts), wgt_(std::move(wgt)),
45           displacement_(std::move(displacement)),
46           free_control_pt_index_(free_control_pt_index) {
47       assert( pts.size() == wgt.rows() );
48       assert( displacement.size() == wgt.rows() );
49     }
50 
51     // x is the parameters
f__anon1f16c7c80111::spline_least_squares_func52     void f( vnl_vector< double > const& x, vnl_vector< double > & fx ) override
53     {
54       // x is the dof-reduced parameters. Convert it back to control points
55       assert( x.size() == free_control_pt_index_.size() );
56       vnl_vector< double > c( spline_->num_of_control_points(), 0.0 );
57       for ( unsigned i=0; i< free_control_pt_index_.size(); ++i )
58         c[ free_control_pt_index_[i] ] = x[ i ];
59 
60       spline_->set_control_points( c );
61       // check the number of residuals
62       assert( fx.size() == pts_.size() );
63       for ( unsigned i = 0; i < pts_.size(); ++i ) {
64         fx[ i ] = ( displacement_[ i ] - spline_->f_x( pts_[ i ] ) )
65           * std::sqrt( wgt_[ i ] );
66       }
67     }
68 
69     // x is the parameters
gradf__anon1f16c7c80111::spline_least_squares_func70     void gradf( vnl_vector< double > const& x, vnl_matrix< double > & jacobian ) override
71     {
72       assert( x.size() == free_control_pt_index_.size() );
73       vnl_vector< double > c( spline_->num_of_control_points(), 0.0 );
74       for ( unsigned i=0; i< free_control_pt_index_.size(); ++i )
75         c[ free_control_pt_index_[i] ] = x[ i ];
76 
77       spline_->set_control_points( c );
78       vnl_vector< double > gr;
79       for ( unsigned i = 0; i < pts_.size(); ++i ) {
80         spline_->basis_response( pts_[i], gr );
81         for ( unsigned j = 0; j < x.size(); ++j )
82           jacobian[ i ][ j ] = - gr[ free_control_pt_index_[j] ]  * std::sqrt( wgt_[ i ] );
83       }
84     }
85 
86    private:
87     rgrl_spline_sptr spline_;
88     std::vector< vnl_vector< double > >  pts_;
89     vnl_diag_matrix< double > wgt_;
90     vnl_vector< double > displacement_;
91     std::vector< unsigned > free_control_pt_index_;
92   };
93 
94   // for Conjugate Gradient and other optimizers
95   struct spline_cost_function : public vnl_cost_function
96   {
spline_cost_function__anon1f16c7c80111::spline_cost_function97     spline_cost_function(
98         const rgrl_spline_sptr &spline, std::vector<vnl_vector<double>> pts,
99         vnl_diag_matrix<double>
100             wgt, // ( num of residuals ) x ( num of residuals )
101         vnl_vector<double> displacement) // ( num of residuals ) x 1
102         : vnl_cost_function(
103               spline->num_of_control_points()), // number of unknowns
104           spline_(spline), pts_(std::move(pts)), wgt_(std::move(wgt)),
105           displacement_(std::move(displacement)) {
106       assert( pts.size() == wgt.rows() );
107       assert( displacement.size() == wgt.rows() );
108     }
109 
110     // x is the parameters
f__anon1f16c7c80111::spline_cost_function111     double f( vnl_vector< double > const& x ) override
112     {
113       double fx = 0;
114       spline_->set_control_points( x );
115       for ( unsigned i = 0; i < pts_.size(); ++i ) {
116         fx += vnl_math::sqr( displacement_[ i ] - spline_->f_x( pts_[ i ] ) )
117           * wgt_[ i ] ;
118       }
119       return fx;
120     }
121 
gradf__anon1f16c7c80111::spline_cost_function122     void gradf (vnl_vector< double > const & /*x*/, vnl_vector< double > &gradient ) override
123     {
124       gradient.fill( 0.0 );
125       vnl_vector< double > gr;
126       for ( unsigned i=0; i<pts_.size(); ++i ) {
127         spline_->basis_response( pts_[i], gr );
128         for ( unsigned j=0; j<spline_->num_of_control_points(); ++j ) {
129           gradient[ j ] -= 2 * ( displacement_[i] - spline_->f_x( pts_[i] ) ) * wgt_[i]* gr[j];
130         }
131       }
132     }
133 
134    private:
135     rgrl_spline_sptr spline_;
136     std::vector< vnl_vector< double > > pts_;
137     vnl_diag_matrix< double > wgt_;
138     vnl_vector< double > displacement_;
139   };
140 } // namespace
141 
rgrl_est_spline(unsigned dof,rgrl_mask_box roi,vnl_vector<double> delta,vnl_vector<unsigned> const & m,bool use_thin_plate,double lambda)142 rgrl_est_spline::rgrl_est_spline(unsigned dof, rgrl_mask_box roi,
143                                  vnl_vector<double> delta,
144                                  vnl_vector<unsigned> const &m,
145                                  bool use_thin_plate, double lambda)
146     : rgrl_nonlinear_estimator(dof), roi_(std::move(roi)),
147       delta_(std::move(delta)), m_(m), use_thin_plate_(use_thin_plate),
148       lambda_(lambda), optimize_method_(RGRL_LEVENBERG_MARQUARDT),
149       global_xform_(nullptr) {
150   unsigned num_control = 1;
151   for (unsigned int i : m)
152     num_control *= i + 3;
153 
154   assert( num_control == dof );
155 }
156 
rgrl_est_spline(unsigned dof,const rgrl_transformation_sptr & global_xform,rgrl_mask_box roi,vnl_vector<double> delta,vnl_vector<unsigned> const & m,bool use_thin_plate,double lambda)157 rgrl_est_spline::rgrl_est_spline(unsigned dof,
158                                  const rgrl_transformation_sptr &global_xform,
159                                  rgrl_mask_box roi, vnl_vector<double> delta,
160                                  vnl_vector<unsigned> const &m,
161                                  bool use_thin_plate, double lambda)
162     : rgrl_nonlinear_estimator(dof), roi_(std::move(roi)),
163       delta_(std::move(delta)), m_(m), use_thin_plate_(use_thin_plate),
164       lambda_(lambda), optimize_method_(RGRL_LEVENBERG_MARQUARDT),
165       global_xform_(global_xform) {
166   unsigned num_control = 1;
167   for (unsigned int i : m)
168     num_control *= i + 3;
169 
170   std::cerr << "rgrl_est_spline.cxx : number of control points: " << num_control << ", dof=" << dof << '\n';
171   assert( num_control == dof );
172 }
173 
174 void
175 rgrl_est_spline::
point_in_knots(vnl_vector<double> const & point,vnl_vector<double> & spline_pt) const176 point_in_knots( vnl_vector< double > const& point, vnl_vector< double > & spline_pt ) const
177 {
178   spline_pt.set_size( point.size() );
179   for ( unsigned i = 0; i < point.size(); ++i ) {
180     spline_pt[ i ] = point[ i ] / delta_[ i ];
181   }
182 }
183 
184 rgrl_transformation_sptr
185 rgrl_est_spline::
estimate(rgrl_set_of<rgrl_match_set_sptr> const & matches,rgrl_transformation const & cur_transform) const186 estimate( rgrl_set_of<rgrl_match_set_sptr> const& matches,
187           rgrl_transformation const& cur_transform ) const
188 {
189   typedef rgrl_match_set::const_from_iterator FIter;
190   typedef FIter::to_iterator TIter;
191 
192   unsigned dim = delta_.size();
193 
194   // Find the number of correspondences
195 //  vnl_vector<int> num( dim, 0 );
196   unsigned num_match = 0;
197   for ( unsigned ms=0; ms < matches.size(); ++ms ) {
198     rgrl_match_set const& match_set = *matches[ms];
199     for ( FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi ) {
200       vnl_vector<double> const& from_pt = fi.from_feature()->location();
201       // If the point is not inside the region of interest, skip it.
202       if ( roi_.inside( from_pt ) ) {
203         num_match += fi.size();
204       }
205     }
206   }
207 
208   std::vector< rgrl_spline_sptr > splines( dim );
209   if ( cur_transform.is_type( rgrl_trans_spline::type_id() ) ) {
210     auto const& cur_trans_spline = dynamic_cast< rgrl_trans_spline const& >(cur_transform);
211     std::cerr << "delta_: " << delta_ << '\n'
212              << "current transformation's delta_: " << cur_trans_spline.get_delta() << '\n';
213     if ( ( delta_ - cur_trans_spline.get_delta()/2 ).two_norm() < 1e-5 ) {
214       for ( unsigned i=0; i<dim; ++i ) {
215         splines[ i ] = cur_trans_spline.get_spline( i )->refinement( m_ );
216       }
217       DebugMacro(1, "###spline is initialized by refinement\n" );
218     }
219      else if ( ( delta_ - cur_trans_spline.get_delta() ).two_norm() < 1e-5 ) {
220        DebugMacro(1, "###spline is initialized by copying\n" );
221       for ( unsigned i=0; i<dim; ++i )
222         splines[ i ] = new rgrl_spline( *(cur_trans_spline.get_spline( i )) );
223     }
224     else {
225       DebugMacro(1, "create spline with m_=" << m_ << '\n');
226       for ( unsigned i=0; i<dim; ++i )
227         splines[ i ] = new rgrl_spline( m_ );
228     }
229   }
230   else {
231     DebugMacro(1, "create spline with m_=" << m_ << '\n' );
232     for ( unsigned i=0; i<dim; ++i )
233       splines[ i ] = new rgrl_spline( m_ );
234   }
235 
236   unsigned int num_control = splines[0]->num_of_control_points();
237 
238   vnl_diag_matrix<double> wgt( num_match, 0 );
239   vnl_matrix<double> displacement( num_match, dim );
240   vnl_matrix<double> g( num_match, num_control );
241 
242   // from points in the spline coordinates
243   vnl_vector< double > tmp( dim, 0.0 );
244   std::vector< vnl_vector< double > > from_pts_in_knots( num_match, tmp );
245 
246   // The index of control points that have constraints.
247   // Used for reducing the degree of freedom
248   std::vector< unsigned > free_control_pt_index;
249   // calculate weight, displacement for each match
250   {
251     unsigned i=0;
252     std::vector< double > score_constraint( num_control, 0.0 );
253     std::vector< bool > control_point_constraint( num_control, false );
254     for ( unsigned ms=0; ms < matches.size(); ++ms ) {
255       rgrl_match_set const& match_set = *matches[ms];
256       DebugMacro_abv(2, "rgrl_est_spline.cxx: from_pt \t to_pt \t displacement\n");
257       for ( FIter fi = match_set.from_begin(); fi != match_set.from_end(); ++fi ) {
258         vnl_vector<double> const& from_pt = fi.from_feature()->location();
259         // If the point is not inside the region of interest, skip it.
260         if (roi_.inside(from_pt) && !fi.empty()) {
261           // convert it into spline's coordinates
262           point_in_knots( from_pt, from_pts_in_knots[i] );
263 
264           vnl_vector< double > gr;
265 
266           splines[0]->basis_response( from_pts_in_knots[i], gr );
267 
268           // see on which control points the point gives constraints
269           for ( unsigned j=0; j<num_control; ++j ) {
270             if ( !control_point_constraint[ j ] ) { // && gr[j]>1e-4
271               score_constraint[ j ] += gr[j];
272               if ( score_constraint[j] > 1e-3 )
273                 control_point_constraint[ j ] = true;
274             }
275           }
276 
277           for ( TIter ti = fi.begin(); ti != fi.end(); ++ti ) {
278             if ( !global_xform_ ) {
279               displacement.set_row( i, ti.to_feature()->location() - from_pt );
280 
281               DebugMacro_abv(2, from_pt << " \t " << ti.to_feature()->location() << " \t " << displacement.get_row( i ) << '\n' );
282             }
283             else {
284               displacement.set_row( i, ti.to_feature()->location() - global_xform_->map_location( from_pt ) );
285 
286               DebugMacro_abv(2, global_xform_->map_location( from_pt ) << " \t " << ti.to_feature()->location()
287                                                                        << " \t " << displacement.get_row( i ) << '\n' );
288             }
289             g.set_row( i, gr );
290             wgt[i] = ti.cumulative_weight();
291             ++i;
292           }
293         }
294       }
295     }
296 
297     // recording which control points have constraints.
298     for ( unsigned i=0; i<num_control; ++i ) {
299       if ( control_point_constraint[ i ] )
300         free_control_pt_index.push_back( i );
301     }
302   }
303 
304   std::cerr << "\nafter reduce dof, dof=" << free_control_pt_index.size() << '\n';
305   DebugMacro( 1,  "\nafter reduce dof, dof=" << free_control_pt_index.size()<< std::endl );
306 
307   vul_timer timer;
308   // Levenberg Marquardt
309   if ( optimize_method_ == RGRL_LEVENBERG_MARQUARDT )
310   {
311     DebugMacro( 1, "Levenberg marquardt :\n" );
312     vnl_matrix< double > covar;
313 
314     if (this->debug_flag() > 1) {
315       std::cout << "rgrl_est_spline.cxx: displacement \t weight\n";
316       for ( unsigned i=0; i<displacement.rows(); ++i )
317         std::cout << i << "    " << displacement.get_row( i ) << " \t " << wgt[i] << '\n';
318     }
319 
320     for ( unsigned i = 0; i < dim ; ++i ) {
321       spline_least_squares_func f( splines[i], from_pts_in_knots, wgt, displacement.get_column( i ), free_control_pt_index );
322       vnl_levenberg_marquardt minimizer( f );
323       // initialization of c is important
324       vnl_vector< double > x( free_control_pt_index.size(), 0 );
325       vnl_vector< double > & c = splines[i]->get_control_points();
326       for ( unsigned j=0; j<x.size(); ++j )
327         x[ j ] = c[ free_control_pt_index[ j ] ];
328       if (this->debug_flag() > 1 ) timer.mark();
329       minimizer.minimize( x );
330       minimizer.diagnose_outcome(std::cout);
331       if (this->debug_flag() > 1 ) {
332         timer.print( std::cout );
333         std::cout << "computing covariance\n";
334         timer.mark();
335         if ( i==0 )
336           covar = minimizer.get_JtJ();
337         timer.print( std::cout );
338         std::cout << "covariance " << covar.rows() << 'x' << covar.columns() << std::endl;
339       }
340 
341       // Convert x back to control points
342 //      vnl_vector< double > c( splines[i]->num_of_control_points(), 0.0 );
343       for ( unsigned j=0; j<x.size(); ++j )
344         c[ free_control_pt_index[ j ] ] = x[ j ];
345       splines[i]->set_control_points( c );
346       DebugMacro( 1, "control points:\n" << c << std::endl );
347     }
348     return new rgrl_trans_spline( splines, vnl_vector<double>(dim,0.0), delta_, global_xform_ );
349   }
350   else if ( optimize_method_ == RGRL_CONJUGATE_GRADIENT ) {
351     DebugMacro( 1, "Conjugate Gradient\n" );
352     for ( unsigned i = 0; i < dim ; ++i ) {
353       spline_cost_function f( splines[i], from_pts_in_knots, wgt, displacement.get_column( i ) );
354       vnl_conjugate_gradient minimizer( f );
355 
356       // initialization of c is important
357       vnl_vector< double > c( splines[i]->num_of_control_points(), 0 );
358       minimizer.minimize( c );
359       splines[i]->set_control_points( c );
360       DebugMacro( 1, "control points:\n" << c << '\n' );
361       }
362     if ( this->debug_flag() > 1) timer.print( std::cout );
363     return new rgrl_trans_spline( splines, vnl_vector<double>(dim,0.0), delta_, global_xform_ );
364   }
365   else if ( optimize_method_ == RGRL_AMOEBA ) {
366     DebugMacro( 1, "Nelder-Meade downhill simplex (AMOEBA)\n" );
367     for ( unsigned i = 0; i < dim ; ++i ) {
368       spline_cost_function f( splines[i], from_pts_in_knots, wgt, displacement.get_column( i ) );
369       vnl_amoeba minimizer( f );
370 
371       // initialization of c is important
372       vnl_vector< double > c( splines[i]->num_of_control_points(), 0 );
373       minimizer.minimize( c );
374       splines[i]->set_control_points( c );
375       DebugMacro( 1, "control points:\n" << c << '\n' );
376     }
377     if (this->debug_flag() > 1) timer.print( std::cout );
378     return new rgrl_trans_spline( splines, vnl_vector<double>(dim,0.0), delta_, global_xform_ );
379   }
380   else if ( optimize_method_ == RGRL_POWELL ) {
381     DebugMacro( 1, "Powell's direction-set\n " );
382     for ( unsigned i = 0; i < dim ; ++i ) {
383       spline_cost_function f( splines[i], from_pts_in_knots, wgt, displacement.get_column( i ) );
384       vnl_powell minimizer( &f );
385 
386       // initialization of c is important
387       vnl_vector< double > c( splines[i]->num_of_control_points(), 0 );
388       minimizer.minimize( c );
389       splines[i]->set_control_points( c );
390       DebugMacro( 1, "control points:\n" << c << '\n' );
391     }
392     if (this->debug_flag() > 1) timer.print( std::cout );
393     return new rgrl_trans_spline( splines, vnl_vector<double>(dim,0.0), delta_, global_xform_ );
394   }
395   else if ( optimize_method_ == RGRL_LBFGS ) {
396     DebugMacro( 1, "LBFGS\n" );
397     for ( unsigned i = 0; i < dim ; ++i ) {
398       spline_cost_function f( splines[i], from_pts_in_knots, wgt, displacement.get_column( i ) );
399       vnl_lbfgs minimizer( f );
400 
401       // initialization of c is important
402       vnl_vector< double > c( splines[i]->num_of_control_points(), 0 );
403       minimizer.minimize( c );
404       splines[i]->set_control_points( c );
405       DebugMacro( 1, "control points:\n" << c << '\n' );
406     }
407     if (this->debug_flag()> 1) timer.print( std::cout );
408     return new rgrl_trans_spline( splines, vnl_vector<double>(dim,0.0), delta_, global_xform_ );
409   }
410   else {   //    // No approximation
411     // Without thin-plate constraints:
412     // Cost = (Z - G*C)^T * W * (Z - G*C)
413     // => C = (G^T * W * G)^{-1} * G^T * W * Z
414     // With thin-plate constraints:
415     // Cost = (Z - G*C)^T * W * (Z - G*C) + C^T * K * C,
416     // where K is the symmetric thin-plate regularization
417     // => C = (G^T * W * G + \lambda * K)^{-1} * G^T * W * Z
418     //DBG( std::cout << "No approximation\n" );
419     DebugMacro( 1, "No approximation\n" );
420 
421     vnl_matrix<double> X0;
422     vnl_matrix<double> covar;
423     // get the covariance
424     if ( use_thin_plate_ ) {
425       vnl_matrix<double> X1 = g.transpose() * wgt.as_matrix();
426       vnl_matrix<double> X3;
427       // covariance = sigma^2 * (G^T * W * G + \lambda * K)^{-1} * G^T * W^2 * G * (G^T *  W * G + \lambda * K)^{-T}
428       {
429         vnl_matrix<double> X2 = X1 * g;
430         vnl_matrix<double> k;
431         splines[0]->thin_plate_regularization(k);
432         vnl_svd<double> svd( X2 + lambda_ * k );
433         X3 = svd.inverse();
434       }
435       X0 = X3 * X1;
436       covar = X0 * X1.transpose() * X3.transpose();
437     }
438     else {
439       vnl_matrix<double> X1 = g.transpose() * wgt.as_matrix();
440       vnl_matrix<double> X3;
441       // covariance = sigma^2 * (G^T * W * G)^{-1} * G^T * W^2 * G * (G^T *  W * G)^{-T}
442       {
443         vnl_matrix<double> X2 = X1 * g;
444         vnl_svd<double> svd( X2 );
445         X3 = svd.inverse();
446       }
447       X0 = X3 * X1;
448       covar = X0 * X1.transpose() * X3.transpose();
449     }
450 
451     // Calculate the parameters of splines
452     for ( unsigned j = 0; j < dim ; ++j ) {
453       vnl_vector<double> c = displacement.get_column( j ).pre_multiply( X0 );
454       splines[j]->set_control_points(c);
455     }
456     if (this->debug_flag()>1) timer.print( std::cout );
457     return new rgrl_trans_spline( splines, vnl_vector<double>(dim,0.0), delta_, covar, global_xform_ );
458   }
459 }
460 
461 rgrl_transformation_sptr
462 rgrl_est_spline::
estimate(rgrl_match_set_sptr matches,rgrl_transformation const & cur_transform) const463 estimate( rgrl_match_set_sptr matches,
464           rgrl_transformation const& cur_transform ) const
465 {
466   // use base class implementation
467   return rgrl_estimator::estimate( matches, cur_transform );
468 }
469 
470 const std::type_info&
471 rgrl_est_spline::
transformation_type() const472 transformation_type() const
473 {
474   return rgrl_trans_spline::type_id();
475 }
476