1 #ifndef rgrl_est_proj_rad_func_txx_
2 #define rgrl_est_proj_rad_func_txx_
3 //:
4 // \file
5 // \author Gehua Yang
6 // \date   March 2007
7 // \brief  a generic class to estimate a homogeneous projection matrix with radial distortion parameter(s)  using L-M
8 
9 #include <iostream>
10 #include <vector>
11 #include "rgrl_est_proj_rad_func.h"
12 
13 #include <rgrl/rgrl_est_proj_func.hxx>
14 #include <rgrl/rgrl_estimator.h>
15 #include <rgrl/rgrl_match_set.h>
16 #include <rgrl/rgrl_set_of.h>
17 
18 #include <vnl/vnl_matrix_fixed.h>
19 #include <vnl/vnl_vector_fixed.h>
20 #include <vnl/vnl_math.h>
21 #include <vnl/vnl_transpose.h>
22 #include <vnl/algo/vnl_levenberg_marquardt.h>
23 #include <vnl/algo/vnl_svd.h>
24 
25 #ifdef _MSC_VER
26 #  include <vcl_msvc_warnings.h>
27 #endif
28 #include <cassert>
29 
30 template <unsigned int Tdim, unsigned int Fdim>
31 rgrl_est_proj_rad_func<Tdim, Fdim>::
rgrl_est_proj_rad_func(rgrl_set_of<rgrl_match_set_sptr> const & matches,unsigned int camera_dof,bool with_grad)32 rgrl_est_proj_rad_func( rgrl_set_of<rgrl_match_set_sptr> const& matches,
33                         unsigned int camera_dof,
34                         bool with_grad )
35 : rgrl_est_proj_func<Tdim, Fdim>( matches, with_grad ),
36   camera_dof_(camera_dof),
37   image_centre_(double(0)),
38   centre_mag_norm_const_(0)
39 {
40   //modify the dof in vnl_least_squares_function
41   vnl_least_squares_function::init(this->proj_size_-1+camera_dof_,
42                                    this->get_number_of_residuals() );
43 
44   // temperary storage space
45   temp_rad_k_.resize( camera_dof_ );
46 }
47 
48 template <unsigned int Tdim, unsigned int Fdim>
49 rgrl_est_proj_rad_func<Tdim, Fdim>::
rgrl_est_proj_rad_func(unsigned int camera_dof,bool with_grad)50 rgrl_est_proj_rad_func( unsigned int camera_dof,
51                         bool with_grad )
52 : rgrl_est_proj_func<Tdim, Fdim>( with_grad ),
53   camera_dof_(camera_dof),
54   image_centre_(double(0)),
55   centre_mag_norm_const_(0)
56 {
57   //modify the dof in vnl_least_squares_function
58   vnl_least_squares_function::init(this->proj_size_-1+camera_dof_,
59                                    this->get_number_of_residuals() );
60 
61   // temperary storage space
62   temp_rad_k_.resize( camera_dof_ );
63 }
64 
65 //: convert parameters
66 template <unsigned int Tdim, unsigned int Fdim>
67 void
68 rgrl_est_proj_rad_func<Tdim, Fdim>::
convert_parameters(vnl_vector<double> & params,vnl_matrix_fixed<double,Tdim+1,Fdim+1> const & proj_matrix,std::vector<double> const & rad_dist,vnl_vector_fixed<double,Fdim> const & fc,vnl_vector_fixed<double,Tdim> const & tc,vnl_vector_fixed<double,Tdim> const & camera_centre)69 convert_parameters( vnl_vector<double>& params,
70                     vnl_matrix_fixed<double, Tdim+1, Fdim+1>const&  proj_matrix,
71                     std::vector<double>             const& rad_dist,
72                     vnl_vector_fixed<double, Fdim> const& fc,
73                     vnl_vector_fixed<double, Tdim> const& tc,
74                     vnl_vector_fixed<double, Tdim> const& camera_centre )
75 {
76   // get params from the projection matrix
77   vnl_vector<double> proj_params;
78   rgrl_est_proj_func<Tdim, Fdim>::convert_parameters( proj_params, proj_matrix, fc, tc );
79 
80   // check camera dof
81   assert( rad_dist.size() == camera_dof_ );
82 
83   // copy to new params
84   params.set_size( proj_params.size() + rad_dist.size() );
85   for ( unsigned i=0; i<proj_params.size(); ++i )
86     params[i] = proj_params[i];
87   for ( unsigned i=0; i<rad_dist.size(); ++i )
88     params[i+proj_params.size()] = rad_dist[i];
89 
90   // set camera centre
91   set_image_centre( camera_centre );
92 }
93 
94 template <unsigned int Tdim, unsigned int Fdim>
95 void
96 rgrl_est_proj_rad_func<Tdim, Fdim>::
transfer_radial_params_into_temp_storage(vnl_vector<double> const & params) const97 transfer_radial_params_into_temp_storage( vnl_vector<double> const& params ) const
98 {
99   assert( params.size() + 1 == this->proj_size_ + camera_dof_ );
100   const unsigned int index_shift = this->proj_size_-1;
101   for ( unsigned i=0; i<camera_dof_; ++i )
102     temp_rad_k_[i] = params[index_shift+i];
103 }
104 
105 //: convert parameters
106 template <unsigned int Tdim, unsigned int Fdim>
107 inline
108 void
109 rgrl_est_proj_rad_func<Tdim, Fdim>::
apply_radial_distortion(vnl_vector_fixed<double,Tdim> & mapped,vnl_vector_fixed<double,Tdim> const & p,std::vector<double> const & radk) const110 apply_radial_distortion( vnl_vector_fixed<double, Tdim>      & mapped,
111                          vnl_vector_fixed<double, Tdim> const& p,
112                          std::vector<double> const& radk ) const
113 {
114   assert( radk.size() == camera_dof_ );
115 
116   vnl_vector_fixed<double, Tdim> centred = p-image_centre_;
117   const double radial_dist = centred.squared_magnitude() / centre_mag_norm_const_;
118 
119   double base = 1;
120   double coeff = 0;
121   for (double i : radk) {
122     base *= radial_dist;
123     coeff += i * base;
124   }
125   mapped = p + coeff*centred;
126 }
127 
128 template <unsigned int Tdim, unsigned int Fdim>
129 void
130 rgrl_est_proj_rad_func<Tdim, Fdim>::
reduced_proj_rad_jacobian(vnl_matrix<double> & base_jac,vnl_matrix_fixed<double,Tdim+1,Fdim+1> const & proj,std::vector<double> const & rad_k,vnl_vector_fixed<double,Fdim> const & from) const131 reduced_proj_rad_jacobian( vnl_matrix<double>                            & base_jac,
132                            vnl_matrix_fixed<double, Tdim+1, Fdim+1> const& proj,
133                            std::vector<double>                       const& rad_k,
134                            vnl_vector_fixed<double, Fdim>           const& from ) const
135 {
136   assert( rad_k.size() == camera_dof_ );
137 
138   const unsigned proj_dof = this->proj_size_ -1;
139   const unsigned param_size = proj_dof + camera_dof_;
140 
141   // 0. set size
142   base_jac.set_size( Tdim, param_size );
143 
144   // 1. get projection matrix jacobian
145   vnl_matrix_fixed<double, Tdim, (Tdim+1)*(Fdim+1)-1> dP_dp;
146   base_type::reduced_proj_jacobian( dP_dp, proj, from );
147 
148   // 2. gradient w.r.t to mapped location
149   vnl_matrix_fixed<double, Tdim, Tdim >  dD_dx;
150   vnl_vector_fixed<double, Tdim> mapped;
151   rgrl_est_proj_map_inhomo_point<Tdim, Fdim>( mapped, proj, from-this->from_centre_ );
152 
153   const vnl_vector_fixed<double, Tdim> centred = mapped-image_centre_;
154   const double radial_dist = centred.squared_magnitude() / centre_mag_norm_const_;
155 
156   // compute radial distortion coefficient
157   double base = 1;
158   double coeff = 0;
159   for (double i : rad_k) {
160     base *= radial_dist;
161     coeff += i * base;
162   }
163 
164   // two part computation for dD_dx
165   // first part
166   dD_dx.set_identity();
167   dD_dx *= (1+coeff);
168 
169   // second part, taking gradient on the squared radial distance
170   base = 2 / centre_mag_norm_const_;
171   for ( unsigned k=0; k<rad_k.size(); ++k )
172   {
173     const double base_coeff = double(k+1)*base*rad_k[k];
174 
175     //upper triangular
176     for ( unsigned i=0; i<Tdim; ++i )
177       for ( unsigned j=i; j<Tdim; ++j ) {
178         dD_dx( i, j ) += base_coeff*centred[i]*centred[j];
179       }
180 
181     // multiplication is at the end of loop,
182     // because in gradient it is to the power of (k-1), not k
183     base *= radial_dist;
184   }
185 
186   // fill in lower triangular
187   for ( unsigned i=0; i<Tdim; ++i )
188     for ( unsigned j=i; j<Tdim; ++j )
189       dD_dx( j, i ) = dD_dx( i, j );
190 
191 
192   // 3. fill in base_jac
193   dP_dp = dD_dx * dP_dp;
194 
195   for ( unsigned i=0; i<Tdim; ++i )
196     for ( unsigned j=0; j<dP_dp.cols(); ++j )
197       base_jac( i, j ) = dP_dp( i, j );
198 
199   // 3. gradient w.r.t to k
200   base = 1;
201   for ( unsigned k=0; k<camera_dof_; ++k ) {
202     const unsigned index = k+proj_dof;
203     base *= radial_dist;
204 
205     for ( unsigned i=0; i<Tdim; ++i )
206       base_jac( i, index ) = centred[i] * base;
207   }
208 }
209 
210 template <unsigned int Tdim, unsigned int Fdim>
211 void
212 rgrl_est_proj_rad_func<Tdim, Fdim>::
full_proj_rad_jacobian(vnl_matrix<double> & base_jac,vnl_matrix_fixed<double,Tdim+1,Fdim+1> const & proj,std::vector<double> const & rad_k,vnl_vector_fixed<double,Fdim> const & from) const213 full_proj_rad_jacobian( vnl_matrix<double>                            & base_jac,
214                         vnl_matrix_fixed<double, Tdim+1, Fdim+1> const& proj,
215                         std::vector<double>                       const& rad_k,
216                         vnl_vector_fixed<double, Fdim>           const& from ) const
217 {
218   assert( rad_k.size() == camera_dof_ );
219 
220   const unsigned param_size = this->proj_size_ + camera_dof_;
221 
222   // 0. set size
223   base_jac.set_size( Tdim, param_size );
224 
225   // 1. get projection matrix jacobian
226   vnl_matrix_fixed<double, Tdim, (Tdim+1)*(Fdim+1)> dP_dp;
227   base_type::full_proj_jacobian( dP_dp, proj, from );
228 
229   // 2. gradient w.r.t to mapped location
230   vnl_matrix_fixed<double, Tdim, Tdim >  dD_dx;
231   vnl_vector_fixed<double, Tdim> mapped;
232   rgrl_est_proj_map_inhomo_point<Tdim, Fdim>( mapped, proj, from-this->from_centre_ );
233 
234   const vnl_vector_fixed<double, Tdim> centred = mapped-image_centre_;
235   const double radial_dist = centred.squared_magnitude() / centre_mag_norm_const_;
236 
237   // compute radial distortion coefficient
238   double base = 1;
239   double coeff = 0;
240   for (double i : rad_k) {
241     base *= radial_dist;
242     coeff += i * base;
243   }
244 
245   // two part computation for dD_dx
246   // first part
247   dD_dx.set_identity();
248   dD_dx *= (1+coeff);
249 
250   // second part, taking gradient on the squared radial distance
251   base = 2 / centre_mag_norm_const_;
252   for ( unsigned k=0; k<rad_k.size(); ++k )
253   {
254     const double base_coeff = double(k+1)*base*rad_k[k];
255 
256     //upper triangular
257     for ( unsigned i=0; i<Tdim; ++i )
258       for ( unsigned j=i; j<Tdim; ++j ) {
259         dD_dx( i, j ) += base_coeff*centred[i]*centred[j];
260       }
261 
262     // multiplication is at the end of loop,
263     // because in gradient it is to the power of (k-1), not k
264     base *= radial_dist;
265   }
266 
267   // fill in lower triangular
268   for ( unsigned i=0; i<Tdim; ++i )
269     for ( unsigned j=i; j<Tdim; ++j )
270       dD_dx( j, i ) = dD_dx( i, j );
271 
272 
273   // 3. fill in base_jac
274   dP_dp = dD_dx * dP_dp;
275 
276   for ( unsigned i=0; i<Tdim; ++i )
277     for ( unsigned j=0; j<dP_dp.cols(); ++j )
278       base_jac( i, j ) = dP_dp( i, j );
279 
280   // 3. gradient w.r.t to k
281   base = 1;
282   for ( unsigned k=0; k<camera_dof_; ++k ) {
283     const unsigned index = k+this->proj_size_;
284     base *= radial_dist;
285 
286     for ( unsigned i=0; i<Tdim; ++i )
287       base_jac( i, index ) = centred[i] * base;
288   }
289 }
290 
291 //: compute jacobian w.r.t. location
292 template <unsigned int Tdim, unsigned int Fdim>
293 void
294 rgrl_est_proj_rad_func<Tdim, Fdim>::
proj_jac_wrt_loc(vnl_matrix_fixed<double,Tdim,Fdim> & jac_loc,vnl_matrix_fixed<double,Tdim+1,Fdim+1> const & proj,std::vector<double> const & rad_k,vnl_vector_fixed<double,Fdim> const & from) const295 proj_jac_wrt_loc( vnl_matrix_fixed<double, Tdim, Fdim>          & jac_loc,
296                   vnl_matrix_fixed<double, Tdim+1, Fdim+1> const& proj,
297                   std::vector<double>                       const& rad_k,
298                   vnl_vector_fixed<double, Fdim>           const& from ) const
299 {
300   // dP / dx
301   vnl_matrix_fixed<double, Tdim, Fdim> dP_dx;
302   rgrl_est_proj_func<Tdim, Fdim>::proj_jac_wrt_loc( dP_dx, proj, from );
303 
304   // 2. gradient w.r.t to mapped location
305   vnl_matrix_fixed<double, Tdim, Tdim >  dD_dx;
306   vnl_vector_fixed<double, Tdim> mapped;
307   rgrl_est_proj_map_inhomo_point<Tdim, Fdim>( mapped, proj, from-this->from_centre_ );
308 
309   const vnl_vector_fixed<double, Tdim> centred = mapped-image_centre_;
310   const double radial_dist = centred.squared_magnitude() / centre_mag_norm_const_;
311 
312   // compute radial distortion coefficient
313   double base = 1;
314   double coeff = 0;
315   for (double i : rad_k) {
316     base *= radial_dist;
317     coeff += i * base;
318   }
319 
320   // two part computation for dD_dx
321   // first part
322   dD_dx.set_identity();
323   dD_dx *= (1+coeff);
324 
325   // second part, taking gradient on the squared radial distance
326   base = 2 / centre_mag_norm_const_;
327   for ( unsigned k=0; k<rad_k.size(); ++k )
328   {
329     const double base_coeff = double(k+1)*base*rad_k[k];
330 
331     //upper triangular
332     for ( unsigned i=0; i<Tdim; ++i )
333       for ( unsigned j=i; j<Tdim; ++j ) {
334         dD_dx( i, j ) += base_coeff*centred[i]*centred[j];
335       }
336 
337     // multiplication is at the end of loop,
338     // because in gradient it is to the power of (k-1), not k
339     base *= radial_dist;
340   }
341 
342   // fill in lower triangular
343   for ( unsigned i=0; i<Tdim; ++i )
344     for ( unsigned j=i; j<Tdim; ++j )
345       dD_dx( j, i ) = dD_dx( i, j );
346 
347   // final jac
348   jac_loc = dD_dx * dP_dx;
349 }
350 
351 template <unsigned int Tdim, unsigned int Fdim>
352 void
353 rgrl_est_proj_rad_func<Tdim, Fdim>::
f(vnl_vector<double> const & x,vnl_vector<double> & fx)354 f(vnl_vector<double> const& x, vnl_vector<double>& fx)
355 {
356   typedef rgrl_match_set::const_from_iterator FIter;
357   typedef FIter::to_iterator TIter;
358 
359   vnl_vector_fixed<double, Tdim> distorted;
360   vnl_matrix_fixed<double, Tdim,Tdim> error_proj_sqrt;
361   unsigned int ind = 0;
362 
363   // retrieve the projection matrix
364   vnl_matrix_fixed<double, Tdim+1, Fdim+1> proj;
365   base_type::restored_centered_proj( proj, x );
366 
367   // retrieve the radial distortion parameters
368   transfer_radial_params_into_temp_storage( x );
369 
370   for ( unsigned ms = 0; ms<this->matches_ptr_->size(); ++ms )
371   {
372     if ( (*this->matches_ptr_)[ms] != nullptr ) // if pointer is valid
373     {
374       rgrl_match_set const& one_set = *((*this->matches_ptr_)[ms]);
375       for ( FIter fi=one_set.from_begin(); fi!=one_set.from_end(); ++fi ) {
376         // map from point
377         vnl_vector_fixed<double, Fdim> from = fi.from_feature()->location();
378         map_loc( distorted, proj, temp_rad_k_, from );
379 
380         for ( TIter ti=fi.begin(); ti!=fi.end(); ++ti ) {
381           vnl_vector_fixed<double, Tdim> to = ti.to_feature()->location();
382           error_proj_sqrt = ti.to_feature()->error_projector_sqrt();
383           double const wgt = std::sqrt(ti.cumulative_weight());
384           vnl_vector_fixed<double, Tdim> diff = error_proj_sqrt * (distorted - to);
385 
386           // fill in
387           for ( unsigned i=0; i<Tdim; ++i )
388             fx(ind++) = wgt * diff[i];
389         }
390       }
391     }
392   }
393 
394   // check
395   assert( ind == this->get_number_of_residuals() );
396 }
397 
398 template <unsigned int Tdim, unsigned int Fdim>
399 void
400 rgrl_est_proj_rad_func<Tdim, Fdim>::
gradf(vnl_vector<double> const & x,vnl_matrix<double> & jacobian)401 gradf(vnl_vector<double> const& x, vnl_matrix<double>& jacobian)
402 {
403   typedef rgrl_match_set::const_from_iterator FIter;
404   typedef FIter::to_iterator TIter;
405 
406   assert( jacobian.rows() == this->get_number_of_residuals() );
407   assert( jacobian.cols()+1 == this->proj_size_+camera_dof_ );
408 
409   vnl_matrix_fixed<double, Tdim,   Tdim>        error_proj_sqrt;
410   vnl_matrix<double> base_jac, jac;
411 
412   // retrieve the projection matrix
413   vnl_matrix_fixed<double, Tdim+1, Fdim+1> proj;
414   base_type::restored_centered_proj( proj, x );
415 
416   // retrieve the radial distortion parameters
417   transfer_radial_params_into_temp_storage( x );
418 
419   unsigned int ind = 0;
420   for ( unsigned ms = 0; ms<this->matches_ptr_->size(); ++ms )
421   {
422     if ( (*this->matches_ptr_)[ms] ) // if pointer is valid
423     {
424       rgrl_match_set const& one_set = *((*this->matches_ptr_)[ms]);
425       for ( FIter fi=one_set.from_begin(); fi!=one_set.from_end(); ++fi )
426       {
427         // map from point
428         vnl_vector_fixed<double, Fdim> from = fi.from_feature()->location();
429 
430         // jacobian computation
431         reduced_proj_rad_jacobian( base_jac, proj, temp_rad_k_, from );
432 
433         for ( TIter ti=fi.begin(); ti!=fi.end(); ++ti ) {
434           //vnl_double_2 to = ti.to_feature()->location();
435           error_proj_sqrt = ti.to_feature()->error_projector_sqrt();
436           double const wgt = std::sqrt(ti.cumulative_weight());
437           jac = error_proj_sqrt * base_jac;
438           jac *= wgt;
439 
440           // fill in
441           for ( unsigned i=0; i<Tdim; ++i,++ind ) {
442             for ( unsigned j=0; j<jac.cols(); ++j )
443               jacobian(ind,j) = jac(i, j);
444           }
445         }
446       }
447     }
448   }
449 }
450 
451 template <unsigned int Tdim, unsigned int Fdim>
452 bool
453 rgrl_est_proj_rad_func<Tdim, Fdim>::
projective_estimate(vnl_matrix_fixed<double,Tdim+1,Fdim+1> & proj,std::vector<double> & rad_dist,vnl_matrix<double> & full_covar,vnl_vector_fixed<double,Fdim> & from_centre,vnl_vector_fixed<double,Tdim> & to_centre,vnl_vector_fixed<double,Tdim> const & camera_centre)454 projective_estimate(  vnl_matrix_fixed<double, Tdim+1, Fdim+1>& proj,
455                       std::vector<double>& rad_dist,
456                       vnl_matrix<double>& full_covar,
457                       vnl_vector_fixed<double, Fdim>& from_centre,
458                       vnl_vector_fixed<double, Tdim>& to_centre,
459                       vnl_vector_fixed<double, Tdim> const& camera_centre)
460 {
461   // compute weighted centres
462   // this function is going to resize the vnl_vector, use temporary ones instead
463   vnl_vector<double> fc, tc;
464   if ( !rgrl_est_compute_weighted_centres( *(this->matches_ptr_), fc, tc ) ) {
465     return false;
466   }
467   assert( fc.size() == from_centre.size() );
468   assert( tc.size() == to_centre.size() );
469   assert( rad_dist.size() == camera_dof_ );
470   from_centre = fc;
471   to_centre = tc;
472 
473   // convert parameters
474   vnl_vector<double> p;
475   this->convert_parameters( p, proj, rad_dist, from_centre, to_centre, camera_centre );
476 
477   vnl_levenberg_marquardt lm( *this );
478   //lm.set_trace( true );
479   //lm.set_check_derivatives( 1 );
480   // we don't need it to be super accurate
481   lm.set_f_tolerance( this->relative_threshold_ );
482   lm.set_max_function_evals( this->max_num_iterations_ );
483 
484   bool ret;
485   if ( this->has_gradient() )
486     ret = lm.minimize_using_gradient(p);
487   else
488     ret = lm.minimize_without_gradient(p);
489   if ( !ret ) {
490     std::cerr <<  "Levenberg-Marquardt failed\n";
491     lm.diagnose_outcome(std::cerr);
492     return false;
493   }
494   // lm.diagnose_outcome(std::cout);
495 
496   // convert parameters back into matrix form
497   this->restored_centered_proj( proj, p );
498   //std::cout << "Final params=" << proj << std::endl;
499 
500   // copy distortion parameters
501   const unsigned int index_shift = this->proj_size_-1;
502   for ( unsigned i=0; i<camera_dof_; ++i )
503     rad_dist[i] = p[ i+index_shift ];
504 
505   // compute covariance
506   // Jac^\top * Jac is INVERSE of the covariance
507   //
508   const unsigned int param_num = this->proj_size_+camera_dof_;
509   vnl_matrix<double> jac( this->get_number_of_residuals(), param_num-1 );
510   this->gradf( p, jac );
511   //
512 
513   // SVD decomposition:
514   // Jac = U W V^\top
515   vnl_svd<double> svd( jac, this->zero_svd_thres_ );
516   if ( svd.rank()+1 < param_num ) {
517     std::cerr <<  "The covariance of projection matrix ranks less than "
518              << param_num-1 << "!\n"
519              << "  The singular values are " << svd.W() << std::endl;
520     return false;
521   }
522 
523   //invert the W matrix and square it
524   vnl_diag_matrix<double> invW( param_num-1 );
525   for ( unsigned i=0; i+1<param_num; ++i )
526     invW[i] = vnl_math::sqr( 1.0/svd.W(i) );
527 
528   //compute inverse of Jac^\top * Jac
529   const vnl_matrix<double>  covar( svd.V() * invW * vnl_transpose( svd.V() ) );
530 
531   // convert the covar to full dof+1 matrix
532   // fill in the row and column of the fixed element
533   // with 0
534   full_covar.set_size( param_num, param_num );
535   full_covar.fill( 0.0 );
536 
537   const unsigned int param_index
538     = this->index_row_*(Fdim+1)
539     + this->index_col_;
540   for ( unsigned i=0,ic=0; i<param_num; ++i,++ic ) {
541     if ( i==param_index ) { --ic; continue; }
542     for ( unsigned j=0,jc=0; j<param_num; ++j,++jc ) {
543       if ( j==param_index ) { --jc; continue; }
544       full_covar( i, j ) = covar( ic, jc );
545     }
546   }
547 
548   return true;
549 }
550 
551 
552 #undef  RGRL_EST_PROJ_RAD_FUNC_INSTANTIATE
553 #define RGRL_EST_PROJ_RAD_FUNC_INSTANTIATE( tdim, fdim ) \
554   template class rgrl_est_proj_rad_func< tdim, fdim >
555 
556 #endif //rgrl_est_proj_rad_func_txx_
557