00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include <pcl/surface/on_nurbs/sequential_fitter.h>
00039
00040 using namespace pcl;
00041 using namespace on_nurbs;
00042 using namespace Eigen;
00043
00044 SequentialFitter::Parameter::Parameter (int order, int refinement, int iterationsQuad, int iterationsBoundary,
00045 int iterationsAdjust, int iterationsInterior, double forceBoundary,
00046 double forceBoundaryInside, double forceInterior, double stiffnessBoundary,
00047 double stiffnessInterior, int resolution)
00048 {
00049 this->order = order;
00050 this->refinement = refinement;
00051 this->iterationsQuad = iterationsQuad;
00052 this->iterationsBoundary = iterationsBoundary;
00053 this->iterationsAdjust = iterationsAdjust;
00054 this->iterationsInterior = iterationsInterior;
00055 this->forceBoundary = forceBoundary;
00056 this->forceBoundaryInside = forceBoundaryInside;
00057 this->forceInterior = forceInterior;
00058 this->stiffnessBoundary = stiffnessBoundary;
00059 this->stiffnessInterior = stiffnessInterior;
00060 this->resolution = resolution;
00061 }
00062
00063 SequentialFitter::SequentialFitter (Parameter p)
00064 {
00065 this->m_params = p;
00066 this->m_have_cloud = false;
00067 this->m_have_corners = false;
00068 this->m_surf_id = -1;
00069 }
00070
00071 void
00072 SequentialFitter::compute_quadfit ()
00073 {
00074 ON_NurbsSurface nurbs;
00075
00076 if (m_have_corners)
00077 {
00078 nurbs = FittingSurface::initNurbs4Corners (2, m_corners[0], m_corners[1], m_corners[2], m_corners[3]);
00079 }
00080 else
00081 {
00082 nurbs = FittingSurface::initNurbsPCA (2, &m_data);
00083 nurbs.GetCV (0, 0, m_corners[0]);
00084 nurbs.GetCV (1, 0, m_corners[1]);
00085 nurbs.GetCV (1, 1, m_corners[2]);
00086 nurbs.GetCV (0, 1, m_corners[3]);
00087 Eigen::Vector3d v0 (m_corners[0].x, m_corners[0].y, m_corners[0].z);
00088 Eigen::Vector3d v1 (m_corners[1].x, m_corners[1].y, m_corners[1].z);
00089 Eigen::Vector3d v2 (m_corners[2].x, m_corners[2].y, m_corners[2].z);
00090 Eigen::Vector3d v3 (m_corners[3].x, m_corners[3].y, m_corners[3].z);
00091 if (is_back_facing (v0, v1, v2, v3))
00092 {
00093 ON_3dPoint tmp[4];
00094 tmp[0] = m_corners[0];
00095 tmp[1] = m_corners[1];
00096 tmp[2] = m_corners[2];
00097 tmp[3] = m_corners[3];
00098 m_corners[3] = tmp[0];
00099 m_corners[2] = tmp[1];
00100 m_corners[1] = tmp[2];
00101 m_corners[0] = tmp[3];
00102 nurbs = FittingSurface::initNurbs4Corners (2, m_corners[0], m_corners[1], m_corners[2], m_corners[3]);
00103 }
00104 }
00105
00106 FittingSurface fitting (&m_data, nurbs);
00107
00108 FittingSurface::Parameter paramFP (m_params.forceInterior, 1.0, 0.0, m_params.forceBoundary, 1.0, 0.0);
00109
00110
00111
00112
00113 for (int r = 0; r < m_params.iterationsQuad; r++)
00114 {
00115 fitting.assemble (paramFP);
00116 fitting.solve ();
00117 }
00118 fitting.m_nurbs.GetCV (0, 0, m_corners[0]);
00119 fitting.m_nurbs.GetCV (1, 0, m_corners[1]);
00120 fitting.m_nurbs.GetCV (1, 1, m_corners[2]);
00121 fitting.m_nurbs.GetCV (0, 1, m_corners[3]);
00122 }
00123
00124 void
00125 SequentialFitter::compute_refinement (FittingSurface* fitting)
00126 {
00127
00128 FittingSurface::Parameter paramFP (0.0, 1.0, 0.0, m_params.forceBoundary, m_params.stiffnessBoundary, 0.0);
00129
00130 for (int r = 0; r < m_params.refinement; r++)
00131 {
00132 fitting->assemble (paramFP);
00133 fitting->solve ();
00134 fitting->refine (0);
00135 fitting->refine (1);
00136 }
00137 }
00138
00139 void
00140 SequentialFitter::compute_boundary (FittingSurface* fitting)
00141 {
00142 FittingSurface::Parameter paramFP (0.0, 1.0, 0.0, m_params.forceBoundary, m_params.stiffnessBoundary, 0.0);
00143
00144
00145 for (int i = 0; i < m_params.iterationsBoundary; i++)
00146 {
00147 fitting->assemble (paramFP);
00148 fitting->solve ();
00149 }
00150 }
00151 void
00152 SequentialFitter::compute_interior (FittingSurface* fitting)
00153 {
00154
00155
00156 FittingSurface::Parameter paramFP (m_params.forceInterior, m_params.stiffnessInterior, 0.0, m_params.forceBoundary,
00157 m_params.stiffnessBoundary, 0.0);
00158
00159 for (int i = 0; i < m_params.iterationsInterior; i++)
00160 {
00161 fitting->assemble (paramFP);
00162
00163 fitting->solve ();
00164 }
00165 }
00166
00167 Eigen::Vector2d
00168 SequentialFitter::project (const Eigen::Vector3d &pt)
00169 {
00170 Eigen::Vector4d pr (m_intrinsic * m_extrinsic * Eigen::Vector4d (pt (0), pt (1), pt (2), 1.0));
00171 pr (0) = pr (0) / pr (2);
00172 pr (1) = pr (1) / pr (2);
00173 if (pt.dot (Eigen::Vector3d (m_extrinsic (0, 2), m_extrinsic (1, 2), m_extrinsic (2, 2))) < 0.0f)
00174 {
00175 pr (0) = -pr (0);
00176 pr (1) = -pr (1);
00177 }
00178 return Eigen::Vector2d (pr (0), pr (1));
00179 }
00180
00181 bool
00182 SequentialFitter::is_back_facing (const Eigen::Vector3d &v0, const Eigen::Vector3d &v1, const Eigen::Vector3d &v2,
00183 const Eigen::Vector3d &)
00184 {
00185 Eigen::Vector3d e1, e2, e3;
00186 e1 = v1 - v0;
00187 e2 = v2 - v0;
00188
00189 Eigen::Vector3d z (m_extrinsic (0, 2), m_extrinsic (1, 2), m_extrinsic (2, 2));
00190 if (z.dot (e1.cross (e2)) > 0.0)
00191 return true;
00192 else
00193 return false;
00194 }
00195
00196
00199 void
00200 SequentialFitter::setInputCloud (pcl::PointCloud<pcl::PointXYZRGB>::Ptr &pcl_cloud)
00201 {
00202 if (pcl_cloud.get () == 0 || pcl_cloud->points.size () == 0)
00203 throw std::runtime_error ("[SequentialFitter::setInputCloud] Error: Empty or invalid pcl-point-cloud.\n");
00204
00205 m_cloud = pcl_cloud;
00206 m_have_cloud = true;
00207 }
00208
00209 void
00210 SequentialFitter::setBoundary (pcl::PointIndices::Ptr &pcl_cloud_indexes)
00211 {
00212 if (m_cloud.get () == 0 || m_cloud->points.size () == 0)
00213 throw std::runtime_error ("[SequentialFitter::setBoundary] Error: Empty or invalid pcl-point-cloud.\n");
00214
00215 this->m_boundary_indices = pcl_cloud_indexes;
00216 m_data.clear_boundary ();
00217 PCL2ON (m_cloud, pcl_cloud_indexes->indices, m_data.boundary);
00218 }
00219
00220 void
00221 SequentialFitter::setInterior (pcl::PointIndices::Ptr &pcl_cloud_indexes)
00222 {
00223 if (m_cloud.get () == 0 || m_cloud->points.size () == 0)
00224 throw std::runtime_error ("[SequentialFitter::setIndices] Error: Empty or invalid pcl-point-cloud.\n");
00225
00226 this->m_interior_indices = pcl_cloud_indexes;
00227 m_data.clear_interior ();
00228 PCL2ON (m_cloud, pcl_cloud_indexes->indices, m_data.interior);
00229 }
00230
00231 void
00232 SequentialFitter::setCorners (pcl::PointIndices::Ptr &corners, bool flip_on_demand)
00233 {
00234 if (m_cloud.get () == 0 || m_cloud->points.size () == 0)
00235 throw std::runtime_error ("[SequentialFitter::setCorners] Error: Empty or invalid pcl-point-cloud.\n");
00236
00237 if (corners->indices.size () < 4)
00238 throw std::runtime_error ("[SequentialFitter::setCorners] Error: to few corners (<4)\n");
00239
00240 if (corners->indices.size () > 4)
00241 printf ("[SequentialFitter::setCorners] Warning: to many corners (>4)\n");
00242
00243 bool flip = false;
00244 pcl::PointXYZRGB &pt0 = m_cloud->at (corners->indices[0]);
00245 pcl::PointXYZRGB &pt1 = m_cloud->at (corners->indices[1]);
00246 pcl::PointXYZRGB &pt2 = m_cloud->at (corners->indices[2]);
00247 pcl::PointXYZRGB &pt3 = m_cloud->at (corners->indices[3]);
00248
00249 if (flip_on_demand)
00250 {
00251 Eigen::Vector3d v0 (pt0.x, pt0.y, pt0.z);
00252 Eigen::Vector3d v1 (pt1.x, pt1.y, pt1.z);
00253 Eigen::Vector3d v2 (pt2.x, pt2.y, pt2.z);
00254 Eigen::Vector3d v3 (pt3.x, pt3.y, pt3.z);
00255 flip = is_back_facing (v0, v1, v2, v3);
00256 }
00257
00258 if (flip)
00259 {
00260 m_corners[3] = ON_3dPoint (pt0.x, pt0.y, pt0.z);
00261 m_corners[2] = ON_3dPoint (pt1.x, pt1.y, pt1.z);
00262 m_corners[1] = ON_3dPoint (pt2.x, pt2.y, pt2.z);
00263 m_corners[0] = ON_3dPoint (pt3.x, pt3.y, pt3.z);
00264 }
00265 else
00266 {
00267 m_corners[0] = ON_3dPoint (pt0.x, pt0.y, pt0.z);
00268 m_corners[1] = ON_3dPoint (pt1.x, pt1.y, pt1.z);
00269 m_corners[2] = ON_3dPoint (pt2.x, pt2.y, pt2.z);
00270 m_corners[3] = ON_3dPoint (pt3.x, pt3.y, pt3.z);
00271 }
00272
00273 m_have_corners = true;
00274 }
00275
00276 void
00277 SequentialFitter::setProjectionMatrix (
00278 const Eigen::Matrix4d &intrinsic,
00279 const Eigen::Matrix4d &extrinsic)
00280 {
00281 m_intrinsic = intrinsic;
00282 m_extrinsic = extrinsic;
00283 }
00284
00285 ON_NurbsSurface
00286 SequentialFitter::compute (bool assemble)
00287 {
00288 FittingSurface* fitting;
00289 ON_NurbsSurface nurbs;
00290 FittingSurface::Parameter paramFP (m_params.forceInterior, m_params.stiffnessInterior, 0.0, m_params.forceBoundary,
00291 m_params.stiffnessBoundary, 0.0);
00292
00293
00294
00295
00296
00297 if (m_data.boundary.size () > 0)
00298 {
00299
00300
00301 compute_quadfit ();
00302
00303 nurbs = FittingSurface::initNurbs4Corners (m_params.order, m_corners[0], m_corners[1], m_corners[2], m_corners[3]);
00304 fitting = new FittingSurface (&m_data, nurbs);
00305 if (assemble)
00306 fitting->assemble (paramFP);
00307
00308 compute_refinement (fitting);
00309
00310 compute_boundary (fitting);
00311 }
00312 else
00313 {
00314 if (this->m_have_corners)
00315 {
00316 nurbs
00317 = FittingSurface::initNurbs4Corners (m_params.order, m_corners[0], m_corners[1], m_corners[2], m_corners[3]);
00318 fitting = new FittingSurface (&m_data, nurbs);
00319 if (assemble)
00320 fitting->assemble (paramFP);
00321 }
00322 else
00323 {
00324 fitting = new FittingSurface (m_params.order, &m_data);
00325 if (assemble)
00326 fitting->assemble (m_params.stiffnessInterior);
00327 int ncv0 = fitting->m_nurbs.m_cv_count[0];
00328 int ncv1 = fitting->m_nurbs.m_cv_count[1];
00329
00330 fitting->m_nurbs.GetCV (0, 0, m_corners[0]);
00331 fitting->m_nurbs.GetCV (ncv0 - 1, 0, m_corners[1]);
00332 fitting->m_nurbs.GetCV (ncv0 - 1, ncv1 - 1, m_corners[2]);
00333 fitting->m_nurbs.GetCV (0, ncv1 - 1, m_corners[3]);
00334
00335 Eigen::Vector3d v0 (m_corners[0].x, m_corners[0].y, m_corners[0].z);
00336 Eigen::Vector3d v1 (m_corners[1].x, m_corners[1].y, m_corners[1].z);
00337 Eigen::Vector3d v2 (m_corners[2].x, m_corners[2].y, m_corners[2].z);
00338 Eigen::Vector3d v3 (m_corners[3].x, m_corners[3].y, m_corners[3].z);
00339
00340 if (is_back_facing (v0, v1, v2, v3))
00341 {
00342 ON_3dPoint tmp[4];
00343 tmp[0] = m_corners[0];
00344 tmp[1] = m_corners[1];
00345 tmp[2] = m_corners[2];
00346 tmp[3] = m_corners[3];
00347 m_corners[3] = tmp[0];
00348 m_corners[2] = tmp[1];
00349 m_corners[1] = tmp[2];
00350 m_corners[0] = tmp[3];
00351 delete (fitting);
00352 nurbs = FittingSurface::initNurbs4Corners (m_params.order, m_corners[0], m_corners[1], m_corners[2],
00353 m_corners[3]);
00354 fitting = new FittingSurface (&m_data, nurbs);
00355 if (assemble)
00356 fitting->assemble (paramFP);
00357 }
00358
00359 for (int r = 0; r < m_params.refinement; r++)
00360 {
00361 fitting->refine (0);
00362 fitting->refine (1);
00363 }
00364 }
00365 }
00366
00367 if (m_data.interior.size () > 0)
00368 compute_interior (fitting);
00369
00370
00371 fitting->assemble ();
00372
00373 m_nurbs = fitting->m_nurbs;
00374
00375 delete (fitting);
00376
00377 return m_nurbs;
00378 }
00379
00380 ON_NurbsSurface
00381 SequentialFitter::compute_boundary (const ON_NurbsSurface &nurbs)
00382 {
00383 if (m_data.boundary.size () <= 0)
00384 {
00385 printf ("[SequentialFitter::compute_boundary] Warning, no boundary points given: setBoundary()\n");
00386 return nurbs;
00387 }
00388
00389 FittingSurface *fitting = new FittingSurface (&m_data, nurbs);
00390
00391 this->compute_boundary (fitting);
00392
00393 m_nurbs = fitting->m_nurbs;
00394
00395
00396 fitting->assemble ();
00397
00398 delete (fitting);
00399
00400 return m_nurbs;
00401 }
00402
00403 ON_NurbsSurface
00404 SequentialFitter::compute_interior (const ON_NurbsSurface &nurbs)
00405 {
00406 if (m_data.boundary.size () <= 0)
00407 {
00408 printf ("[SequentialFitter::compute_interior] Warning, no interior points given: setInterior()\n");
00409 return nurbs;
00410 }
00411 FittingSurface *fitting = new FittingSurface (&m_data, nurbs);
00412
00413 this->compute_interior (fitting);
00414
00415
00416 fitting->assemble ();
00417
00418 m_nurbs = fitting->m_nurbs;
00419
00420 delete (fitting);
00421
00422 return m_nurbs;
00423 }
00424
00425 void
00426 SequentialFitter::getInteriorError (std::vector<double> &error)
00427 {
00428 error = m_data.interior_error;
00429 }
00430
00431 void
00432 SequentialFitter::getBoundaryError (std::vector<double> &error)
00433 {
00434 error = m_data.boundary_error;
00435 }
00436
00437 void
00438 SequentialFitter::getInteriorParams (std::vector<Eigen::Vector2d, Eigen::aligned_allocator<Eigen::Vector2d> > ¶ms)
00439 {
00440 params = m_data.interior_param;
00441 }
00442
00443 void
00444 SequentialFitter::getBoundaryParams (std::vector<Eigen::Vector2d, Eigen::aligned_allocator<Eigen::Vector2d> > ¶ms)
00445 {
00446 params = m_data.boundary_param;
00447 }
00448
00449 void
00450 SequentialFitter::getInteriorNormals (std::vector<Eigen::Vector3d, Eigen::aligned_allocator<Eigen::Vector3d> > &normals)
00451 {
00452 normals = m_data.interior_normals;
00453 }
00454
00455 void
00456 SequentialFitter::getBoundaryNormals (std::vector<Eigen::Vector3d, Eigen::aligned_allocator<Eigen::Vector3d> > &normals)
00457 {
00458 normals = m_data.boundary_normals;
00459 }
00460
00461 void
00462 SequentialFitter::getClosestPointOnNurbs (
00463 ON_NurbsSurface nurbs,
00464 const Eigen::Vector3d &pt,
00465 Eigen::Vector2d& params,
00466 int maxSteps,
00467 double accuracy)
00468 {
00469 Eigen::Vector3d p, tu, tv;
00470 double error;
00471
00472 params = FittingSurface::findClosestElementMidPoint (nurbs, pt);
00473 params = FittingSurface::inverseMapping (nurbs, pt, params, error, p, tu, tv, maxSteps, accuracy);
00474 }
00475
00476 ON_NurbsSurface
00477 SequentialFitter::grow (float max_dist, float max_angle, unsigned min_length, unsigned max_length)
00478 {
00479 unsigned num_bnd = unsigned (this->m_data.boundary_param.size ());
00480
00481 if (num_bnd == 0)
00482 throw std::runtime_error ("[SequentialFitter::grow] No boundary given.");
00483
00484 if (unsigned (this->m_data.boundary.size ()) != num_bnd)
00485 {
00486 printf ("[SequentialFitter::grow] %zu %u\n", this->m_data.boundary.size (), num_bnd);
00487 throw std::runtime_error ("[SequentialFitter::grow] size of boundary and boundary parameters do not match.");
00488 }
00489
00490 if (this->m_boundary_indices->indices.size () != num_bnd)
00491 {
00492 printf ("[SequentialFitter::grow] %zu %u\n", this->m_boundary_indices->indices.size (), num_bnd);
00493 throw std::runtime_error ("[SequentialFitter::grow] size of boundary indices and boundary parameters do not match.");
00494 }
00495
00496 float angle = cosf (max_angle);
00497 unsigned bnd_moved (0);
00498
00499 for (unsigned i = 0; i < num_bnd; i++)
00500 {
00501 Eigen::Vector3d r, tu, tv, n, bn;
00502 double pointAndTangents[9];
00503 double u = this->m_data.boundary_param[i] (0);
00504 double v = this->m_data.boundary_param[i] (1);
00505
00506
00507 m_nurbs.Evaluate (u, v, 1, 3, pointAndTangents);
00508 r (0) = pointAndTangents[0];
00509 r (1) = pointAndTangents[1];
00510 r (2) = pointAndTangents[2];
00511 tu (0) = pointAndTangents[3];
00512 tu (1) = pointAndTangents[4];
00513 tu (2) = pointAndTangents[5];
00514 tv (0) = pointAndTangents[6];
00515 tv (1) = pointAndTangents[7];
00516 tv (2) = pointAndTangents[8];
00517
00518 n = tu.cross (tv);
00519 n.normalize ();
00520
00521
00522 double eps = 0.00000001;
00523
00524 if (u < eps)
00525 bn = n.cross (tv);
00526 if (u > 1.0 - eps)
00527 bn = -n.cross (tv);
00528 if (v < eps)
00529 bn = -n.cross (tu);
00530 if (v > 1.0 - eps)
00531 bn = n.cross (tu);
00532
00533 bn.normalize ();
00534
00535 Eigen::Vector3d e (r (0) + bn (0) * max_dist, r (1) + bn (1) * max_dist, r (2) + bn (2) * max_dist);
00536
00537
00538 Eigen::Vector2d ri = this->project (r);
00539 Eigen::Vector2d ei = this->project (e);
00540 Eigen::Vector2d bni = ei - ri;
00541 bni.normalize ();
00542
00543
00544 float max_dist_sq = max_dist * max_dist;
00545 bool valid = false;
00546 pcl::PointXYZRGB point = m_cloud->at (this->m_boundary_indices->indices[i]);
00547 for (unsigned j = min_length; j < max_length; j++)
00548 {
00549 int col = int (ri (0) + bni (0) * j);
00550 int row = int (ri (1) + bni (1) * j);
00551
00552 if (row >= int (m_cloud->height) || row < 0)
00553 {
00554 j = max_length;
00555 break;
00556 }
00557 if (col >= int (m_cloud->width) || col < 0)
00558 {
00559 j = max_length;
00560 break;
00561 }
00562
00563 unsigned idx = row * m_cloud->width + col;
00564
00565 pcl::PointXYZRGB &pt = m_cloud->at (idx);
00566 if (!pcl_isnan (pt.x) && !pcl_isnan (pt.y) && !pcl_isnan (pt.z))
00567 {
00568
00569
00570 Eigen::Vector3d d (pt.x - r (0), pt.y - r (1), pt.z - r (2));
00571 if (d.dot (d) < max_dist_sq)
00572 {
00573 d.normalize ();
00574 if (std::abs (d.dot (bn)) > (angle))
00575 {
00576 valid = true;
00577 point = pt;
00578 }
00579 }
00580 }
00581 }
00582
00583
00584 if (valid)
00585 {
00586 this->m_data.interior.push_back (this->m_data.boundary[i]);
00587 this->m_data.boundary[i] (0) = point.x;
00588 this->m_data.boundary[i] (1) = point.y;
00589 this->m_data.boundary[i] (2) = point.z;
00590 bnd_moved++;
00591 }
00592
00593 }
00594
00595 compute_interior (m_nurbs);
00596
00597 double int_err (0.0);
00598 double div_err = 1.0 / double (m_data.interior_error.size ());
00599 for (unsigned i = 0; i < m_data.interior_error.size (); i++)
00600 {
00601 int_err += (m_data.interior_error[i] * div_err);
00602 }
00603
00604 printf ("[SequentialFitter::grow] average interior error: %e\n", int_err);
00605
00606 return m_nurbs;
00607
00608 }
00609
00610 unsigned
00611 SequentialFitter::PCL2ON (pcl::PointCloud<pcl::PointXYZRGB>::Ptr &pcl_cloud, const std::vector<int> &indices,
00612 vector_vec3d &on_cloud)
00613 {
00614 unsigned numPoints (0);
00615
00616 for (unsigned i = 0; i < indices.size (); i++)
00617 {
00618
00619 pcl::PointXYZRGB &pt = pcl_cloud->at (indices[i]);
00620
00621 if (!pcl_isnan (pt.x) && !pcl_isnan (pt.y) && !pcl_isnan (pt.z))
00622 {
00623 on_cloud.push_back (Eigen::Vector3d (pt.x, pt.y, pt.z));
00624 numPoints++;
00625 }
00626
00627 }
00628
00629 return numPoints;
00630 }