contrib/rpl/rgrl/rgrl_invariant_single_landmark.cxx
Go to the documentation of this file.
00001 #include "rgrl_invariant_single_landmark.h"
00002 #include "rgrl_trans_similarity.h"
00003 #include "rgrl_scale.h"
00004 #include "rgrl_cast.h"
00005 
00006 #include <vcl_vector.h>
00007 #include <vcl_algorithm.h>
00008 #include <vcl_cassert.h>
00009 
00010 #include <vnl/vnl_math.h>
00011 #include <vnl/vnl_inverse.h>
00012 #include <vnl/vnl_double_4.h>
00013 #include <vnl/vnl_double_2.h>
00014 #include <vnl/vnl_vector.h>
00015 #include <vnl/vnl_matrix_fixed.h>
00016 
00017 rgrl_invariant_single_landmark::
00018 rgrl_invariant_single_landmark(vnl_vector<double> location,
00019                                vnl_vector<double> vessel_dir1,
00020                                vnl_vector<double> vessel_dir2,
00021                                vnl_vector<double> vessel_dir3,
00022                                double width1, double width2, double width3,
00023                                double angular_std,
00024                                double width_ratio_std):
00025   location_(location),
00026   center_set_(false),
00027   is_estimate_set_(false)
00028 {
00029   vcl_vector< vnl_vector<double> > vessel_dirs;
00030   vessel_dirs.push_back(vessel_dir1);
00031   vessel_dirs.push_back(vessel_dir2);
00032   vessel_dirs.push_back(vessel_dir3);
00033 
00034   local_widths_.push_back(width1);
00035   local_widths_.push_back(width2);
00036   local_widths_.push_back(width3);
00037 
00038   // Compute the radius of the region that the landmark occupies
00039   //
00040   radius_ = vnl_math_max(width1, vnl_math_max(width2, width3));
00041 
00042   // reorder the vessels directions, so that they're ordered in
00043   // increasing counter-clock-wise angles from the x-axis.
00044   //
00045   vcl_vector<double> angles;
00046   reorder_vessel(vessel_dirs, local_widths_, angles);
00047 
00048   // If the base trace direction is close to 0 degrees (below the threshold)
00049   // then this landmark is ambiguous
00050   //
00051   if ( angles[0] < angular_std*2)
00052     is_ambiguous_ = true;
00053 
00054   // Set boundary_points_ and trace_normals_ with the new vessel
00055   // ordering.
00056   //
00057   for (unsigned i = 0; i<3; i++ ) {
00058     vnl_double_2 normal = vnl_double_2(-vessel_dirs[i][1], vessel_dirs[i][0]);
00059     vnl_double_2 trace_pt = location_+radius_*vessel_dirs[i];
00060 
00061     boundary_points_.push_back( trace_pt-normal*local_widths_[i]/2.0 );
00062     boundary_points_.push_back( trace_pt+normal*local_widths_[i]/2.0 );
00063     trace_normals_.push_back(normal);
00064   }
00065 
00066   // Compute the invariants
00067   //
00068   angular_invariants_.set_size(3);
00069   angular_invariants_[0] = angles[0];
00070   angular_invariants_[1] = angles[1];
00071   angular_invariants_[2] = angles[2];
00072 
00073   // local_widths_ should not contain zero entries
00074   cartesian_invariants_.set_size(2);
00075   cartesian_invariants_[0] = vcl_atan(local_widths_[0]/local_widths_[1])*angular_std/width_ratio_std;
00076   cartesian_invariants_[1] = vcl_atan(local_widths_[0]/local_widths_[2])*angular_std/width_ratio_std;
00077 }
00078 
00079 rgrl_invariant_single_landmark::
00080 rgrl_invariant_single_landmark(const rgrl_invariant_single_landmark& copy,
00081                                double angular_std,
00082                                double width_ratio_std)
00083   : rgrl_invariant(copy),
00084     location_(copy.location_),
00085     radius_(copy.radius_),
00086     center_set_(false)
00087 {
00088   // copy the normal, width, and boundary points of each trace in the
00089   // signature the index of each is shifted by one
00090   for (unsigned int i = 0; i<3; i++) {
00091     trace_normals_.push_back(copy.trace_normals_[(i+1)%3]);
00092     local_widths_.push_back(copy.local_widths_[(i+1)%3]);
00093     boundary_points_.push_back(copy.boundary_points_[((i+1)%3)*2]);
00094     boundary_points_.push_back( copy.boundary_points_[((i+1)%3)*2+1]);
00095   }
00096   //compute invariants
00097   angular_invariants_.set_size(3);
00098   angular_invariants_[0] = copy.angular_invariants_[1];
00099   angular_invariants_[1] = copy.angular_invariants_[2];
00100   angular_invariants_[2] = copy.angular_invariants_[0];
00101 
00102   cartesian_invariants_.set_size(2);
00103   cartesian_invariants_[0] = vcl_atan(local_widths_[0]/local_widths_[1])*angular_std/width_ratio_std;
00104   cartesian_invariants_[1] = vcl_atan(local_widths_[0]/local_widths_[2])*angular_std/width_ratio_std;
00105 }
00106 
00107 const vnl_double_2&
00108 rgrl_invariant_single_landmark::
00109 location() const
00110 {
00111   return location_;
00112 }
00113 
00114 const vnl_double_2&
00115 rgrl_invariant_single_landmark::
00116 boundary_point_location(int i) const
00117 {
00118   assert(i >= 0 && unsigned(i)<boundary_points_.size());
00119   return boundary_points_[i];
00120 }
00121 
00122 const vnl_double_2&
00123 rgrl_invariant_single_landmark::
00124 boundary_point_normal(int i) const
00125 {
00126   assert(i >= 0 && unsigned(i)<boundary_points_.size());
00127   return trace_normals_[i/2];
00128 }
00129 
00130 const vnl_vector<double>&
00131 rgrl_invariant_single_landmark::
00132 cartesian_invariants() const
00133 {
00134   return cartesian_invariants_;
00135 }
00136 
00137 const vnl_vector<double>&
00138 rgrl_invariant_single_landmark::
00139 angular_invariants() const
00140 {
00141   return angular_invariants_;
00142 }
00143 
00144 rgrl_mask_box
00145 rgrl_invariant_single_landmark::
00146 region() const
00147 {
00148   double ratio = 5;
00149   vnl_double_2 x0(location_[0] - ratio*radius_, location_[1] - ratio*radius_);
00150   vnl_double_2 x1(location_[0] + ratio*radius_, location_[1] + ratio*radius_);
00151 
00152   return rgrl_mask_box(x0.as_ref(), x1.as_ref());
00153 }
00154 
00155 bool
00156 rgrl_invariant_single_landmark::
00157 estimate(rgrl_invariant_sptr         from_inv,
00158          rgrl_transformation_sptr&   xform,
00159          rgrl_scale_sptr&            scale )
00160 {
00161   rgrl_invariant_single_landmark* from = rgrl_cast<rgrl_invariant_single_landmark*>(from_inv);
00162 
00163   // Form the lhs and rhs matrices from the centered points.
00164   // Only compute the upper triangular part of the lhs matrix, then
00165   //  copy the results to the lower part to form the symmetrical matrix
00166   vnl_matrix_fixed<double,4,4> sum_prod(0.0);
00167   vnl_matrix_fixed<double,4,1> sum_rhs(0.0);
00168 
00169   unsigned int i=0;
00170   unsigned int num_boundary_points = 6;
00171   while (i < num_boundary_points) {
00172     double px, py, nx, ny, qx, qy;
00173     nx = this->boundary_point_normal(i)[0];
00174     ny = this->boundary_point_normal(i)[1];
00175 
00176     px = from->boundary_point_location(i)[0] - from->center()[0];
00177     py = from->boundary_point_location(i)[1] - from->center()[1];
00178 
00179     qx = this->boundary_point_location(i)[0] - this->center()[0];
00180     qy = this->boundary_point_location(i)[1] - this->center()[1];
00181 
00182     ++i;
00183 
00184     double elt0 = nx;
00185     double elt1 = ny;
00186     double elt2 = px*nx + py*ny;
00187     double elt3 = -py*nx + px*ny;
00188     sum_prod(0,0)+=elt0*elt0;     sum_prod(0,1)+=elt0*elt1;
00189     sum_prod(0,2)+=elt0*elt2;     sum_prod(0,3)+=elt0*elt3;
00190     sum_prod(1,1)+=elt1*elt1;     sum_prod(1,2)+=elt1*elt2;
00191     sum_prod(1,3)+=elt1*elt3;     sum_prod(2,2)+=elt2*elt2;
00192     sum_prod(2,3)+=elt2*elt3;     sum_prod(3,3)+=elt3*elt3;
00193 
00194     double u = nx*qx + ny*qy;
00195     sum_rhs(0,0)+=u*elt0;
00196     sum_rhs(1,0)+=u*elt1;
00197     sum_rhs(2,0)+=u*elt2;
00198     sum_rhs(3,0)+=u*elt3;
00199   }
00200 
00201   //for landmark center location(s)
00202   double px = from->location()[0] - from->center()[0];
00203   double py = from->location()[1] - from->center()[1];
00204 
00205   sum_prod(0,0)+=1;               sum_prod(0,1)+=0;
00206   sum_prod(0,2)+=px;              sum_prod(0,3)+=-py;
00207   sum_prod(1,1)+=1;               sum_prod(1,2)+=py;
00208   sum_prod(1,3)+=px;              sum_prod(2,2)+=px*px+py*py;
00209   sum_prod(2,3)+=0;               sum_prod(3,3)+=px*px+py*py;
00210 
00211   double qx = this->location()[0] - this->center()[0];
00212   double qy = this->location()[1] - this->center()[1];
00213 
00214   sum_rhs(0,0)+=qx;
00215   sum_rhs(1,0)+=qy;
00216   sum_rhs(2,0)+=px*qx+py*qy;
00217   sum_rhs(3,0)+=-py*qx+px*qy;
00218 
00219   // Fill in the lhs below the main diagonal.
00220   for ( int i=1; i<4; ++i )
00221     for ( int j=0; j<i; ++j )
00222       sum_prod(i,j) = sum_prod(j,i);
00223 
00224 
00225   // Find the scales and scale the matrices appropriate to normalize
00226   // them and increase the numerical stability.
00227   double factor0 = vnl_math_max(sum_prod(0,0),sum_prod(1,1));
00228   double factor1 = vnl_math_max(sum_prod(2,2),sum_prod(3,3));
00229   double norm_scale = vcl_sqrt(factor1 / factor0);   // neither should be 0
00230 
00231   vnl_double_4 s;
00232   s(2) = s(3) = 1; s(0) = s(1) = norm_scale;
00233   for ( int i=0; i<4; i++ ) {
00234     sum_rhs(i,0) *= s(i);
00235     for ( int j=0; j<4; j++ )
00236       sum_prod(i,j) *= s(i) * s(j);
00237   }
00238 
00239   // Estimate the transform
00240   vnl_matrix_fixed<double,4,4> norm_covar = vnl_inverse(sum_prod);
00241   vnl_matrix_fixed<double,4,1> c_params = norm_covar * sum_rhs;
00242 
00243   // Eliminate the scale of the sum_prod
00244   //
00245   for ( int i=0; i<4; i++ ) {
00246     for ( int j=0; j<4; j++ )
00247       norm_covar(i,j) *= s(i) * s(j);
00248   }
00249   for ( int i=0; i<4; i++ ) {
00250     c_params(i,0) *= s(i);
00251   }
00252 
00253   // Eliminate the center
00254   //
00255   double tx = c_params(0,0)+this->center()[0]
00256               -c_params(2,0)*from->center()[0]
00257               +c_params(3,0)*from->center()[1];
00258   double ty = c_params(1,0)+this->center()[1]
00259               -c_params(3,0)*from->center()[0]
00260               -c_params(2,0)*from->center()[1];
00261 
00262   // Initial estimate of the transform (without proper scaling on the
00263   // covar matrix)
00264   //
00265   vnl_double_2 trans(tx, ty);
00266   vnl_matrix<double> A(2,2);
00267   A(0,0) = A(1,1) = c_params(2,0);
00268   A(0,1) = -c_params(3,0);
00269   A(1,0) = -A(0,1);
00270   rgrl_trans_similarity est_xform(A, trans.as_ref(), norm_covar.as_ref());
00271 
00272   // Compute the scale
00273   //
00274   double obj = 0;
00275   double geometric_scale = 0;
00276   vnl_vector<double> mapped;
00277   for (unsigned int i = 0; i< num_boundary_points; i++) {
00278     est_xform.map_location(from->boundary_point_location(i).as_ref(), mapped);
00279     obj += vnl_math_sqr( dot_product((this->boundary_point_location(i) - mapped),
00280                                      this->boundary_point_normal(i)) );
00281   }
00282   est_xform.map_location(from->location().as_ref(), mapped);
00283   obj += (location_ - mapped).squared_magnitude();
00284   //n = 8 for number of constraints
00285   //k = 4 for dof
00286   //geometric_scale = vcl_sqrt(obj/(n-k));
00287   geometric_scale = vcl_sqrt(obj/4);
00288 
00289   // Set the return parameters
00290   //
00291   xform = new rgrl_trans_similarity( est_xform.A(), est_xform.t(),
00292                                      est_xform.covar()*vnl_math_sqr(geometric_scale) );
00293   scale = new rgrl_scale;
00294   scale->set_geometric_scale( geometric_scale );
00295 
00296   return is_estimate_set_ = true;
00297 }
00298 
00299 const vnl_double_2&
00300 rgrl_invariant_single_landmark::
00301 center()
00302 {
00303   if ( center_set_ ) return center_;
00304 
00305   //update center_
00306   double xc = location_[0];
00307   double yc = location_[1];
00308 
00309   for (unsigned int i = 0; i< boundary_points_.size(); i++) {
00310     xc += boundary_points_[i][0];
00311     yc += boundary_points_[i][1];
00312   }
00313 
00314   int size = int(boundary_points_.size() + 1);
00315   xc /= size; yc /= size;
00316   center_[0] = xc;
00317   center_[1] = yc;
00318   center_set_ = true;
00319 
00320   return center_;
00321 }
00322 
00323 //------------ Non-member Functions ----------------------------
00324 void
00325 rgrl_invariant_single_landmark::
00326 reorder_vessel(vcl_vector<vnl_vector<double> >& directions,
00327                vcl_vector<double>& local_widths,
00328                vcl_vector<double>& angles)
00329 {
00330   // create a basis for the x-axis
00331   vnl_double_2 x_axis(1, 0);
00332 
00333   // make a copy of the old stuff
00334   vcl_vector<vnl_vector<double> > old_dirs = directions;
00335   vcl_vector<double> old_widths = local_widths;
00336   directions.clear();
00337   local_widths.clear();
00338 
00339   // calculate the counterclockwise angle between the basis and each
00340   // trace direction.
00341   angles.clear();
00342   for (int i = 0; i<3; ++i)
00343     angles.push_back( ccw_angle_between(x_axis, old_dirs[i]) );
00344 
00345   // make a copy of the angles before sorting, and sort the angles
00346   vcl_vector<double> old_angles(angles);
00347   vcl_sort(angles.begin(), angles.end());
00348 
00349   // re-assign the directions
00350   for (int i=0; i<3; ++i){
00351     for (int j=0; j<3; ++j){
00352       if (angles[i]==old_angles[j]){
00353         directions.push_back(old_dirs[j]);
00354         local_widths.push_back(old_widths[j]);
00355         break;
00356       }
00357     }
00358   }
00359 }
00360 
00361 double
00362 rgrl_invariant_single_landmark::
00363 ccw_angle_between(vnl_double_2 from, vnl_double_2 to)
00364 {
00365   double cosine = (from[0]*to[0] + from[1]*to[1])/
00366                   (from.magnitude()*to.magnitude()); //normalize
00367   double angle = vcl_acos(cosine);
00368   double z = from[0]*to[1] - from[1]*to[0];
00369   if (z >= 0)
00370     return angle;
00371   else
00372     return vnl_math::pi*2 - angle;
00373 }