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