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/fitting_surface_im.h>
00039 #include <stdexcept>
00040
00041 using namespace pcl;
00042 using namespace on_nurbs;
00043
00044 pcl::PointXYZRGB
00045 FittingSurfaceIM::computeMean () const
00046 {
00047 pcl::PointXYZRGB u;
00048 u.x = 0.0;
00049 u.y = 0.0;
00050 u.z = 0.0;
00051
00052 double ds = 1.0 / double (m_indices.size ());
00053
00054 const pcl::PointCloud<pcl::PointXYZRGB> &cloud_ref = *m_cloud;
00055 for (size_t idx = 0; idx < m_indices.size (); idx++)
00056 {
00057 int i = m_indices[idx] % cloud_ref.width;
00058 int j = m_indices[idx] / cloud_ref.width;
00059
00060 const pcl::PointXYZRGB &point = cloud_ref (i, j);
00061 if (pcl_isnan (point.x) || pcl_isnan (point.y) || pcl_isnan (point.z))
00062 continue;
00063
00064 u.x += point.x * float (ds);
00065 u.y += point.y * float (ds);
00066 u.z += point.z * float (ds);
00067 }
00068
00069 return u;
00070 }
00071
00072 Eigen::Vector4d
00073 FittingSurfaceIM::computeIndexBoundingBox (pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud,
00074 const std::vector<int> &indices)
00075 {
00076 Eigen::Vector4d bb = Eigen::Vector4d (DBL_MAX, 0, DBL_MAX, 0);
00077 const pcl::PointCloud<pcl::PointXYZRGB> &cloud_ref = *cloud;
00078
00079 for (size_t idx = 0; idx < indices.size (); idx++)
00080 {
00081 int i = indices[idx] % cloud_ref.width;
00082 int j = indices[idx] / cloud_ref.width;
00083
00084 const pcl::PointXYZRGB &point = cloud_ref (i, j);
00085 if (pcl_isnan (point.x) || pcl_isnan (point.y) || pcl_isnan (point.z))
00086 continue;
00087
00088 if (i < bb (0))
00089 bb (0) = i;
00090 if (i > bb (1))
00091 bb (1) = i;
00092 if (j < bb (2))
00093 bb (2) = j;
00094 if (j > bb (3))
00095 bb (3) = j;
00096 }
00097 return bb;
00098 }
00099
00100 void
00101 FittingSurfaceIM::setInputCloud (pcl::PointCloud<pcl::PointXYZRGB>::Ptr _cloud)
00102 {
00103 m_cloud = _cloud;
00104 }
00105
00106 void
00107 FittingSurfaceIM::setIndices (std::vector<int> &_indices)
00108 {
00109 m_indices = _indices;
00110 }
00111
00112 void
00113 FittingSurfaceIM::setCamera (const Eigen::Matrix3d &i)
00114 {
00115 m_intrinsic = i;
00116 }
00117
00118 void
00119 FittingSurfaceIM::setCamera (const Eigen::Matrix3f &i)
00120 {
00121 printf("[FittingSurfaceIM::setCamera] Warning, this function is not tested!\n");
00122 m_intrinsic << i (0, 0), i (0, 1), i (0, 2), i (1, 0), i (1, 1), i (1, 2), i (2, 0), i (2, 1), i (2, 2);
00123 }
00124
00125 std::vector<double>
00126 FittingSurfaceIM::getElementVector (const ON_NurbsSurface &nurbs, int dim)
00127 {
00128 std::vector<double> result;
00129
00130 int idx_min = 0;
00131 int idx_max = nurbs.KnotCount (dim) - 1;
00132 if (nurbs.IsClosed (dim))
00133 {
00134 idx_min = nurbs.Order (dim) - 2;
00135 idx_max = nurbs.KnotCount (dim) - nurbs.Order (dim) + 1;
00136 }
00137
00138 const double* knots = nurbs.Knot (dim);
00139
00140 result.push_back (knots[idx_min]);
00141
00142
00143 for (int E = idx_min + 1; E <= idx_max; E++)
00144 {
00145
00146 if (knots[E] != knots[E - 1])
00147 result.push_back (knots[E]);
00148
00149 }
00150
00151 return result;
00152 }
00153
00154 void
00155 FittingSurfaceIM::refine ()
00156 {
00157 {
00158 int dim = 0;
00159 std::vector<double> elements = getElementVector (m_nurbs, dim);
00160 for (unsigned i = 0; i < elements.size () - 1; i++)
00161 {
00162 double xi = elements[i] + 0.5 * (elements[i + 1] - elements[i]);
00163 m_nurbs.InsertKnot (dim, xi, 1);
00164 }
00165 }
00166 {
00167 int dim = 1;
00168 std::vector<double> elements = getElementVector (m_nurbs, dim);
00169 for (unsigned i = 0; i < elements.size () - 1; i++)
00170 {
00171 double xi = elements[i] + 0.5 * (elements[i + 1] - elements[i]);
00172 m_nurbs.InsertKnot (dim, xi, 1);
00173 }
00174 }
00175
00176 Eigen::Vector2d bbx (m_nurbs.Knot (0, 0), m_nurbs.Knot (0, m_nurbs.KnotCount (0) - 1));
00177 Eigen::Vector2d bby (m_nurbs.Knot (1, 0), m_nurbs.Knot (1, m_nurbs.KnotCount (1) - 1));
00178
00179 int dx = int (bbx (1) - bbx (0));
00180 int dy = int (bby (1) - bby (0));
00181 double ddx = double (dx) / (m_nurbs.CVCount (0) - 1);
00182 double ddy = double (dy) / (m_nurbs.CVCount (1) - 1);
00183
00184 m_cps_px.clear ();
00185 for (int i = 0; i < m_nurbs.CVCount (0); i++)
00186 {
00187 for (int j = 0; j < m_nurbs.CVCount (1); j++)
00188 {
00189 int px = int (bbx (0) + ddx * i);
00190 int py = int (bby (0) + ddy * j);
00191 m_cps_px.push_back (Eigen::Vector2i (px, py));
00192 }
00193 }
00194 }
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204 void
00205 FittingSurfaceIM::initSurface (int order, const Eigen::Vector4d &bb)
00206 {
00207 m_cps_px.clear ();
00208
00209
00210 m_bb = bb;
00211
00212 pcl::PointXYZRGB pt = computeMean ();
00213
00214 double dx = bb (1) - bb (0);
00215 double dy = bb (3) - bb (2);
00216 double ddx = dx / (order - 1);
00217 double ddy = dy / (order - 1);
00218
00219
00220
00221
00222 m_nurbs = ON_NurbsSurface (3, false, order, order, order, order);
00223 m_nurbs.MakeClampedUniformKnotVector (0, dx);
00224 m_nurbs.MakeClampedUniformKnotVector (1, dy);
00225
00226 for (int i = 0; i < m_nurbs.KnotCount (0); i++)
00227 {
00228 double k = m_nurbs.Knot (0, i);
00229 m_nurbs.SetKnot (0, i, k + m_bb (0));
00230 }
00231
00232 for (int i = 0; i < m_nurbs.KnotCount (1); i++)
00233 {
00234 double k = m_nurbs.Knot (1, i);
00235 m_nurbs.SetKnot (1, i, k + m_bb (2));
00236 }
00237
00238 for (int i = 0; i < m_nurbs.Order (0); i++)
00239 {
00240 for (int j = 0; j < m_nurbs.Order (1); j++)
00241 {
00242 int px = int (m_bb (0) + ddx * i + 0.5);
00243 int py = int (m_bb (2) + ddy * j + 0.5);
00244
00245 m_cps_px.push_back (Eigen::Vector2i (px, py));
00246
00247 ON_3dPoint p;
00248 p.x = pt.z * (px - m_intrinsic (0, 2)) / m_intrinsic (0, 0);
00249 p.y = pt.z * (py - m_intrinsic (1, 2)) / m_intrinsic (1, 1);
00250 p.z = pt.z;
00251
00252 m_nurbs.SetCV (i, j, p);
00253 }
00254 }
00255
00256
00257
00258 }
00259
00260 void
00261 FittingSurfaceIM::assemble (bool inverse_mapping)
00262 {
00263 int nInt = int (m_indices.size ());
00264 int nCageReg = (m_nurbs.m_cv_count[0] - 2) * (m_nurbs.m_cv_count[1] - 2);
00265 int nCageRegBnd = 2 * (m_nurbs.m_cv_count[0] - 1) + 2 * (m_nurbs.m_cv_count[1] - 1);
00266
00267 int rows = nInt + nCageReg + nCageRegBnd;
00268 int ncps = m_nurbs.CVCount ();
00269
00270 m_solver.assign (rows, ncps, 1);
00271
00272 unsigned row (0);
00273
00274
00275 const pcl::PointCloud<pcl::PointXYZRGB> &cloud_ref = *m_cloud;
00276 for (size_t i = 0; i < m_indices.size (); i++)
00277 {
00278 int px = m_indices[i] % cloud_ref.width;
00279 int py = m_indices[i] / cloud_ref.width;
00280
00281 const pcl::PointXYZRGB &pt = cloud_ref.at (m_indices[i]);
00282 Eigen::Vector2i params (px, py);
00283
00284 if (pcl_isnan (pt.z) || pt.z == 0.0)
00285 throw std::runtime_error ("[FittingSurfaceIM::assemble] Error, not a number (pt.z)");
00286
00287 if (inverse_mapping)
00288 {
00289 Eigen::Vector3d point (pt.x, pt.y, pt.z);
00290 double error;
00291 Eigen::Vector3d p, tu, tv;
00292 Eigen::Vector2d params1 (params (0), params (1));
00293 params1 = inverseMapping (m_nurbs, point, params1, error, p, tu, tv, 200, 1e-6, true);
00294 params (0) = int (params1 (0));
00295 params (1) = int (params1 (1));
00296 }
00297
00298 addPointConstraint (params, pt.z, 1.0, row);
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308 }
00309
00310
00311 double smooth (0.1);
00312 if (nCageReg > 0)
00313 addCageInteriorRegularisation (smooth, row);
00314
00315 if (nCageRegBnd > 0)
00316 {
00317 addCageBoundaryRegularisation (smooth, NORTH, row);
00318 addCageBoundaryRegularisation (smooth, SOUTH, row);
00319 addCageBoundaryRegularisation (smooth, WEST, row);
00320 addCageBoundaryRegularisation (smooth, EAST, row);
00321 addCageCornerRegularisation (smooth * 2.0, row);
00322 }
00323
00324
00325
00326
00327
00328
00329 }
00330
00331 void
00332 FittingSurfaceIM::addPointConstraint (const Eigen::Vector2i ¶ms, double z, double weight, unsigned &row)
00333 {
00334 double *N0 = new double[m_nurbs.Order (0) * m_nurbs.Order (0)];
00335 double *N1 = new double[m_nurbs.Order (1) * m_nurbs.Order (1)];
00336
00337 int E = ON_NurbsSpanIndex (m_nurbs.m_order[0], m_nurbs.m_cv_count[0], m_nurbs.m_knot[0], params (0), 0, 0);
00338 int F = ON_NurbsSpanIndex (m_nurbs.m_order[1], m_nurbs.m_cv_count[1], m_nurbs.m_knot[1], params (1), 0, 0);
00339
00340 ON_EvaluateNurbsBasis (m_nurbs.Order (0), m_nurbs.m_knot[0] + E, params (0), N0);
00341 ON_EvaluateNurbsBasis (m_nurbs.Order (1), m_nurbs.m_knot[1] + F, params (1), N1);
00342
00343 m_solver.f (row, 0, z * weight);
00344
00345 for (int i = 0; i < m_nurbs.Order (0); i++)
00346 {
00347
00348 for (int j = 0; j < m_nurbs.Order (1); j++)
00349 {
00350
00351 m_solver.K (row, lrc2gl (E, F, i, j), weight * N0[i] * N1[j]);
00352
00353 }
00354
00355 }
00356
00357 row++;
00358
00359 delete [] N1;
00360 delete [] N0;
00361 }
00362
00363 void
00364 FittingSurfaceIM::addCageInteriorRegularisation (double weight, unsigned &row)
00365 {
00366 for (int i = 1; i < (m_nurbs.m_cv_count[0] - 1); i++)
00367 {
00368 for (int j = 1; j < (m_nurbs.m_cv_count[1] - 1); j++)
00369 {
00370
00371 m_solver.f (row, 0, 0.0);
00372
00373 m_solver.K (row, grc2gl (i + 0, j + 0), -4.0 * weight);
00374 m_solver.K (row, grc2gl (i + 0, j - 1), 1.0 * weight);
00375 m_solver.K (row, grc2gl (i + 0, j + 1), 1.0 * weight);
00376 m_solver.K (row, grc2gl (i - 1, j + 0), 1.0 * weight);
00377 m_solver.K (row, grc2gl (i + 1, j + 0), 1.0 * weight);
00378
00379 row++;
00380 }
00381 }
00382 }
00383
00384 void
00385 FittingSurfaceIM::addCageBoundaryRegularisation (double weight, int side, unsigned &row)
00386 {
00387 int i = 0;
00388 int j = 0;
00389
00390 switch (side)
00391 {
00392 case SOUTH:
00393 j = m_nurbs.m_cv_count[1] - 1;
00394 case NORTH:
00395 for (i = 1; i < (m_nurbs.m_cv_count[0] - 1); i++)
00396 {
00397
00398 m_solver.f (row, 0, 0.0);
00399
00400 m_solver.K (row, grc2gl (i + 0, j), -2.0 * weight);
00401 m_solver.K (row, grc2gl (i - 1, j), 1.0 * weight);
00402 m_solver.K (row, grc2gl (i + 1, j), 1.0 * weight);
00403
00404 row++;
00405 }
00406 break;
00407
00408 case EAST:
00409 i = m_nurbs.m_cv_count[0] - 1;
00410 case WEST:
00411 for (j = 1; j < (m_nurbs.m_cv_count[1] - 1); j++)
00412 {
00413
00414 m_solver.f (row, 0, 0.0);
00415
00416 m_solver.K (row, grc2gl (i, j + 0), -2.0 * weight);
00417 m_solver.K (row, grc2gl (i, j - 1), 1.0 * weight);
00418 m_solver.K (row, grc2gl (i, j + 1), 1.0 * weight);
00419
00420 row++;
00421 }
00422 break;
00423 }
00424 }
00425
00426 void
00427 FittingSurfaceIM::addCageCornerRegularisation (double weight, unsigned &row)
00428 {
00429 {
00430 int i = 0;
00431 int j = 0;
00432
00433 m_solver.f (row, 0, 0.0);
00434
00435 m_solver.K (row, grc2gl (i + 0, j + 0), -2.0 * weight);
00436 m_solver.K (row, grc2gl (i + 1, j + 0), 1.0 * weight);
00437 m_solver.K (row, grc2gl (i + 0, j + 1), 1.0 * weight);
00438
00439 row++;
00440 }
00441
00442 {
00443 int i = m_nurbs.m_cv_count[0] - 1;
00444 int j = 0;
00445
00446 m_solver.f (row, 0, 0.0);
00447
00448 m_solver.K (row, grc2gl (i + 0, j + 0), -2.0 * weight);
00449 m_solver.K (row, grc2gl (i - 1, j + 0), 1.0 * weight);
00450 m_solver.K (row, grc2gl (i + 0, j + 1), 1.0 * weight);
00451
00452 row++;
00453 }
00454
00455 {
00456 int i = m_nurbs.m_cv_count[0] - 1;
00457 int j = m_nurbs.m_cv_count[1] - 1;
00458
00459 m_solver.f (row, 0, 0.0);
00460
00461 m_solver.K (row, grc2gl (i + 0, j + 0), -2.0 * weight);
00462 m_solver.K (row, grc2gl (i - 1, j + 0), 1.0 * weight);
00463 m_solver.K (row, grc2gl (i + 0, j - 1), 1.0 * weight);
00464
00465 row++;
00466 }
00467
00468 {
00469 int i = 0;
00470 int j = m_nurbs.m_cv_count[1] - 1;
00471
00472 m_solver.f (row, 0, 0.0);
00473
00474 m_solver.K (row, grc2gl (i + 0, j + 0), -2.0 * weight);
00475 m_solver.K (row, grc2gl (i + 1, j + 0), 1.0 * weight);
00476 m_solver.K (row, grc2gl (i + 0, j - 1), 1.0 * weight);
00477
00478 row++;
00479 }
00480
00481 }
00482
00483 void
00484 FittingSurfaceIM::solve (double damp)
00485 {
00486 if (m_solver.solve ())
00487 updateSurf (damp);
00488 }
00489
00490 void
00491 FittingSurfaceIM::updateSurf (double damp)
00492 {
00493 int ncp = m_nurbs.CVCount ();
00494
00495 for (int A = 0; A < ncp; A++)
00496 {
00497
00498 int I = gl2gr (A);
00499 int J = gl2gc (A);
00500
00501 ON_3dPoint cp_prev;
00502 m_nurbs.GetCV (I, J, cp_prev);
00503
00504 ON_3dPoint cp;
00505 cp.z = cp_prev.z + damp * (m_solver.x (A, 0) - cp_prev.z);
00506 cp.x = cp.z * (m_cps_px[A] (0) - m_intrinsic (0, 2)) / m_intrinsic (0, 0);
00507 cp.y = cp.z * (m_cps_px[A] (1) - m_intrinsic (1, 2)) / m_intrinsic (1, 1);
00508
00509 m_nurbs.SetCV (I, J, cp);
00510
00511 }
00512
00513 }
00514
00515 Eigen::Vector2d
00516 FittingSurfaceIM::findClosestElementMidPoint (const ON_NurbsSurface &nurbs, const Eigen::Vector3d &pt)
00517 {
00518 Eigen::Vector2d hint;
00519 Eigen::Vector3d r;
00520 std::vector<double> elementsU = getElementVector (nurbs, 0);
00521 std::vector<double> elementsV = getElementVector (nurbs, 1);
00522
00523 double d_shortest (DBL_MAX);
00524 for (unsigned i = 0; i < elementsU.size () - 1; i++)
00525 {
00526 for (unsigned j = 0; j < elementsV.size () - 1; j++)
00527 {
00528 double points[3];
00529 double d;
00530
00531 double xi = elementsU[i] + 0.5 * (elementsU[i + 1] - elementsU[i]);
00532 double eta = elementsV[j] + 0.5 * (elementsV[j + 1] - elementsV[j]);
00533
00534 nurbs.Evaluate (xi, eta, 0, 3, points);
00535 r (0) = points[0] - pt (0);
00536 r (1) = points[1] - pt (1);
00537 r (2) = points[2] - pt (2);
00538
00539 d = r.squaredNorm ();
00540
00541 if ((i == 0 && j == 0) || d < d_shortest)
00542 {
00543 d_shortest = d;
00544 hint (0) = xi;
00545 hint (1) = eta;
00546 }
00547 }
00548 }
00549
00550 return hint;
00551 }
00552
00553 Eigen::Vector2d
00554 FittingSurfaceIM::inverseMapping (const ON_NurbsSurface &nurbs, const Eigen::Vector3d &pt, const Eigen::Vector2d &hint,
00555 double &error, Eigen::Vector3d &p, Eigen::Vector3d &tu, Eigen::Vector3d &tv,
00556 int maxSteps, double accuracy, bool quiet)
00557 {
00558
00559 double pointAndTangents[9];
00560
00561 Eigen::Vector2d current, delta;
00562 Eigen::Matrix2d A;
00563 Eigen::Vector2d b;
00564 Eigen::Vector3d r;
00565 std::vector<double> elementsU = getElementVector (nurbs, 0);
00566 std::vector<double> elementsV = getElementVector (nurbs, 1);
00567 double minU = elementsU[0];
00568 double minV = elementsV[0];
00569 double maxU = elementsU[elementsU.size () - 1];
00570 double maxV = elementsV[elementsV.size () - 1];
00571
00572 current = hint;
00573
00574 for (int k = 0; k < maxSteps; k++)
00575 {
00576
00577 nurbs.Evaluate (current (0), current (1), 1, 3, pointAndTangents);
00578 p (0) = pointAndTangents[0];
00579 p (1) = pointAndTangents[1];
00580 p (2) = pointAndTangents[2];
00581 tu (0) = pointAndTangents[3];
00582 tu (1) = pointAndTangents[4];
00583 tu (2) = pointAndTangents[5];
00584 tv (0) = pointAndTangents[6];
00585 tv (1) = pointAndTangents[7];
00586 tv (2) = pointAndTangents[8];
00587
00588 r = p - pt;
00589
00590 b (0) = -r.dot (tu);
00591 b (1) = -r.dot (tv);
00592
00593 A (0, 0) = tu.dot (tu);
00594 A (0, 1) = tu.dot (tv);
00595 A (1, 0) = A (0, 1);
00596 A (1, 1) = tv.dot (tv);
00597
00598 delta = A.ldlt ().solve (b);
00599
00600 if (delta.norm () < accuracy)
00601 {
00602
00603 error = r.norm ();
00604 return current;
00605
00606 }
00607 else
00608 {
00609 current = current + delta;
00610
00611 if (current (0) < minU)
00612 current (0) = minU;
00613 else if (current (0) > maxU)
00614 current (0) = maxU;
00615
00616 if (current (1) < minV)
00617 current (1) = minV;
00618 else if (current (1) > maxV)
00619 current (1) = maxV;
00620
00621 }
00622
00623 }
00624
00625 error = r.norm ();
00626
00627 if (!quiet)
00628 {
00629 printf ("[FittingSurface::inverseMapping] Warning: Method did not converge (%e %d)\n", accuracy, maxSteps);
00630 printf (" %f %f ... %f %f\n", hint (0), hint (1), current (0), current (1));
00631 }
00632
00633 return current;
00634
00635 }