contrib/oxl/mvl/HomgNorm2D.cxx
Go to the documentation of this file.
00001 // This is oxl/mvl/HomgNorm2D.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 
00008 #include "HomgNorm2D.h"
00009 #include <vcl_iostream.h>
00010 #include <vnl/vnl_math.h>
00011 #include <vgl/vgl_homg_point_2d.h>
00012 #include <vgl/algo/vgl_homg_operators_2d.h> // for matrix * vgl_homg_point_2d
00013 
00014 // ctor
00015 HomgNorm2D::HomgNorm2D(vcl_vector<vgl_homg_point_2d<double> > const& points, bool unit_omega):
00016   normalized_(points.size()),
00017   unit_omega_(unit_omega)
00018 {
00019   normalize(points);
00020 }
00021 
00022 HomgNorm2D::HomgNorm2D(const vcl_vector<HomgPoint2D>& points, bool unit_omega):
00023   normalized_(points.size()),
00024   unit_omega_(unit_omega)
00025 {
00026   normalize(points);
00027 }
00028 
00029 // Destructor
00030 HomgNorm2D::~HomgNorm2D()
00031 {
00032 }
00033 
00034 static bool paranoid = true;
00035 static void centre(const vcl_vector<HomgPoint2D>& in, vcl_vector<HomgPoint2D>& out, double* cx_out, double* cy_out);
00036 static void centre(vcl_vector<vgl_homg_point_2d<double> > const& in, vcl_vector<HomgPoint2D>& out, double&, double&);
00037 static double scale_xyroot2(const vcl_vector<HomgPoint2D>& in, vcl_vector<HomgPoint2D>& out);
00038 
00039 //: Put the cog at the origin.
00040 // Scale x,y so that mean (x^2 + y^2) = 2 when z = 1.
00041 
00042 void HomgNorm2D::normalize(vcl_vector<vgl_homg_point_2d<double> > const& points)
00043 {
00044   // from ho_trivechomg_normalise
00045   normalized_.resize(points.size());
00046 
00047   double cx, cy;
00048   centre(points, normalized_, cx, cy);
00049 
00050   double diameter = scale_xyroot2(normalized_, normalized_);
00051   if (diameter == 0) {
00052     vcl_cerr << "HomgNorm2D: All points coincident\n";
00053     diameter = 1;
00054     was_coincident_=true;
00055   }
00056   else
00057     was_coincident_=false;
00058 
00059   SimilarityMetric::set_center_and_scale(cx, cy, 1/diameter);
00060 
00061   if (paranoid) {
00062     SimilarityMetric::scale_matrices(1/diameter);
00063     for (unsigned i = 0; i < points.size(); ++i) {
00064       vnl_double_3 ni = normalized_[i].get_vector();
00065       vgl_homg_point_2d<double> mi = SimilarityMetric::imagehomg_to_homg(points[i]);
00066       ni[0] -= mi.x(); ni[1] -= mi.y(); ni[2] -= mi.w(); 
00067       double l = ni.magnitude();
00068       if (l > 1e-12) {
00069         vcl_cerr << "HomgNorm2D: "
00070                  << "d = " << diameter
00071                  << ", ni = " << normalized_[i].get_vector()
00072                  << ", mi = " << mi
00073                  << ", Residual = " << ni
00074                  << " (mag = " << l << ")\n";
00075       }
00076     }
00077   }
00078 
00079   if (unit_omega_) {
00080     /* homogeneous scale factor is normally undetermined - set it here though. */
00081     for (unsigned i = 0; i < normalized_.size(); ++i) {
00082       HomgPoint2D& p = normalized_[i];
00083       double x = p.x();
00084       double y = p.y();
00085       double w = p.w();
00086 
00087       if (w == 0) {
00088         /* average magnitude of finite points is root(3) after this stage - do the same for pts at infinity. */
00089 
00090         double scaling = vcl_sqrt(3.0) / vnl_math_hypot(x, y);
00091         x *= scaling;
00092         y *= scaling;
00093       } else {
00094         x /= w;
00095         y /= w;
00096         w = 1.0;
00097       }
00098       p.set(x,y,w);
00099     }
00100   }
00101 }
00102 
00103 void HomgNorm2D::normalize(const vcl_vector<HomgPoint2D>& points)
00104 {
00105   // from ho_trivechomg_normalise
00106   normalized_.resize(points.size());
00107 
00108 #if 0
00109   for (unsigned i = 0; i < points.size(); ++i)
00110     vcl_cerr << points[i].get_vector() << vcl_endl;
00111 #endif
00112 
00113   double cx, cy;
00114   centre(points, normalized_, &cx, &cy);
00115 
00116   double diameter = scale_xyroot2(normalized_, normalized_);
00117   if (diameter == 0) {
00118     vcl_cerr << "HomgNorm2D: All points coincident\n";
00119     diameter = 1;
00120     was_coincident_=true; // FSM
00121   }
00122   else
00123     was_coincident_=false;
00124 
00125   SimilarityMetric::set_center_and_scale(cx, cy, 1/diameter);
00126 
00127   //vcl_cerr << "NORM = " << norm_matrix_ << vcl_endl;
00128   if (paranoid) {
00129     SimilarityMetric::scale_matrices(1/diameter);
00130     for (unsigned i = 0; i < points.size(); ++i) {
00131       vnl_double_3 ni = normalized_[i].get_vector();
00132       vnl_double_3 mi = SimilarityMetric::imagehomg_to_homg(points[i]).get_vector();
00133       vnl_double_3 residual = ni - mi;
00134       double l = residual.magnitude();
00135       if (l > 1e-12) {
00136      // vcl_cerr << "\n\n";
00137         vcl_cerr << "HomgNorm2D: "
00138                  << "d = " << diameter
00139                  << "ni = " << ni
00140                  << "mi = " << mi
00141                  << "Residual = " << residual
00142                  << " mag = " << l << vcl_endl;
00143       }
00144     }
00145   }
00146 
00147   if (unit_omega_) {
00148     /* homogeneous scale factor is normally undetermined - set it here though. */
00149     for (unsigned i = 0; i < normalized_.size(); ++i) {
00150       HomgPoint2D& p = normalized_[i];
00151       double x = p.x();
00152       double y = p.y();
00153       double w = p.w();
00154 
00155       if (w == 0) {
00156         /* average magnitude of finite points is root(3) after this stage - do the same for pts at infinity. */
00157 
00158         double scaling = vcl_sqrt(3.0) / vnl_math_hypot(x, y);
00159         x *= scaling;
00160         y *= scaling;
00161       } else {
00162         x /= w;
00163         y /= w;
00164         w = 1.0;
00165       }
00166       p.set(x,y,w);
00167     }
00168   }
00169 }
00170 
00171 static void centre(const vcl_vector<HomgPoint2D>& in,
00172                    vcl_vector<HomgPoint2D>& out,
00173                    double *cx, double *cy)
00174 {
00175   // Compute center of mass
00176   double cog_x = 0;
00177   double cog_y = 0;
00178   unsigned cog_count = 0;
00179   unsigned n = in.size();
00180   for (unsigned i = 0; i < n; ++i) {
00181     const HomgPoint2D& p = in[i];
00182     double x,y;
00183     if (p.get_nonhomogeneous(x, y)) {
00184       cog_x += x;
00185       cog_y += y;
00186       ++cog_count;
00187     }
00188   }
00189   if (cog_count > 0) {
00190     cog_x /= (double) cog_count;
00191     cog_y /= (double) cog_count;
00192   }
00193 
00194   // Remember cog for caller
00195   *cx = cog_x;
00196   *cy = cog_y;
00197 
00198   // Build conditioning matrix
00199   vnl_double_3x3 C;
00200   C.set_identity();
00201   C(0,2) = -cog_x;
00202   C(1,2) = -cog_y;
00203 
00204   // Transform points
00205   {
00206     for (unsigned i = 0; i < n; ++i)
00207       out[i].set(C * in[i].get_vector());
00208   }
00209 
00210   // Check transformation has worked.
00211   if (paranoid) {
00212     cog_x = 0;
00213     cog_y = 0;
00214     for (unsigned i = 0; i < n; ++i) {
00215       const HomgPoint2D& p = out[i];
00216       double x,y;
00217       if (p.get_nonhomogeneous(x, y)) {
00218         cog_x += x;
00219         cog_y += y;
00220       }
00221     }
00222     if (vnl_math_hypot(cog_x, cog_y) > 1e-10)
00223       vcl_cerr << "HomgNorm2D: expected (0,0) computed (" << cog_x << "," << cog_y << ")\n";
00224   }
00225 }
00226 
00227 static void centre(vcl_vector<vgl_homg_point_2d<double> > const& in,
00228                    vcl_vector<HomgPoint2D>& out,
00229                    double& cx, double& cy)
00230 {
00231   // Compute center of mass
00232   double cog_x = 0;
00233   double cog_y = 0;
00234   unsigned cog_count = 0;
00235   unsigned n = in.size();
00236   for (unsigned i = 0; i < n; ++i) {
00237     vgl_homg_point_2d<double> const& p = in[i];
00238     if (p.w() != 0.0) {
00239       cog_x += p.x()/p.w();
00240       cog_y += p.y()/p.w();
00241       ++cog_count;
00242     }
00243   }
00244   if (cog_count > 0) {
00245     cog_x /= (double) cog_count;
00246     cog_y /= (double) cog_count;
00247   }
00248 
00249   // Remember cog for caller
00250   cx = cog_x;
00251   cy = cog_y;
00252 
00253   // Build conditioning matrix
00254   vnl_double_3x3 C;
00255   C.set_identity();
00256   C(0,2) = -cog_x;
00257   C(1,2) = -cog_y;
00258 
00259   // Transform points
00260   for (unsigned i = 0; i < n; ++i)
00261   {
00262     vgl_homg_point_2d<double> q = C * in[i];
00263     out[i].set(q.x(),q.y(),q.w());
00264   }
00265 
00266   // Check transformation has worked.
00267   if (paranoid) {
00268     cog_x = 0;
00269     cog_y = 0;
00270     for (unsigned i = 0; i < n; ++i) {
00271       const HomgPoint2D& p = out[i];
00272       double x,y;
00273       if (p.get_nonhomogeneous(x, y)) {
00274         cog_x += x;
00275         cog_y += y;
00276       }
00277     }
00278     if (vnl_math_hypot(cog_x, cog_y) > 1e-10)
00279       vcl_cerr << "HomgNorm2D: expected (0,0) computed (" << cog_x << "," << cog_y << ")\n";
00280   }
00281 }
00282 
00283 // - Scale the x and y components so that the average bivec magnitude is vcl_sqrt(2).
00284 // Scale x,y so that mean (x^2 + y^2) = 2 when z = 1.
00285 
00286 static double scale_xyroot2(const vcl_vector<HomgPoint2D>& in,
00287                             vcl_vector<HomgPoint2D>& out)
00288 {
00289   double magnitude = 0;
00290   unsigned numfinite = 0;
00291   for (unsigned i = 0; i < in.size(); ++i) {
00292     const HomgPoint2D& p = in[i];
00293     double x,y;
00294     if (p.get_nonhomogeneous(x, y)) {
00295       magnitude += vnl_math_hypot(x, y);
00296       ++numfinite;
00297     }
00298   }
00299 
00300   // Compute diameter
00301   double diameter = 1;
00302   if (numfinite > 0) {
00303     magnitude /= (double) numfinite;
00304     diameter = magnitude / vcl_sqrt(2.0);
00305   }
00306 
00307   // Scale the points
00308   {
00309     for (unsigned i = 0; i < in.size(); ++i)
00310       out[i].set(in[i].x(), in[i].y(), in[i].w() * diameter);
00311   }
00312 
00313   if (paranoid) {
00314     magnitude = 0;
00315     numfinite = 0;
00316     for (unsigned i = 0; i < in.size(); ++i) {
00317       const HomgPoint2D& p = out[i];
00318       double x,y;
00319       if (p.get_nonhomogeneous(x, y)) {
00320         magnitude += vnl_math_hypot(x, y);
00321         ++numfinite;
00322       }
00323     }
00324     if (numfinite > 0) magnitude /= numfinite;
00325     const double expected = vcl_sqrt(2.0);
00326     if (vnl_math_abs(expected - magnitude) > 1e-13) // 1e-14 gave false alarm -- fsm
00327       vcl_cerr << "HomgNorm2D: Expected magnitude " << expected << " computed magnitude " << magnitude << vcl_endl;
00328   }
00329 
00330   // Return
00331   return diameter;
00332 }