contrib/gel/vdgl/vdgl_digital_region.cxx
Go to the documentation of this file.
00001 // This is gel/vdgl/vdgl_digital_region.cxx
00002 #include "vdgl_digital_region.h"
00003 //:
00004 // \file
00005 
00006 #include <vcl_cmath.h> // for fabs and sqrt
00007 #include <vcl_cassert.h>
00008 #include <vnl/algo/vnl_svd.h>
00009 #include <vnl/vnl_float_3.h>
00010 
00011 #ifndef MAX_ROUNDOFF
00012 #define MAX_ROUNDOFF .000025
00013 #endif
00014 
00015 #define near_zero(a) (vcl_fabs (a) < MAX_ROUNDOFF)
00016 
00017 vsol_spatial_object_2d* vdgl_digital_region::clone() const
00018 {
00019   return new vdgl_digital_region(*this);
00020 }
00021 
00022 //-------------------------------------------------------------------------
00023 // Constructors
00024 //
00025 vdgl_digital_region::vdgl_digital_region(vdgl_digital_region const& r)
00026   : vsol_region_2d(r),
00027     npts_(0), pixel_size_(1.f), xp_(0), yp_(0), pix_(0),
00028     max_(0), min_((unsigned short)(-1)), xo_(0.f), yo_(0.f),
00029     io_(0.f), io_stdev_(0.0f), pix_index_(0),
00030     fit_valid_(false), scatter_matrix_valid_(false),
00031     X2_(0), Y2_(0), I2_(0), XY_(0), XI_(0), YI_(0), error_(0), sigma_sq_(0)
00032 {
00033   for (unsigned int i = 0; i<r.Npix(); ++i)
00034     this->IncrementMeans(r.Xj()[i], r.Yj()[i], r.Ij()[i]);
00035   this->InitPixelArrays();
00036   for (unsigned int i = 0; i<r.Npix(); i++)
00037     this->InsertInPixelArrays(r.Xj()[i], r.Yj()[i], r.Ij()[i]);
00038   this->ComputeIntensityStdev();
00039 }
00040 
00041 vdgl_digital_region::vdgl_digital_region(int npts, const float* xp, const float* yp,
00042                                          const unsigned short *pix)
00043   : vsol_region_2d(),
00044     npts_(0), pixel_size_(1.f), xp_(0), yp_(0), pix_(0),
00045     max_(0), min_((unsigned short)(-1)), xo_(0.f), yo_(0.f),
00046     io_(0.f), io_stdev_(0.0f), pix_index_(0),
00047     fit_valid_(false), scatter_matrix_valid_(false),
00048     X2_(0), Y2_(0), I2_(0), XY_(0), XI_(0), YI_(0), error_(0), sigma_sq_(0)
00049 {
00050   assert(npts > 0);
00051   for (int i = 0; i<npts; i++)
00052     this->IncrementMeans(xp[i], yp[i], pix[i]);
00053   this->InitPixelArrays();
00054   for (unsigned int i = 0; i<npts_; i++)
00055     this->InsertInPixelArrays(xp[i], yp[i], pix[i]);
00056   this->ComputeIntensityStdev();
00057 }
00058 
00059 //-------------------------------------------------------------------------
00060 //
00061 // Destructor.
00062 //
00063 vdgl_digital_region::~vdgl_digital_region()
00064 {
00065   delete[] xp_;
00066   delete[] yp_;
00067   delete[] pix_;
00068 }
00069 
00070 //-------------------------------------------------------
00071 //: The X pixel coordinate
00072 float vdgl_digital_region::X() const
00073 {
00074   if (pix_index_<0 || pix_index_ >= (int)npts_)
00075     return 0.0f;
00076   else
00077     return xp_[pix_index_];
00078 }
00079 
00080 //-------------------------------------------------------
00081 //: The Y pixel coordinate
00082 float vdgl_digital_region::Y() const
00083 {
00084   if (pix_index_<0 || pix_index_ >= (int)npts_)
00085     return 0.0f;
00086   else
00087     return yp_[pix_index_];
00088 }
00089 
00090 //-------------------------------------------------------
00091 //: The pixel Intensity
00092 unsigned short vdgl_digital_region::I() const
00093 {
00094   if (pix_index_<0 || pix_index_ >= (int)npts_)
00095     return (unsigned short)0;
00096   else
00097     return pix_[pix_index_];
00098 }
00099 
00100 //-------------------------------------------------------
00101 //: Modify the X pixel coordinate
00102 void vdgl_digital_region::set_X(float x)
00103 {
00104   if (pix_index_<0 || pix_index_ >= (int)npts_)
00105     return;
00106   xp_[pix_index_]=x;
00107 }
00108 
00109 //-------------------------------------------------------
00110 //: Modify the Y pixel coordinate
00111 void vdgl_digital_region::set_Y(float y)
00112 {
00113   if (pix_index_<0 || pix_index_ >= (int)npts_)
00114     return;
00115   yp_[pix_index_]=y;
00116 }
00117 
00118 //-------------------------------------------------------
00119 //: Modify the pixel intensity
00120 void vdgl_digital_region::set_I(unsigned short I)
00121 {
00122   if (pix_index_<0 || pix_index_ >= (int)npts_)
00123     return;
00124   pix_[pix_index_]=I;
00125 }
00126 
00127 //--------------------------------------------------------
00128 //:
00129 //  Initialize the region for accepting a stream of pixels
00130 //  through repeated calls to ::IncrementMeans(..)
00131 void vdgl_digital_region::ResetPixelData()
00132 {
00133   npts_ = 0;
00134   delete [] xp_; xp_ = NULL;
00135   delete [] yp_; yp_ = NULL;
00136   delete [] pix_; pix_ = NULL;
00137   xo_ = yo_ = io_ = io_stdev_=0.0f;
00138 }
00139 
00140 //-----------------------------------------------------------------
00141 //:
00142 //    Used to scan through a set of pixels and acquire mean values
00143 //    for coordinates and intensity.  This approach is necessary to
00144 //    accumulate scatter matrices which have zero mean. One scans
00145 //    the region pixels twice. First to get the means and number of
00146 //    region pixels and then to insert the pixel data into fixed arrays
00147 void vdgl_digital_region::IncrementMeans(float x, float y,
00148                                          unsigned short pix)
00149 {
00150   ++npts_;
00151   xo_ += x;
00152   yo_ += y;
00153   io_ += pix;
00154 }
00155 
00156 //-----------------------------------------------------------------
00157 //:
00158 // Calculate the standard deviation of intensity for the region.
00159 //
00160 float vdgl_digital_region::ComputeIntensityStdev()
00161 {
00162   io_stdev_ = 0.0f; // start from scratch each time
00163   float mean = this->Io(); // get the mean.
00164   for (unsigned int i=0; i<npts_; i++) {
00165     io_stdev_ += (pix_[i]-mean)*(pix_[i]-mean);
00166   }
00167   io_stdev_ = io_stdev_ * 1.0f/(npts_ - 1.0f);
00168   io_stdev_ = vcl_sqrt(io_stdev_);
00169   return io_stdev_;
00170 }
00171 
00172 //-----------------------------------------------------------------
00173 //:
00174 // Now we have the number of pixels so we can create the storage arrays.
00175 void vdgl_digital_region::InitPixelArrays()
00176 {
00177   assert(npts_ > 0);
00178   delete [] xp_;  xp_ = new float[npts_];
00179   delete [] yp_;  yp_ = new float[npts_];
00180   delete [] pix_; pix_ = new unsigned short[npts_];
00181   pix_index_ = 0;
00182   min_ = (unsigned short)(-1);
00183   max_ = 0;
00184 }
00185 
00186 //------------------------------------------------------------------------
00187 //: Insert pixel data into the face arrays.
00188 void vdgl_digital_region::InsertInPixelArrays(float x, float y,
00189                                               unsigned short pix)
00190 {
00191   assert(npts_ > 0);
00192   if (pix_index_<0||pix_index_>(int)npts_) return;
00193   xp_[pix_index_] = x; yp_[pix_index_] = y;
00194   pix_[pix_index_] = pix;
00195   if (pix<min_) min_ = pix;
00196   if (pix>max_) max_ = pix;
00197   ++pix_index_;
00198 }
00199 
00200 float vdgl_digital_region::Xo() const
00201 {
00202   assert(npts_ > 0);
00203   return xo_/npts_;
00204 }
00205 
00206 float vdgl_digital_region::Yo() const
00207 {
00208   assert(npts_ > 0);
00209   return yo_/npts_;
00210 }
00211 
00212 float vdgl_digital_region::Io() const
00213 {
00214   assert(npts_ > 0);
00215   return io_/npts_;
00216 }
00217 
00218 float vdgl_digital_region::Io_sd() const
00219 {
00220   assert(npts_ > 0);
00221   return io_stdev_;
00222 }
00223 
00224 //: Individual scatter matrix elements
00225 double vdgl_digital_region::X2() const
00226 {
00227   if (!scatter_matrix_valid_)
00228     this->ComputeScatterMatrix();
00229   return X2_;
00230 }
00231 
00232 double vdgl_digital_region::Y2() const
00233 {
00234   if (!scatter_matrix_valid_)
00235     this->ComputeScatterMatrix();
00236   return Y2_;
00237 }
00238 
00239 double vdgl_digital_region::XY() const
00240 {
00241   if (!scatter_matrix_valid_)
00242     this->ComputeScatterMatrix();
00243   return XY_;
00244 }
00245 
00246 double vdgl_digital_region::I2() const
00247 {
00248   if (!scatter_matrix_valid_)
00249     this->ComputeScatterMatrix();
00250   return I2_;
00251 }
00252 
00253 double vdgl_digital_region::XI() const
00254 {
00255   if (!scatter_matrix_valid_)
00256     this->ComputeScatterMatrix();
00257   return XI_;
00258 }
00259 
00260 double vdgl_digital_region::YI() const
00261 {
00262   if (!scatter_matrix_valid_)
00263     this->ComputeScatterMatrix();
00264   return YI_;
00265 }
00266 
00267 //------------------------------------------------------------
00268 //: Compute the diameter from the scatter matrix
00269 float vdgl_digital_region::Diameter() const
00270 {
00271   // make sure the scatter matrix is valid
00272   if (!scatter_matrix_valid_)
00273     this->ComputeScatterMatrix();
00274   if (this->Npix() < 4)
00275     return 1.0f;
00276   // construct the lower right 2x2 matrix of S, s.
00277   vnl_matrix<double> s(2, 2, 0.0);
00278   for (int r = 1; r<=2; r++)
00279     for (int c = 1; c<=2; c++)
00280       s(r-1,c-1) = Si_(r,c);
00281   //Compute SVD of s to get diameter
00282   vnl_svd<double> svd(s);
00283   if (svd.rank()!=2)
00284     return float(vcl_sqrt(this->area()));
00285   //The factor of two is to estimate the extreme limit of the distribution
00286   double radius = 2*vcl_sqrt(vcl_fabs(svd.W(0))+ vcl_fabs(svd.W(1)));
00287   return float(2*radius);//diameter
00288 }
00289 
00290 //------------------------------------------------------------
00291 //: Compute the aspect ratio from the scatter matrix
00292 float vdgl_digital_region::AspectRatio() const
00293 {
00294   // make sure the scatter matrix is valid
00295   if (!scatter_matrix_valid_)
00296     this->ComputeScatterMatrix();
00297   if (this->Npix() < 4)
00298     return 1.0f;
00299   // construct the lower right 2x2 matrix of S, s.
00300   vnl_matrix<double> s(2, 2, 0.0);
00301   for (int r = 1; r<=2; r++)
00302     for (int c = 1; c<=2; c++)
00303       s(r-1,c-1) = Si_(r,c);
00304   //Compute SVD of s to get aspect ratio
00305   vnl_svd<double> svd(s);
00306   if (svd.rank()!=2)
00307     return 1.0f;
00308   return (float)vcl_sqrt(svd.W(0)/svd.W(1));
00309 }
00310 
00311 //------------------------------------------------------------
00312 //: Compute the principal orientation of the region.
00313 //   major_axis is a 2-d vector representing the orientation.
00314 void vdgl_digital_region::PrincipalOrientation(vnl_float_2& major_axis)
00315 {
00316   // make sure the scatter matrix is valid
00317   if (!scatter_matrix_valid_)
00318     this->ComputeScatterMatrix();
00319   if (this->Npix() < 4)
00320   {
00321     vcl_cout << "In vdgl_digital_region::PrincipalOrientation(..) Npts<4\n";
00322     major_axis[0]=1.0; major_axis[1]=0.0;
00323     return;
00324   }
00325   // construct the lower right 2x2 matrix of S, s.
00326   vnl_matrix<double> s(2, 2, 0.0);
00327   for (int r = 1; r<=2; r++)
00328     for (int c = 1; c<=2; c++)
00329       s(r-1,c-1) = Si_(r,c);
00330   //Compute SVD of s to get aspect ratio
00331   vnl_svd<double> svd(s);
00332   if (svd.rank()!=2)
00333   {
00334     vcl_cout << "In vdgl_digital_region::PrincipalOrientation(..) Insufficient rank\n";
00335     major_axis[0]=1.0; major_axis[1]=0.0;
00336     return;
00337   }
00338   vnl_matrix<double> v = svd.V();
00339   //2 sigma gives a good estimate of axis length (sigma = principal eigenvalue)
00340   double radius = 2*vcl_sqrt(vcl_fabs(svd.W(0)));
00341   major_axis[0]=float(v(0,0)*radius);
00342   major_axis[1]=float(v(1,0)*radius);
00343 }
00344 
00345 double vdgl_digital_region::Ix() const
00346 {
00347   if (!fit_valid_)
00348     this->DoPlaneFit();
00349   return Ix_;
00350 }
00351 
00352 double vdgl_digital_region::Iy() const
00353 {
00354   if (!fit_valid_)
00355     this->DoPlaneFit();
00356   return Iy_;
00357 }
00358 
00359 
00360 //--------------------------------------------------------------
00361 //: Update the scatter matrix elements.
00362 // The means are subtracted from the input values before incrementing.
00363 // Thus the scatter matrix is formed with a coordinate system at the origin.
00364 void vdgl_digital_region::IncrByXYI(double x, double y, int intens) const
00365 {
00366   fit_valid_=false;
00367   scatter_matrix_valid_=false;
00368   double dbx = x - this->Xo(), dby = y - this->Yo();
00369   double dint = (double)intens- this->Io();
00370   X2_ += dbx*dbx;  Y2_ += dby*dby;  I2_ += dint*dint;
00371   XY_ += dbx*dby;  XI_ += dbx*dint;  YI_ += dby*dint;
00372 }
00373 
00374 //-------------------------------------------
00375 //: The scatter matrix (upper 3x3) is transformed to the origin by subtracting off the means.
00376 void vdgl_digital_region::ComputeScatterMatrix() const
00377 {
00378   if (!npts_)
00379   {
00380     vcl_cout << "In vdgl_digital_region::ComputeScatterMatrix() - no pixels\n";
00381     return;
00382   }
00383   X2_ = 0;  Y2_ = 0;  I2_ = 0;
00384   XY_ = 0;  XI_ = 0;  YI_ = 0;
00385 
00386   for (this->reset(); this->next();)
00387     this->IncrByXYI(this->X(), this->Y(), this->I());
00388 
00389 
00390   //The Scatter Matrix
00391   //
00392   //   -             -
00393   //  | I^2  XI   YI  |
00394   //  |               |
00395   //  | XI   X^2  XY  |
00396   //  |               |
00397   //  | YI   XY   Y^2 |
00398   //   -             -
00399   //
00400   Si_(0,0) = I2_/npts_;
00401   Si_(1,0) = Si_(0,1) = XI_/npts_;
00402   Si_(2,0) = Si_(0,2) = YI_/npts_;
00403   Si_(1,1) = X2_/npts_;
00404   Si_(2,1) = Si_(1,2) = XY_/npts_;
00405   Si_(2,2) = Y2_/npts_;
00406   scatter_matrix_valid_ = true;
00407 }
00408 
00409 //---------------------------------------------------------------
00410 //: Computes the squared deviation from the sample mean
00411 //
00412 double vdgl_digital_region::ComputeSampleResidual() const
00413 {
00414   if (!fit_valid_)
00415     this->DoPlaneFit();
00416   return Si_(0,0) + Si_(1,1) + Si_(2,2);
00417 }
00418 
00419 //: Solve for the fitted plane.
00420 //  Closed form solution in terms of the scatter matrix.
00421 void vdgl_digital_region::DoPlaneFit() const
00422 {
00423   assert(npts_ >= 4);
00424   fit_valid_ = true;
00425 
00426   if (!scatter_matrix_valid_)
00427     this->ComputeScatterMatrix();
00428 
00429   double den = Si_(1,1)*Si_(2,2)
00430              - Si_(1,2)*Si_(2,1);
00431   if (near_zero(den))
00432   {
00433     vcl_cout << "In vdgl_digital_region::SolveForPlane(..) determinant near zero\n";
00434     Ix_ = Iy_ = 0;
00435     error_ = Si_(0,0);
00436     sigma_sq_ = Si_(0,0) + Si_(1,1) + Si_(2,2);
00437     return;
00438   }
00439   double adet = Si_(0,1)*Si_(2,2)
00440               - Si_(0,2)*Si_(2,1);
00441   double bdet = Si_(0,2)*Si_(1,1)
00442               - Si_(0,1)*Si_(1,2);
00443   Ix_ = adet/den;
00444   Iy_ = bdet/den;
00445   error_ = Si_(0,0) - 2*Ix_*Si_(0,1) - 2*Iy_*Si_(0,2)
00446          + Ix_*Ix_*Si_(1,1) + 2*Ix_*Iy_*Si_(1,2) + Iy_*Iy_*Si_(2,2);
00447   sigma_sq_ =  this->ComputeSampleResidual();
00448 }
00449 
00450 
00451 void vdgl_digital_region::PrintFit() const
00452 {
00453   if (!fit_valid_)
00454     this->DoPlaneFit();
00455   vcl_cout << "IntensityFit(In Plane Coordinates): "
00456            << "Number of Points =" <<  npts_ << vcl_endl
00457            << "Scatter Matrix:\n"
00458            << "X2 Y2 I2   " << X2() << ' ' << Y2() << ' ' << I2() << vcl_endl
00459            << "XY XI YI = " << XY() << ' ' << XI() << ' ' << YI() << vcl_endl
00460            << "Xo Yo Io   " << Xo() << ' ' << Yo() << ' ' << Io() << vcl_endl
00461            << "fitted Plane:\n"
00462            << "di/dx " << this->Ix() << vcl_endl
00463            << "di/dy " << this->Iy() << vcl_endl
00464            << "sample variance: " << this->Var()<< vcl_endl
00465            << "squared cost: " << error_ << vcl_endl
00466            << "average cost: " << vcl_sqrt(error_) << vcl_endl << vcl_endl;
00467 }
00468 
00469 #if 0
00470 //-----------------------------------------------------------
00471 //: Return a package of fitted coefficients
00472 //
00473 IntensityCoef_ref vdgl_digital_region::GetIntCoef() const
00474 {
00475   if (!fit_valid_)
00476     this->DoPlaneFit();
00477   return new IntensityCoef(this->Npix(), float(this->Var()),
00478                            float(this->Io()), float(this->Ix()),
00479                            float(this->Iy()));
00480 }
00481 #endif
00482 
00483 //-------------------------------------------------------
00484 //: The Residual Intensity
00485 float vdgl_digital_region::Ir() const
00486 {
00487   if (pix_index_<0 || pix_index_ >= (int)npts_)
00488     return 0.0f;
00489   if (npts_<4)
00490     return 0.0f;
00491 
00492   if (!fit_valid_)
00493   {
00494     int initial_pix_index = pix_index_;//Save the current pix_index_ state
00495     this->DoPlaneFit();
00496     pix_index_ = initial_pix_index;//Restore the pix_index_ state
00497   }
00498   float val = float(this->I());
00499   float io = this->Io(),
00500         ix = float(this->Ix()), x = this->X(), xo = this->Xo(),
00501         iy = float(this->Iy()), y = this->Y(), yo = this->Yo();
00502   float plane_int = io + ix*(x - xo) + iy*(y-yo);
00503 
00504   return val - plane_int;
00505 }
00506 
00507 bool vdgl_digital_region::transform(vnl_float_3x3 const& t)
00508 {
00509   //Transform the Pixels
00510   if (npts_ == 0)
00511     return false;
00512   for (unsigned int i=0; i<npts_; i++)
00513   {
00514     vnl_float_3 p = t * vnl_float_3(xp_[i], yp_[i], 1.0f);
00515     xp_[i]=p[0]/p[2];
00516     yp_[i]=p[1]/p[2];
00517   }
00518   //Transform the mean pixel position
00519   vnl_float_3 m = t * vnl_float_3(this->Xo(), this->Yo(), 1.0f);
00520   xo_ = m[0]/m[2]*npts_;
00521   yo_ = m[1]/m[2]*npts_;
00522   fit_valid_ = false;
00523   scatter_matrix_valid_=false;
00524   return true;
00525 }
00526 
00527 //--------------------------------------------------------
00528 //: Compute the histogram
00529 vcl_vector<unsigned int> vdgl_digital_region::histogram(int nbins)
00530 {
00531   assert(nbins > 0);
00532   if (nbins == 1) return vcl_vector<unsigned int>(1, npts_);
00533   vcl_vector<unsigned int> hist(nbins, 0U);
00534   if (npts_ == 0) return hist;
00535   float res = max_-min_, step = res/nbins + 1e-6f;
00536   for (unsigned int i =0; i<npts_; ++i)
00537     ++hist[int((pix_[i]-min_)/step)];
00538   return hist;
00539 }
00540 
00541 vcl_vector<unsigned int> vdgl_digital_region::residual_histogram(int nbins,
00542                                                                  float* min,
00543                                                                  float* max)
00544 {
00545   assert(nbins > 0);
00546   if (nbins == 1) return vcl_vector<unsigned int>(1, npts_);
00547   vcl_vector<unsigned int> hist(nbins, 0U);
00548   if (npts_ == 0) return hist;
00549   this->reset();
00550   float mini = this->Ir(), maxi=mini;
00551   while (this->next())
00552   {
00553     float ir = this->Ir();
00554     if (ir<mini) mini = ir;
00555     if (ir>maxi) maxi = ir;
00556   }
00557   if (min) *min = mini;
00558   if (max) *max = maxi;
00559   float res = maxi-mini, step = res/nbins + 1e-6f;
00560   for (this->reset(); this->next(); )
00561     ++hist[int((this->Ir()-mini)/step)];
00562   return hist;
00563 }