00001 
00008 #include "v4r_utils/ellipse_refinement.h"
00009 #include <Eigen/Dense>
00010 #include <iostream>
00011 
00012 
00013 
00014 namespace V4R
00015 {
00016 
00017 template<typename T>
00018 inline bool isZero(const T d)
00019 {
00020     return fabs(d) <= std::numeric_limits<T>::epsilon();
00021 }
00022 
00026 inline double scaleAngle_0_2pi(double a)
00027 {
00028     while(a >= 2.*M_PI) a -= 2.*M_PI;
00029     while(a < 0.) a += 2.*M_PI;
00030     return a;
00031 }
00032 
00033 using namespace std;
00034 
00038 EllipseRefinement::EllipseRefinement(const Parameter &_param)
00039     : param(_param)
00040 {
00041 
00042 }
00043 
00044 EllipseRefinement::~EllipseRefinement()
00045 {
00046 
00047 }
00048 
00052 bool EllipseRefinement::refine(const cv::Mat_<short> &im_dx, const cv::Mat_<short> &im_dy,
00053                                    const std::vector<cv::Point2f> &points, Ellipse &ellipse)
00054 {
00055     
00056     
00057     
00058     
00059 
00060     double minx=10000, maxx=0, miny=10000, maxy=0;
00061     double minGradPoints=10000,maxGradPoints=0;
00062 
00063     for(unsigned i=0; i<points.size(); i++)
00064     {
00065         const cv::Point2f &pt = points[i];
00066 
00067         if(pt.x < minx) minx=pt.x;
00068         if(pt.y < miny) miny=pt.y;
00069         if(pt.x > maxx) maxx=pt.x;
00070         if(pt.y > maxy) maxy=pt.y;
00071         float a =   im_dx(pt.y,pt.x);
00072         float b =   im_dy(pt.y,pt.x);
00073         float grad = sqrt(a*a + b*b);
00074         if(minGradPoints > grad) minGradPoints = grad;
00075         if(grad > maxGradPoints) maxGradPoints = grad;
00076     }
00077 
00078     double gradThreshold = (minGradPoints + (maxGradPoints - minGradPoints)/2)/2;
00079     gradThreshold = param.min_gradient; 
00080     
00081     int ellipseRand=param.border; 
00082     int rand=param.border; 
00083     pointsToUse.clear();
00084     double sumx=0,sumy=0;
00085     
00086     short min_y = (short)(miny-rand);
00087     short max_y = (short)(maxy+rand);
00088     short min_x = (short)(minx-rand);
00089     short max_x = (short)(maxx+rand);
00090     if (min_y < 0) min_y = 0;
00091     if (max_y >= im_dx.rows) max_y = (short)im_dx.rows-1;
00092     if (min_x < 0) min_x = 0;
00093     if (max_x >= im_dx.cols) max_x = (short)im_dx.cols-1;
00094 
00095     for(short j=min_y; j < max_y; j++)
00096     {
00097         for(short i=min_x; i < max_x; i++)
00098         {
00099             float a =   im_dx(j,i);
00100             float b =   im_dy(j,i);
00101             float grad = sqrt(a*a + b*b);
00102             if(gradThreshold < grad &&
00103                     ellipse.insideEllipse(ellipse.a+ellipseRand,ellipse.b+ellipseRand,ellipse.x,ellipse.y, ellipse.phi,i,j) &&
00104                     !ellipse.insideEllipse(ellipse.a-ellipseRand,ellipse.b-ellipseRand,ellipse.x,ellipse.y, ellipse.phi,i,j))
00105             {
00106                 pointsToUse.push_back(cv::Point2d(i,j));
00107                 sumx+=i;
00108                 sumy+=j;
00109             }
00110         }
00111     }
00112 
00113     
00114     
00115     double mx = sumx/pointsToUse.size();
00116     double my = sumy/pointsToUse.size();
00117 
00118     
00119     double lengths=0;
00120     for(unsigned i=0; i<pointsToUse.size(); i++)
00121     {
00122         double a = pointsToUse[i].x-mx;
00123         double b = pointsToUse[i].y-my;
00124 
00125         lengths+= sqrt(a*a + b*b);
00126     }
00127 
00128     double ss = sqrt(2.) / (lengths/pointsToUse.size());
00129     Eigen::Matrix3d H;
00130     H << ss, 0, -ss*mx, 0, ss, -ss*my, 0, 0, 1;
00131     Eigen::Matrix3d Hinv = H.inverse();
00132     Eigen::Matrix3d HinvT = Hinv.transpose();
00133 
00134 
00135     
00136     
00137 
00138 
00139 
00140 
00141 
00142 
00143 
00144 
00145 
00146 
00147 
00148 
00149 
00150 
00151 
00152 
00153 
00154 
00155 
00156 
00157 
00158 
00159 
00160 
00161 
00162 
00163 
00164 
00165 
00166 
00167 
00168 
00169 
00170 
00171 
00172 
00173 
00174 
00175 
00176 
00177 
00178 
00179 
00180 
00181 
00182 
00183 
00184 
00185 
00186     Eigen::MatrixXd M = Eigen::MatrixXd::Zero(5,5);
00187     Eigen::VectorXd E = Eigen::VectorXd::Zero(5);
00188     Eigen::VectorXd Kprima(5);
00189     Eigen::MatrixXd res1(5,5);
00190     Eigen::Vector3d li;
00191 
00192     std::vector<Eigen::Vector3d> tangentLines((unsigned)pointsToUse.size());
00193 
00194     for (unsigned i=0; i<pointsToUse.size(); i++)
00195     {
00196         const cv::Point2d &pt = pointsToUse[i];
00197         Kprima.setZero();
00198         res1.setZero();
00199         float a =   im_dx((short)pt.y,(short)pt.x);
00200         float b =   im_dy((short)pt.y,(short)pt.x);
00201         float grad = sqrt(a*a + b*b);
00202 
00203         
00204         li << double(im_dx((short)pt.y,(short)pt.x)), double(im_dy((short)pt.y,(short)pt.x)),
00205            -( double(im_dx((short)pt.y,(short)pt.x))*pt.x + double(im_dy((short)pt.y,(short)pt.x))*pt.y );
00206 
00207         
00208         li /= grad;
00209 
00210         
00211         li = HinvT * li;
00212 
00213         
00214         tangentLines[i] = li;
00215 
00216         double weight = grad*grad;
00217         Kprima[0] = li[0]*li[0];
00218         Kprima[1] = li[0]*li[1];
00219         Kprima[2] = li[1]*li[1];
00220         Kprima[3] = li[0]*li[2];
00221         Kprima[4] = li[1]*li[2];
00222 
00223         res1 = weight*Kprima*Kprima.transpose();
00224         M += res1;
00225 
00226         Kprima[0] *= -weight*li[2]*li[2];
00227         Kprima[1] *= -weight*li[2]*li[2];
00228         Kprima[2] *= -weight*li[2]*li[2];
00229         Kprima[3] *= -weight*li[2]*li[2];
00230         Kprima[4] *= -weight*li[2]*li[2];
00231 
00232         E += Kprima;
00233     }
00234 
00235     
00236     
00237 
00238     Eigen::MatrixXd MT(5,5);
00239     MT = M.transpose();
00240 
00241     Eigen::MatrixXd MPerMT(5,5), MPerMTinv(5,5);
00242 
00243     MPerMT = MT*M;
00244     MPerMTinv = MPerMT.inverse();
00245     MPerMT = MPerMTinv*MT;
00246 
00247     Eigen::VectorXd params(5);
00248     params = MPerMT*E;
00249 
00250     Eigen::Matrix3d CNorm;
00251     CNorm << params[0],    params[1]/2., params[3]/2.,
00252           params[1]/2., params[2],    params[4]/2.,
00253           params[3]/2., params[4]/2., 1;
00254 
00255     
00256     Eigen::Matrix3d CNormMalHInv;
00257     CNormMalHInv = Hinv*CNorm*HinvT;
00258 
00259     
00260     
00261 
00262     double dSumDistance=0;
00263     Eigen::Vector3d pole;
00264 
00265     for (unsigned i=0; i<pointsToUse.size(); i++)
00266     {
00267         const Eigen::Vector3d &l = tangentLines[i];
00268         
00269         pole = CNorm*l;
00270 
00271         double x0 = pole[0]/pole[2];
00272         double y0 = pole[1]/pole[2];
00273         double a = l[0];
00274         double b = l[1];
00275         double d = abs(l[0] * x0 + l[1] * y0 + l[2]) / sqrt(a*a+b*b);
00276         dSumDistance += d;
00277     }
00278 
00279     ellipse.fit_error = dSumDistance / double(pointsToUse.size());
00280 
00281     
00282     
00283 
00284     Eigen::Matrix3d C;
00285     C = CNormMalHInv.inverse();
00286 
00287     double Ac,Bc,Cc,Dc,Ec,Fc;
00288     Ac = C(0,0);
00289     Bc = C(0,1);
00290     Cc = C(1,1);
00291     Dc = C(2,0);
00292     Ec = C(1,2);
00293     Fc = C(2,2);
00294 
00295     
00296     bool ell_ok = ellipse.computeAndSetGeomFromConic(Ac,Bc,Cc,Dc,Ec,Fc);
00297 
00298     return ell_ok;
00299 }
00300 
00301 
00302 
00306 EllipseRefinement::Ellipse::Ellipse()
00307     : id(0), x(0), y(0), a(0), b(0), phi(0), fit_error(0.), support(0.), a2(0), a4(0), b2(0), b4(0), x2(0), y2(0)
00308 {}
00309 
00310 EllipseRefinement::Ellipse::Ellipse(const Ellipse &e)
00311     : id(e.id), x(e.x), y(e.y), a(e.a), b(e.b), phi(e.phi), fit_error(e.fit_error), support(e.support), a2(e.a2), a4(e.a4), b2(e.b2), b4(e.b4), x2(e.x2), y2(e.y2){
00312 }
00313 
00314 void EllipseRefinement::Ellipse::setEllipse(const double &_x, const double &_y, const double &_a, const double &_b, const double &_phi) {
00315     x = _x, y = _y, a = _a, b = _b, phi = _phi;
00316     a2 = a*a;
00317     a4 = a2*a2;
00318     b2 = b*b;
00319     b4 = b2*b2;
00320     x2 = 0;
00321     y2 = 0;
00322 }
00323 
00327 void EllipseRefinement::Ellipse::setEllipse(const cv::RotatedRect& rect) {
00328     x = rect.center.x;
00329     y = rect.center.y;
00330     
00331     a = double(rect.size.width)/2.;
00332     b = double(rect.size.height)/2.;
00333     
00334     phi = scaleAngle_0_2pi(rect.angle*M_PI/180.);
00335     
00336     if(a < b)
00337     {
00338         swap(a, b);
00339         phi = scaleAngle_0_2pi(phi + M_PI_2);
00340     }
00341     setEllipse(x, y, a, b, phi);
00342 }
00343 
00347 unsigned EllipseRefinement::Ellipse::ellipseSupport(const std::vector<cv::Point2f> &points, double inl_dist, std::vector<bool> &inl_idx)
00348 {
00349     double co = cos(-phi), si = sin(-phi), n, d, dist;
00350     unsigned z;
00351     unsigned nbInl=0;
00352 
00353     cv::Point2f p, q;
00354 
00355     fit_error = 0.;
00356     inl_idx.resize(points.size());
00357 
00358     for(z=0; z<points.size(); z++)
00359     {
00360         
00361         
00362         
00363         
00364         
00365         p=points[z];
00366         p.x -= x;
00367         p.y -= y;
00368         q.x = co*p.x - si*p.y;
00369         q.y = si*p.x + co*p.y;
00370         
00371         if(isZero(q.x) && isZero(q.y))
00372             dist = b;
00373         else
00374         {
00375             x2 = q.x * q.x;
00376             y2 = q.y * q.y;
00377             n = fabs(x2/a2 + y2/b2 - 1.);
00378             d = 2.*sqrt(x2/a4 + y2/b4);
00379             dist = n/d;
00380         }
00381         if(dist <= inl_dist)
00382         {
00383             inl_idx[z]=true;
00384             nbInl++;
00385             fit_error += dist;
00386         }
00387         else
00388             inl_idx[z]=false;
00389     }
00390 
00391     double circumference = ellipseCircumference(a, b);
00392 
00393     if (nbInl>0 && !isZero(circumference))
00394     {
00395         fit_error /= (double)nbInl;
00396         support = (double)nbInl / circumference;
00397     }
00398     else
00399     {
00400         fit_error = DBL_MAX;
00401         support=0;
00402     }
00403 
00404     return nbInl;
00405 }
00406 
00407 
00408 void EllipseRefinement::Ellipse::get(cv::RotatedRect &r) {
00409     r.center.x = x, r.center.y = y;
00410     r.size.width = a*2., r.size.height = b*2.;
00411     r.angle = phi*180./M_PI;
00412 }
00413 
00414 
00418 bool EllipseRefinement::Ellipse::computeAndSetGeomFromConic(double Ac, double Bc, double Cc, double Dc, double Ec, double Fc) {
00419 
00420     double BcBcAcCc = (Bc*Bc - Ac*Cc);
00421     if (BcBcAcCc == 0) {
00422         return false;
00423     }
00424     double x0 = (Cc*Dc - Bc*Ec) / BcBcAcCc;
00425     double y0 = (Ac*Ec - Bc*Dc) / BcBcAcCc;
00426     double a0, a0_2, b0, b0_2, phi0;
00427     double d = Ac-Cc;
00428     double SqrAcCc4BcBc = d*d + 4*Bc*Bc;
00429     if (SqrAcCc4BcBc < 0) {
00430         return false;
00431     }
00432     a0_2 = (2*(Ac*Ec*Ec + Cc*Dc*Dc + Fc*Bc*Bc - 2*Bc*Dc*Ec - Ac*Cc*Fc)) / ((Bc*Bc - Ac*Cc)*((sqrt(SqrAcCc4BcBc)) - (Ac+Cc)));
00433     b0_2 = (2*(Ac*Ec*Ec + Cc*Dc*Dc + Fc*Bc*Bc - 2*Bc*Dc*Ec - Ac*Cc*Fc)) / ((Bc*Bc - Ac*Cc)*(-(sqrt(SqrAcCc4BcBc)) - (Ac+Cc)));
00434     if ((a0_2 < 0) || (b0_2 < 0)) {
00435         return false;
00436     }
00437     a0 = sqrt(a0_2);
00438     b0 = sqrt(b0_2);
00439 
00440     
00441     phi0=0;
00442     if (Bc == 0) {
00443         if (Ac < Cc) {
00444             
00445             phi0=M_PI_2;
00446         }
00447     } else {
00448         double z = (Ac-Cc) / (2*Bc);
00449         if (Ac < Cc) {
00450             phi0=0.5*(atan(1./z));
00451             
00452         }
00453         if (Ac > Cc) {
00454             phi0=M_PI_2 + 0.5*(atan(1./z));
00455             
00456         }
00457     }
00458 
00459     x = x0;
00460     y = y0;
00461     a = a0;
00462     b = b0;
00463 
00464     
00465     if (a < b) {
00466         swap(a, b);
00467         
00468     }
00469 
00470     
00471 
00472     return true;
00473 }
00474 
00479 double EllipseRefinement::Ellipse::ellipseCircumference(double a, double b)
00480 {
00481     return M_PI*(1.5*(a + b) - sqrt(a*b));
00482 }
00483 
00484 
00488 bool EllipseRefinement::Ellipse::insideEllipse(double _a, double _b, double _x0, double _y0, double _phi, double _x, double _y) const
00489 {
00490     double dx = ((_x - _x0)*cos(_phi) + (_y-_y0)*sin(_phi)) / _a;
00491     double dy = (-(_x - _x0)*sin(_phi) + (_y-_y0)*cos(_phi)) / _b;
00492     double distance = dx * dx + dy * dy;
00493     return (distance < 1.0) ? 1 : 0;
00494 
00495 }
00496 }  
00497