fitting_curve_pdm.cpp
Go to the documentation of this file.
00001 /*
00002  * Software License Agreement (BSD License)
00003  *
00004  *  Copyright (c) 2012-, Open Perception, Inc.
00005  *  All rights reserved.
00006  *
00007  *  Redistribution and use in source and binary forms, with or without
00008  *  modification, are permitted provided that the following conditions
00009  *  are met:
00010  *
00011  *   * Redistributions of source code must retain the above copyright
00012  *     notice, this list of conditions and the following disclaimer.
00013  *   * Redistributions in binary form must reproduce the above
00014  *     copyright notice, this list of conditions and the following
00015  *     disclaimer in the documentation and/or other materials provided
00016  *     with the distribution.
00017  *   * Neither the name of the copyright holder(s) nor the names of its
00018  *     contributors may be used to endorse or promote products derived
00019  *     from this software without specific prior written permission.
00020  *
00021  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00022  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00023  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00024  *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00025  *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00026  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00027  *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00029  *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00031  *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00032  *  POSSIBILITY OF SUCH DAMAGE.
00033  *
00034  * 
00035  *
00036  */
00037 
00038 #include <pcl/surface/on_nurbs/fitting_curve_pdm.h>
00039 #include <stdexcept>
00040 
00041 using namespace pcl;
00042 using namespace on_nurbs;
00043 
00044 FittingCurve::FittingCurve (int order, NurbsDataCurve *data)
00045 {
00046   if (order < 2)
00047     throw std::runtime_error ("[NurbsFittingCylinder::NurbsFittingCylinder] Error order to low (order<2).");
00048 
00049   ON::Begin ();
00050 
00051   m_data = data;
00052   m_nurbs = initNurbsCurvePCA (order, m_data->interior);
00053 
00054   in_max_steps = 100;
00055   in_accuracy = 1e-4;
00056   m_quiet = true;
00057 }
00058 
00059 FittingCurve::FittingCurve (NurbsDataCurve *data, const ON_NurbsCurve &ns)
00060 {
00061   ON::Begin ();
00062 
00063   m_nurbs = ON_NurbsCurve (ns);
00064   m_data = data;
00065 
00066   in_max_steps = 100;
00067   in_accuracy = 1e-4;
00068   m_quiet = true;
00069 }
00070 
00071 int
00072 FittingCurve::findElement (double xi, const std::vector<double> &elements)
00073 {
00074   if (xi >= elements.back ())
00075     return (int (elements.size ()) - 2);
00076 
00077   for (unsigned i = 0; i < elements.size () - 1; i++)
00078   {
00079     if (xi >= elements[i] && xi < elements[i + 1])
00080     {
00081       return i;
00082     }
00083   }
00084 
00085   //  xi < elements.front()
00086   return 0;
00087 
00088 }
00089 
00090 void
00091 FittingCurve::refine ()
00092 {
00093   std::vector<double> xi;
00094 
00095   std::vector<double> elements = this->getElementVector (m_nurbs);
00096 
00097   for (unsigned i = 0; i < elements.size () - 1; i++)
00098     xi.push_back (elements[i] + 0.5 * (elements[i + 1] - elements[i]));
00099 
00100   for (unsigned i = 0; i < xi.size (); i++)
00101     m_nurbs.InsertKnot (xi[i], 1);
00102 }
00103 
00104 void
00105 FittingCurve::assemble (const Parameter &parameter)
00106 {
00107   int cp_red = m_nurbs.m_order - 2;
00108   int ncp = m_nurbs.m_cv_count - 2 * cp_red;
00109   int nCageReg = m_nurbs.m_cv_count - 2 * cp_red;
00110   int nInt = int (m_data->interior.size ());
00111 
00112   int nrows = nInt + nCageReg;
00113 
00114   double wInt = 1.0;
00115   double wCageReg = parameter.smoothness;
00116 
00117   m_solver.assign (nrows, ncp, 3);
00118 
00119   unsigned row (0);
00120 
00121   assembleInterior (wInt, row);
00122 
00123   addCageRegularisation (wCageReg, row);
00124 }
00125 
00126 void
00127 FittingCurve::solve (double damp)
00128 {
00129   if (m_solver.solve ())
00130     updateCurve (damp);
00131 }
00132 
00133 void
00134 FittingCurve::updateCurve (double)
00135 {
00136   int cp_red = m_nurbs.m_order - 2;
00137   int ncp = m_nurbs.m_cv_count - 2 * cp_red;
00138 
00139   for (int j = 0; j < ncp; j++)
00140   {
00141 
00142     ON_3dPoint cp;
00143     cp.x = m_solver.x (j, 0);
00144     cp.y = m_solver.x (j, 1);
00145     cp.z = m_solver.x (j, 2);
00146 
00147     m_nurbs.SetCV (j + cp_red, cp);
00148   }
00149 
00150   for (int j = 0; j < cp_red; j++)
00151   {
00152 
00153     ON_3dPoint cp;
00154     m_nurbs.GetCV (m_nurbs.m_cv_count - 1 - cp_red + j, cp);
00155     m_nurbs.SetCV (j, cp);
00156 
00157     m_nurbs.GetCV (cp_red - j, cp);
00158     m_nurbs.SetCV (m_nurbs.m_cv_count - 1 - j, cp);
00159   }
00160 }
00161 
00162 void
00163 FittingCurve::addPointConstraint (const double &param, const Eigen::Vector3d &point, double weight, unsigned &row)
00164 {
00165   int cp_red = m_nurbs.m_order - 2;
00166   int ncp = m_nurbs.m_cv_count - 2 * cp_red;
00167   double *N = new double[m_nurbs.m_order * m_nurbs.m_order];
00168 
00169   int E = ON_NurbsSpanIndex (m_nurbs.m_order, m_nurbs.m_cv_count, m_nurbs.m_knot, param, 0, 0);
00170 
00171   ON_EvaluateNurbsBasis (m_nurbs.m_order, m_nurbs.m_knot + E, param, N);
00172 
00173   m_solver.f (row, 0, point (0) * weight);
00174   m_solver.f (row, 1, point (1) * weight);
00175   m_solver.f (row, 2, point (2) * weight);
00176 
00177   for (int i = 0; i < m_nurbs.m_order; i++)
00178     m_solver.K (row, (E + i) % ncp, weight * N[i]);
00179 
00180   row++;
00181 
00182   delete [] N;
00183 }
00184 
00185 void
00186 FittingCurve::addCageRegularisation (double weight, unsigned &row)
00187 {
00188   int cp_red = (m_nurbs.m_order - 2);
00189   int ncpj = (m_nurbs.m_cv_count - 2 * cp_red);
00190 
00191   m_solver.f (row, 0, 0.0);
00192   m_solver.f (row, 1, 0.0);
00193   m_solver.f (row, 2, 0.0);
00194 
00195   for (int j = 1; j < ncpj + 1; j++)
00196   {
00197 
00198     m_solver.K (row, (j + 0) % ncpj, -2.0 * weight);
00199     m_solver.K (row, (j - 1) % ncpj, 1.0 * weight);
00200     m_solver.K (row, (j + 1) % ncpj, 1.0 * weight);
00201 
00202     row++;
00203   }
00204 }
00205 
00206 ON_NurbsCurve
00207 FittingCurve::initNurbsCurve2D (int order, const vector_vec2d &data)
00208 {
00209   if (data.empty ())
00210     printf ("[FittingCurve::initNurbsCurve2D] Warning, no boundary parameters available\n");
00211 
00212   Eigen::Vector2d mean = NurbsTools::computeMean (data);
00213 
00214   unsigned s = unsigned (data.size ());
00215 
00216   double r (0.0);
00217   for (unsigned i = 0; i < s; i++)
00218   {
00219     Eigen::Vector2d d = data[i] - mean;
00220     double sn = d.squaredNorm ();
00221     if (sn > r)
00222       r = sn;
00223   }
00224   r = sqrt (r);
00225 
00226   int ncpsV (2 * order);
00227   ON_NurbsCurve nurbs = ON_NurbsCurve (3, false, order, ncpsV);
00228   nurbs.MakePeriodicUniformKnotVector (1.0 / (ncpsV - order + 1));
00229 
00230   double dcv = (2.0 * M_PI) / (ncpsV - order + 1);
00231   Eigen::Vector2d cv;
00232   for (int j = 0; j < ncpsV; j++)
00233   {
00234     cv (0) = r * sin (dcv * j);
00235     cv (1) = r * cos (dcv * j);
00236     cv = cv + mean;
00237     nurbs.SetCV (j, ON_3dPoint (cv (0), cv (1), 0.0));
00238   }
00239 
00240   return nurbs;
00241 }
00242 
00243 ON_NurbsCurve
00244 FittingCurve::initNurbsCurvePCA (int order, const vector_vec3d &data, int ncps, double rf)
00245 {
00246   if (data.empty ())
00247     printf ("[FittingCurve::initNurbsCurvePCA] Warning, no boundary parameters available\n");
00248 
00249   Eigen::Vector3d mean;
00250   Eigen::Matrix3d eigenvectors;
00251   Eigen::Vector3d eigenvalues;
00252 
00253   unsigned s = unsigned (data.size ());
00254 
00255   NurbsTools::pca (data, mean, eigenvectors, eigenvalues);
00256 
00257   eigenvalues = eigenvalues / s; // seems that the eigenvalues are dependent on the number of points (???)
00258 
00259   double r = rf * sqrt (eigenvalues (0));
00260 
00261   if (ncps < 2 * order)
00262     ncps = 2 * order;
00263 
00264   ON_NurbsCurve nurbs = ON_NurbsCurve (3, false, order, ncps);
00265   nurbs.MakePeriodicUniformKnotVector (1.0 / (ncps - order + 1));
00266 
00267   double dcv = (2.0 * M_PI) / (ncps - order + 1);
00268   Eigen::Vector3d cv, cv_t;
00269   for (int j = 0; j < ncps; j++)
00270   {
00271     cv (0) = r * sin (dcv * j);
00272     cv (1) = r * cos (dcv * j);
00273     cv (2) = 0.0;
00274     cv_t = eigenvectors * cv + mean;
00275     nurbs.SetCV (j, ON_3dPoint (cv_t (0), cv_t (1), cv_t (2)));
00276   }
00277 
00278   return nurbs;
00279 }
00280 
00281 std::vector<double>
00282 FittingCurve::getElementVector (const ON_NurbsCurve &nurbs)
00283 {
00284   std::vector<double> result;
00285 
00286   int idx_min = 0;
00287   int idx_max = nurbs.m_knot_capacity - 1;
00288   if (nurbs.IsClosed ())
00289   {
00290     idx_min = nurbs.m_order - 2;
00291     idx_max = nurbs.m_knot_capacity - nurbs.m_order + 1;
00292   }
00293 
00294   const double* knotsU = nurbs.Knot ();
00295 
00296   result.push_back (knotsU[idx_min]);
00297 
00298   //for(int E=(m_nurbs.m_order[0]-2); E<(m_nurbs.m_knot_capacity[0]-m_nurbs.m_order[0]+2); E++) {
00299   for (int E = idx_min + 1; E <= idx_max; E++)
00300   {
00301 
00302     if (knotsU[E] != knotsU[E - 1]) // do not count double knots
00303       result.push_back (knotsU[E]);
00304 
00305   }
00306 
00307   return result;
00308 }
00309 
00310 void
00311 FittingCurve::assembleInterior (double wInt, unsigned &row)
00312 {
00313   int nInt = int (m_data->interior.size ());
00314   m_data->interior_line_start.clear ();
00315   m_data->interior_line_end.clear ();
00316   m_data->interior_error.clear ();
00317   m_data->interior_normals.clear ();
00318   for (int p = 0; p < nInt; p++)
00319   {
00320     Eigen::Vector3d pcp (m_data->interior[p] (0), m_data->interior[p] (1), m_data->interior[p] (2));
00321 
00322     // inverse mapping
00323     double param;
00324     Eigen::Vector3d pt, t;
00325     double error;
00326     if (p < int (m_data->interior_param.size ()))
00327     {
00328       param = inverseMapping (m_nurbs, pcp, m_data->interior_param[p], error, pt, t, in_max_steps, in_accuracy);
00329       m_data->interior_param[p] = param;
00330     }
00331     else
00332     {
00333       param = findClosestElementMidPoint (m_nurbs, pcp);
00334       param = inverseMapping (m_nurbs, pcp, param, error, pt, t, in_max_steps, in_accuracy);
00335       m_data->interior_param.push_back (param);
00336     }
00337 
00338     m_data->interior_error.push_back (error);
00339     m_data->interior_line_start.push_back (pcp);
00340     m_data->interior_line_end.push_back (pt);
00341 
00342     addPointConstraint (m_data->interior_param[p], m_data->interior[p], wInt, row);
00343   }
00344 }
00345 
00346 double
00347 FittingCurve::inverseMapping (const ON_NurbsCurve &nurbs, const Eigen::Vector3d &pt, const double &hint, double &error,
00348                               Eigen::Vector3d &p, Eigen::Vector3d &t, int maxSteps, double accuracy, bool quiet)
00349 {
00350   //int cp_red = (nurbs.m_order - 2);
00351   //int ncpj = int (nurbs.m_cv_count - 2 * cp_red);
00352   double pointAndTangents[6];
00353 
00354   double current, delta;
00355   Eigen::Vector3d r;
00356   std::vector<double> elements = getElementVector (nurbs);
00357   double minU = elements[0];
00358   double maxU = elements[elements.size () - 1];
00359 
00360   current = hint;
00361 
00362   for (int k = 0; k < maxSteps; k++)
00363   {
00364 
00365     nurbs.Evaluate (current, 1, 3, pointAndTangents);
00366 
00367     p (0) = pointAndTangents[0];
00368     p (1) = pointAndTangents[1];
00369     p (2) = pointAndTangents[2];
00370 
00371     t (0) = pointAndTangents[3];
00372     t (1) = pointAndTangents[4];
00373     t (2) = pointAndTangents[5];
00374 
00375     r = p - pt;
00376 
00377     // step width control
00378     int E = findElement (current, elements);
00379     double e = elements[E + 1] - elements[E];
00380 
00381     delta = -(0.5 * e) * r.dot (t) / t.norm (); //  A.ldlt().solve(b);
00382 
00383     if (std::fabs (delta) < accuracy)
00384     {
00385 
00386       error = r.norm ();
00387       return current;
00388 
00389     }
00390     else
00391     {
00392       current = current + delta;
00393 
00394       if (current < minU)
00395         current = maxU - (minU - current);
00396       else if (current > maxU)
00397         current = minU + (current - maxU);
00398 
00399     }
00400 
00401   }
00402 
00403   error = r.norm ();
00404 
00405   if (!quiet)
00406   {
00407     printf ("[FittingCurve::inverseMapping] Warning: Method did not converge (%e %d).\n", accuracy, maxSteps);
00408     printf ("[FittingCurve::inverseMapping] hint: %f current: %f delta: %f error: %f\n", hint, current, delta, error);
00409   }
00410 
00411   return current;
00412 }
00413 
00414 double
00415 FittingCurve::findClosestElementMidPoint (const ON_NurbsCurve &nurbs, const Eigen::Vector3d &pt)
00416 {
00417   double hint (0.0);
00418   Eigen::Vector3d p, r;
00419   std::vector<double> elements = getElementVector (nurbs);
00420   double points[3];
00421 
00422   double d_shortest (DBL_MAX);
00423 
00424   for (unsigned i = 0; i < elements.size () - 1; i++)
00425   {
00426     double xi = elements[i] + 0.5 * (elements[i + 1] - elements[i]);
00427 
00428     nurbs.Evaluate (xi, 0, 3, points);
00429     p (0) = points[0];
00430     p (1) = points[1];
00431     p (2) = points[2];
00432 
00433     r = p - pt;
00434 
00435     double d = r.squaredNorm ();
00436 
00437     if (d < d_shortest)
00438     {
00439       d_shortest = d;
00440       hint = xi;
00441     }
00442   }
00443 
00444   return hint;
00445 }
00446 


pcl
Author(s): Open Perception
autogenerated on Wed Aug 26 2015 15:24:09