Developer Documentation
ICPPlugin.cc
1 /*===========================================================================*\
2 * *
3 * OpenFlipper *
4  * Copyright (c) 2020, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
11  *---------------------------------------------------------------------------*
12  * *
13  * Redistribution and use in source and binary forms, with or without *
14  * modification, are permitted provided that the following conditions *
15  * are met: *
16  * *
17  * 1. Redistributions of source code must retain the above copyright notice, *
18  * this list of conditions and the following disclaimer. *
19  * *
20  * 2. Redistributions in binary form must reproduce the above copyright *
21  * notice, this list of conditions and the following disclaimer in the *
22  * documentation and/or other materials provided with the distribution. *
23  * *
24  * 3. Neither the name of the copyright holder nor the names of its *
25  * contributors may be used to endorse or promote products derived from *
26  * this software without specific prior written permission. *
27  * *
28  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 \*===========================================================================*/
41 
42 
43 #include "ICPPlugin.hh"
44 #include <QGridLayout>
45 #include <QPushButton>
46 #include <QLabel>
47 #include <QBitmap>
48 
49 ICPPlugin::ICPPlugin() :
50  tool_(nullptr),
51  toolIcon_(nullptr)
52 {
53 
54 }
55 
56 ICPPlugin::~ICPPlugin()
57 {
58 
59 }
60 
61 void ICPPlugin::initializePlugin()
62 {
63  if ( OpenFlipper::Options::gui() ) {
64  tool_ = new ICPToolbarWidget();
65  // connect signals->slots
66  connect( tool_->btnICP, SIGNAL(clicked()), this, SLOT(toolbarICP()) );
67 
68  QLabel* label = tool_->img_step_1;
69  QPixmap pixmap(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"icp_step1.png");
70  label->setPixmap(pixmap);
71  label->setMask(pixmap.mask());
72 
73  label->show();
74 
75  toolIcon_ = new QIcon(OpenFlipper::Options::iconDirStr()+OpenFlipper::Options::dirSeparator()+"ICPPluginIcon.png");
76  emit addToolbox( tr("ICP") , tool_, toolIcon_ );
77  }
78 
79 }
80 
81 void ICPPlugin::pluginsInitialized() {
82 
83 
84 }
85 
89 static bool compare_ev(const Eigen::VectorXd& _lhs, const Eigen::VectorXd& _rhs) {
90  return std::abs(_lhs(_lhs.rows()-1)) > std::abs(_rhs(_rhs.rows()-1));
91 }
92 
96 static Eigen::MatrixXd sorted_cols_by_ev(Eigen::MatrixXd _A, Eigen::MatrixXd _EV)
97 {
98  // add eigenvalues to column eigenvector
99  _A.conservativeResize(_A.rows()+1, _A.cols());
100  for (int64_t i=0;i<_A.cols();i++) {
101  _A(_A.rows()-1, i) = _EV(i, 0);
102  }
103  std::vector<Eigen::VectorXd> vec;
104  for (int64_t i = 0; i < _A.cols(); ++i)
105  vec.push_back(_A.col(i));
106 
107  // sort eigenvectors by last component (eigenvalue)
108  std::sort(vec.begin(), vec.end(), &compare_ev);
109 
110  for (unsigned int i = 0; i < _A.cols(); ++i)
111  _A.col(i) = vec[i];
112 
113  return _A;
114 }
115 
116 // from MovePlugin
120 template< typename MeshT >
121 static void transformMesh(ACG::Matrix4x4d _mat , MeshT* _mesh ) {
122  // Get the inverse matrix of the transformation for the normals
123  ACG::Matrix4x4d invTranspMat = _mat;
124 
125  // Build inverse transposed matrix of _mat
126  invTranspMat.invert();
127  invTranspMat.transpose();
128 
129  for ( auto v_it : _mesh->vertices() ) {
130 
131  // transform the mesh vertex
132  _mesh->set_point(v_it,_mat.transform_point(_mesh->point(v_it)));
133 
134  // transform the vertex normal
135  typename MeshT::Normal n = invTranspMat.transform_vector(_mesh->normal(v_it));
136 
137  n.normalize();
138 
139  _mesh->set_normal(v_it,n);
140  }
141 
142  for ( auto f_it : _mesh->faces() ) {
143 
144  // transform the face normal
145  typename MeshT::Normal n = invTranspMat.transform_vector(_mesh->normal(f_it));
146 
147  n.normalize();
148 
149  _mesh->set_normal(f_it,n);
150  }
151 }
152 
156 static void getMeshPoints(TriMesh* _mesh, ICPContext* _context, VertexList* _result) {
157  unsigned long counter = 0;
158 
159  for ( auto v_it : _mesh->vertices() ) {
160  counter++;
161  if (counter % 10000 == 0) {
162  if (_context->params_->debugOutput_)
163  std::cout << counter << "/" << _mesh->n_vertices() << " mesh point copy" << std::endl << std::flush;
164  }
165  PolyMesh::VertexHandle point = v_it;
166 
167  _result->push_back(point);
168  }
169 }
170 
174 static void findAllNearestPoints(VertexList &_sourcePoints, ICPContext* _context, PointList* _result) {
175  // for every source point
176  for (VertexList::iterator it = _sourcePoints.begin() ; it != _sourcePoints.end() ; ++it) {
177  PolyMesh::VertexHandle closest;
178  double closestDistance = DBL_MAX;
179  PolyMesh::Point referencePoint = _context->sourceMesh_->point(*it);
180 
181 
182  // search clostest target point
183  for (auto it2 : _context->targetMesh_->vertices()) {
184  PolyMesh::Point delta = referencePoint - _context->targetMesh_->point(it2);
185  double distance = delta[0]*delta[0] + delta[1]*delta[1] + delta[2]*delta[2];
186  if (distance < 0) distance *= -1;
187  if (distance < closestDistance) {
188  // current point is smallest so far
189  closestDistance = distance;
190  closest = it2;
191  }
192  }
193 
194  // save point info
196  info.point_ = _context->targetMesh_->point(closest);
197  info.normal_ = _context->targetMesh_->normal(closest);
198  _result->push_back(info);
199  }
200 }
201 
205 static void findAllNearestPointsBSP(VertexList &_sourcePoints, ICPContext* _context, PointList* _result) {
206  if (_context->params_->debugOutput_)
207  std::cout << "generate BSP ... " << std::flush;
208 
209  // generate bsp
210  TriMeshObject::OMTriangleBSP* targetBSP = _context->targetObject_->requestTriangleBsp();
211 
212  if (_context->params_->debugOutput_)
213  std::cout << "done. nearest point calculation" << std::endl << std::flush;
214 
215  unsigned long long counter = 0;
216 
217  // for every source point
218  for (VertexList::iterator it = _sourcePoints.begin() ; it != _sourcePoints.end() ; ++it) {
219  counter++;
220  if (counter % 1000 == 0) {
221  if (_context->params_->debugOutput_)
222  std::cout << counter << "/" << _sourcePoints.size() << " nearest point calculation" << std::endl << std::flush;
223  }
224 
225  // find nearest surface on target
226  PolyMesh::Point point = _context->sourceMesh_->point(*it);
227  TriMeshObject::OMTriangleBSP::NearestNeighbor nearest = targetBSP->nearest(point);
228  PolyMesh::FaceHandle face = nearest.handle;
229  PolyMesh::HalfedgeHandle he = _context->targetMesh_->halfedge_handle(face);
230  PolyMesh::VertexHandle vh = _context->targetMesh_->to_vertex_handle(he);
231  PolyMesh::Point pivot = _context->targetMesh_->point(vh);
232  PolyMesh::Normal faceNormal = _context->targetMesh_->normal(face);
233 
234  // find nearest point on that surface
235  PolyMesh::Point projected = ACG::Geometry::projectToPlane(pivot, faceNormal, point);
236 
238  info.point_ = projected;
239  info.normal_ = faceNormal;
240 
241  // now calculate interpolated normal
242 
243  // find face's vertices
244  PolyMesh::VertexHandle adjacentVH[3];
245  auto faceIterator = _context->targetMesh_->fv_iter(face);
246  bool error = false;
247  if (faceIterator.is_valid()) { adjacentVH[0] = *faceIterator; faceIterator++; } else error = true;
248  if (faceIterator.is_valid()) { adjacentVH[1] = *faceIterator; faceIterator++; } else error = true;
249  if (faceIterator.is_valid()) { adjacentVH[2] = *faceIterator; faceIterator++; } else error = true;
250 
251  ACG::Vec3d projectedACG;
252 
253  projectedACG[0] = projected[0];
254  projectedACG[1] = projected[1];
255  projectedACG[2] = projected[2];
256 
257  if (!error && _context->params_->interpolateNormals_) {
258  ACG::Vec3d adjacentV[3];
259  for (int i=0;i<3;i++) {
260  const PolyMesh::Point p1 = _context->targetMesh_->point(adjacentVH[i]);
261  adjacentV[i][0] = p1[0];
262  adjacentV[i][1] = p1[1];
263  adjacentV[i][2] = p1[2];
264  }
265 
267 
268  // calculate bary coords
269  if (ACG::Geometry::baryCoord(projectedACG, adjacentV[0], adjacentV[1], adjacentV[2], baryCoord)) {
270 
271  // interpolate normals
272  info.normal_ = (
273  baryCoord[0] * _context->targetMesh_->normal(adjacentVH[0]) +
274  baryCoord[1] * _context->targetMesh_->normal(adjacentVH[1]) +
275  baryCoord[2] * _context->targetMesh_->normal(adjacentVH[2])
276  ).normalized();
277 
278  } else {
279  if (_context->params_->debugOutput_)
280  std::cout << "barycoord failed" << std::endl << std::flush;
281  }
282  } else {
283  if (_context->params_->debugOutput_)
284  std::cout << "circulator failed" << std::endl << std::flush;
285  }
286 
287  _result->push_back(info);
288  }
289 }
290 
294 static double mse(VertexList &_pointsSource, PointList &_pointsTarget, ICPContext* _context) {
295  double sum = 0;
296  VertexList::iterator itSource = _pointsSource.begin();
297  PointList::iterator itTarget = _pointsTarget.begin();
298 
299  // for every source point
300  for (unsigned int i=0;i<_pointsSource.size();i++) {
301  PolyMesh::VertexHandle source = *itSource;
302  PolyMesh::Point target = itTarget->point_;
303  PolyMesh::Point delta = target-_context->sourceMesh_->point(source);
304  // calculate distance to target
305  sum += delta[0]*delta[0] + delta[1]*delta[1] + delta[2]*delta[2];
306  ++itSource; ++itTarget;
307  }
308 
309  return sum / static_cast<double>(_pointsSource.size());
310 }
311 
315 static PolyMesh::Point calculateCOG(VertexList& _points, TriMesh* _mesh) {
316  PolyMesh::Point sum(0,0,0);
317  for (VertexList::iterator it=_points.begin() ; it != _points.end() ; ++it) {
318  sum += _mesh->point(*it);
319  }
320  return sum / static_cast<double>(_points.size());
321 }
322 
326 static PolyMesh::Point calculateCOG(PointList& _points) {
327  PolyMesh::Point sum(0,0,0);
328  for (PointList::iterator it=_points.begin() ; it != _points.end() ; ++it) {
329  sum += it->point_;
330  }
331  return sum / static_cast<double>(_points.size());
332 }
333 
337 template < typename VectorT >
338 static VectorT calculateCOG(const std::vector<VectorT>& _points) {
339  VectorT sum;
340  for (unsigned int i=0;i<sum.dim();i++) sum[i] = 0;
341 
342  for (unsigned int i=0;i<_points.size();i++) {
343  sum += _points[i];
344  }
345 
346  return sum / static_cast<double>(_points.size());
347 }
348 
352 static double calculateSize(VertexList* _points, TriMesh* _mesh, PolyMesh::Point &_origin) {
353  double sum = 0;
354  for (VertexList::iterator it=_points->begin() ; it != _points->end() ; ++it) {
355  PolyMesh::Point p = _mesh->point(*it);
356  PolyMesh::Point delta = p - _origin;
357 
358  sum += (delta[0]*delta[0] + delta[1]*delta[1] + delta[2]*delta[2]);
359  }
360 
361  sum = std::sqrt(sum);
362 
363  sum /= static_cast<double>(_points->size());
364 
365  return sum;
366 }
367 
371 static double calculateSize(PointList* _points, PolyMesh::Point &_origin) {
372  double sum = 0;
373  for (PointList::iterator it=_points->begin() ; it != _points->end() ; ++it) {
374  PolyMesh::Point p = it->point_;
375  PolyMesh::Point delta = p - _origin;
376 
377  sum += (delta[0]*delta[0] + delta[1]*delta[1] + delta[2]*delta[2]);
378  }
379 
380  sum = std::sqrt(sum);
381 
382  sum /= static_cast<double>(_points->size());
383 
384  return sum;
385 }
386 
391 static void deleteNonPairs(VertexList &_sourceP, PointList &_targetP, ICPContext* _context) {
392  if (_context->params_->debugOutput_)
393  std::cout << "delete non pairs" << std::endl << std::flush;
394 
395  VertexList::iterator itSource = _sourceP.begin();
396  PointList::iterator itTarget = _targetP.begin();
397 
398  const double distanceThreshold = _context->params_->distanceThreshold_;
399  const double distanceThreasholdSquared = distanceThreshold * distanceThreshold;
400 
401  const double normalDotThreshold = _context->params_->normalDotThreshold_ / 100.0;
402 
403  unsigned long rmDistance = 0;
404  unsigned long rmNormal = 0;
405 
406  // for every source target pair
407  while (itSource != _sourceP.end()) {
408  PolyMesh::Point source = _context->sourceMesh_->point(*itSource);
409  PolyMesh::Point target = itTarget->point_;
410 
411  PolyMesh::Normal sourceN = _context->sourceMesh_->normal(*itSource);
412  PolyMesh::Normal targetN = itTarget->normal_;
413 
414  PolyMesh::Point delta = target - source;
415 
416  double distanceSquared = delta[0]*delta[0] + delta[1]*delta[1] + delta[2]*delta[2];
417  double normalDot = sourceN[0]*targetN[0] + sourceN[1]*targetN[1] + sourceN[2]*targetN[2];
418 
419  // TODO rm test
420  // if (normalDot < 0) normalDot *= -1.0;
421 
422  // check if distance differs
423  if (distanceSquared > distanceThreasholdSquared) {
424  // remove pair
425 
426  rmDistance++;
427 
428  _sourceP.erase(itSource++);
429  _targetP.erase(itTarget++);
430 
431  // check if normal differs
432  } else if ( normalDot < normalDotThreshold || normalDot < 0.0) {
433  // remove pair
434 
435  rmNormal++;
436 
437  _sourceP.erase(itSource++);
438  _targetP.erase(itTarget++);
439  } else {
440  // continue
441 
442  ++itSource;
443  ++itTarget;
444  }
445  }
446 
447  if (_context->params_->debugOutput_)
448  std::cout << "after deletion " << _sourceP.size() << " points left | rmDistance:" << rmDistance << ",rmNormal:" << rmNormal << std::endl << std::flush;
449 }
450 
454 template < typename VectorT >
455 static Eigen::MatrixXd estimatedRigidTransform(const std::vector<VectorT>& _sourceCloud, const std::vector<VectorT>& _targetCloud) {
456  assert(_sourceCloud.size() == _targetCloud.size());
457  unsigned int dim = 3;
458  {
459  VectorT assertVector;
460  dim = assertVector.dim();
461  assert(dim == 3); // todo: check math for other dimensions
462  }
463 
464  VectorT cogSource = calculateCOG(_sourceCloud);
465  VectorT cogTarget = calculateCOG(_targetCloud);
466 
467  Eigen::MatrixXd centroid_A(dim,1);
468  Eigen::MatrixXd centroid_B(dim,1);
469 
470  for (unsigned int i=0;i<dim;i++) {
471  centroid_A(i,0) = cogSource[i];
472  centroid_B(i,0) = cogTarget[i];
473  }
474 
475  Eigen::MatrixXd Am(dim, _sourceCloud.size());
476  Eigen::MatrixXd Bm(dim, _targetCloud.size());
477 
478  {
479  for (unsigned int i=0;i<_sourceCloud.size();i++) {
480  VectorT a = _sourceCloud[i];
481  VectorT b = _targetCloud[i];
482 
483  for (unsigned int d=0 ; d<dim ; d++) {
484  Am(d, i) = a[d] - cogSource[d];
485  Bm(d, i) = b[d] - cogTarget[d];
486  }
487  }
488  }
489 
490  Eigen::MatrixXd H = Am * Bm.transpose();
491  Eigen::JacobiSVD<Eigen::MatrixXd> svd(H, Eigen::ComputeFullU | Eigen::ComputeFullV);
492 
493  Eigen::MatrixXd R = svd.matrixV() * svd.matrixU().transpose();
494 
495  if (R.determinant() < 0) {
496  Eigen::MatrixXd V = svd.matrixV();
497 
498  for (unsigned int i=0 ; i<dim ; i++) {
499  V(i,dim-1) = V(i,dim-1) * -1.0;
500  }
501 
502  R = V * svd.matrixU().transpose();
503  }
504 
505  Eigen::MatrixXd t = -R * centroid_A + centroid_B;
506 
507  R.conservativeResize(R.rows()+1, R.cols()+1);
508 
509  for (unsigned int i=0;i<dim+1;i++) {
510  R(dim, i) = i==dim ? 1 : 0;
511  R(i, dim) = i==dim ? 1 : t(i, 0);
512  }
513 
514  return R;
515 }
516 
520 static ACG::GLMatrixd calculateRotationMatrix(VertexList &_sourceP, PointList &_targetP, ICPContext* _context) {
521  assert(_sourceP.size() == _targetP.size());
522 
523  std::vector<ACG::Vec3d> sourceCloud(_sourceP.size()), targetCloud(_sourceP.size());
524  VertexList::iterator iterA = _sourceP.begin();
525  PointList::iterator iterB = _targetP.begin();
526 
527  for (unsigned int i=0;i<_sourceP.size();i++) {
528  sourceCloud[i] = _context->sourceMesh_->point(*iterA);
529  targetCloud[i] = iterB->point_;
530 
531  ++iterA;
532  ++iterB;
533  }
534 
535  Eigen::MatrixXd rotEigen = estimatedRigidTransform(sourceCloud, targetCloud);
536  ACG::GLMatrixd rot;
537  rot.identity();
538 
539  if (_context->params_->debugOutput_)
540  std::cout << "Final rotation matrix:" << std::endl << rotEigen << std::endl << std::flush;
541 
542  for (unsigned int x=0;x<3;x++) {
543  for (unsigned int y=0;y<3;y++) {
544  rot(x,y) = rotEigen(x,y);
545  }
546  }
547 
548  return rot;
549 }
550 
554 static void debugOutPairs(ICPContext* _context, VertexList& _source, PointList& _target) {
555  if (!_context->params_->debugOutput_) return;
556 
557  VertexList::iterator sourceIt = _source.begin();
558  PointList::iterator targetIt = _target.begin();
559 
560  for (;sourceIt != _source.end();) {
561 
562  PolyMesh::Point a = _context->sourceMesh_->point(*sourceIt);
563  PolyMesh::Point b = targetIt->point_;
564  double dist = (a[0]-b[0])*(a[0]-b[0]) + (a[1]-b[1])*(a[1]-b[1]) + (a[2]-b[2])*(a[2]-b[2]);
565  dist = std::sqrt(dist);
566  std::cout << a << " -> " << b << " distance: " << dist << std::endl << std::flush;
567 
568  ++sourceIt;
569  ++targetIt;
570  }
571 }
572 
576 void ICPPlugin::doICP(ICPContext* _context) {
577  const int maxIterations = _context->params_->maxIterations_;
578  const double errorThreshold = _context->params_->errorThreshold_;
579 
580  double currentError = DBL_MAX;
581  // loop CP while error is below threshold for max maxIterations
582  int i = 0;
583  for (i=0; (i<maxIterations && currentError > errorThreshold) || maxIterations == 0 ; i++) {
584  VertexList meshPointsSource;
585  getMeshPoints(_context->sourceMesh_, _context, &meshPointsSource);
586 
587  if (_context->params_->debugOutput_)
588  std::cout << "ICP Iteration " << i+1 << "/" << maxIterations << " mse: " << ( (currentError<DBL_MAX-1e3) ? currentError : -1.0 ) << std::endl << std::flush;
589 
590  // determine for each point in S the closest point in T
591  PointList closestsPointOnTarget;
592  findAllNearestPointsBSP(meshPointsSource, _context, &closestsPointOnTarget);
593 
594  // delete posible non pairs
595  deleteNonPairs(meshPointsSource, closestsPointOnTarget, _context);
596  // debugOutPairs(_context, meshPointsSource, closestsPointOnTarget);
597 
598  if (meshPointsSource.size() <=6) {
599  if (_context->params_->debugOutput_)
600  std::cout << "ICP not enough points" << std::endl << std::flush;
601  break;
602  }
603 
604  {
605  PolyMesh::Point cogSource = calculateCOG(meshPointsSource, _context->sourceMesh_);
606  PolyMesh::Point cogTarget = calculateCOG(closestsPointOnTarget);
607 
608  double sizeSource = calculateSize(&meshPointsSource, _context->sourceMesh_, cogSource);
609  double sizeTarget = calculateSize(&closestsPointOnTarget, cogTarget);
610 
611  double sizeAvg = (1.0/sizeSource+1.0/sizeTarget)/2.0;
612  if (_context->params_->doResize_)
613  _context->params_->distanceThreshold_ *= sizeAvg;
614 
615  if (_context->params_->debugOutput_)
616  std::cout << "translate source by " << -cogSource << " and scale by " << 1.0/sizeSource << std::endl
617  << "translate target by " << -cogTarget << " and scale by " << 1.0/sizeTarget << std::endl << std::flush;
618 
619  ACG::GLMatrixd rotMat = calculateRotationMatrix(meshPointsSource, closestsPointOnTarget, _context);
620 
621  if (maxIterations == 0) {
622  break;
623  }
624 
625  // rotate
626  transformMesh(rotMat,
627  _context->sourceMesh_);
628 
629  cogSource = calculateCOG(meshPointsSource, _context->sourceMesh_);
630  cogTarget = calculateCOG(closestsPointOnTarget);
631 
632  ACG::GLMatrixd transMat;
633  transMat.identity();
634  transMat.translate(cogTarget-cogSource);
635 
636  // translate
637  transformMesh(transMat,
638  _context->sourceMesh_);
639 
640  emit updatedObject(_context->sourceID_, UPDATE_GEOMETRY);
641  }
642 
643  // calculate MSE
644  currentError = mse(meshPointsSource, closestsPointOnTarget, _context);
645  }
646 
647  if (_context->params_->debugOutput_)
648  std::cout << "Exited ICP with error: " << currentError << " after " << i << "/" << maxIterations << " iterations" << std::endl << std::flush;
649 
650  emit updatedObject(_context->sourceID_, UPDATE_GEOMETRY);
651  emit updatedObject(_context->targetID_, UPDATE_GEOMETRY);
652 }
653 
658 void ICPPlugin::icp(int _maxIterations, double _errorThreshold, double _distanceThreshold, double _normalDotThreshold, bool _debugOutput, bool _doResize, bool _interpolateNormals, int _sourceObjectID, int _targetObjectID) {
659  // pack structs
660  ICPParameters icpParams;
661  icpParams.distanceThreshold_ = _distanceThreshold;
662  icpParams.errorThreshold_ = _errorThreshold;
663  icpParams.maxIterations_ = _maxIterations;
664  icpParams.normalDotThreshold_ = _normalDotThreshold;
665  icpParams.debugOutput_ = _debugOutput;
666  icpParams.doResize_ = _doResize;
667  icpParams.interpolateNormals_ = _interpolateNormals;
668 
669  ICPContext icpContext;
670  icpContext.sourceMesh_ = PluginFunctions::triMesh(_sourceObjectID);
671  icpContext.targetMesh_ = PluginFunctions::triMesh(_targetObjectID);
672  icpContext.sourceObject_ = PluginFunctions::triMeshObject(_sourceObjectID);
673  icpContext.targetObject_ = PluginFunctions::triMeshObject(_targetObjectID);
674  icpContext.sourceID_ = _sourceObjectID;
675  icpContext.targetID_ = _targetObjectID;
676  icpContext.params_ = &icpParams;
677 
678  // execute icp
679  doICP(&icpContext);
680 }
681 
687  if(OpenFlipper::Options::nogui()) {
688  emit log(LOGERR, "no gui detected - use icp(...) instead");
689  return;
690  }
691 
692  // pack structs
693  ICPParameters icpParams;
694  icpParams.distanceThreshold_ = this->tool_->icp_distanceThreshold->value();
695  icpParams.errorThreshold_ = this->tool_->icp_errorThreshold->value();
696  icpParams.maxIterations_ = this->tool_->icp_maxIterations->value();
697  icpParams.normalDotThreshold_ = this->tool_->icp_normalDotThreshold->value();
698  icpParams.debugOutput_ = this->tool_->ck_debugOutput->isChecked();
699  icpParams.doResize_ = this->tool_->ck_doResize->isChecked();
700  icpParams.interpolateNormals_ = this->tool_->ck_interpolateNormals->isChecked();
701 
702  ICPContext icpContext;
703  icpContext.sourceMesh_ = nullptr;
704  icpContext.targetMesh_ = nullptr;
705  icpContext.sourceObject_ = nullptr;
706  icpContext.targetObject_ = nullptr;
707  icpContext.sourceID_ = icpContext.targetID_ = -1;
708  icpContext.params_ = &icpParams;
709 
710  // find target
712  if ( o_it->dataType( DATA_TRIANGLE_MESH ) ) {
713  if (icpContext.targetMesh_ == nullptr) {
714  icpContext.targetMesh_ = PluginFunctions::triMesh(o_it);
715  icpContext.targetID_ = o_it->id();
716  icpContext.targetObject_ = PluginFunctions::triMeshObject(o_it->id());
717  } else {
718  emit log(LOGERR, "Only one target is supported");
719  }
720  } else {
721  emit log(LOGERR, "DataType not supported.");
722  }
723  }
724  if (icpContext.targetMesh_ == nullptr) {
725  emit log(LOGERR, "no target selected");
726  return;
727  }
728 
729  // for every source object execute icp source -> target
731  if ( o_it->dataType( DATA_TRIANGLE_MESH ) ) {
732  icpContext.sourceMesh_ = PluginFunctions::triMesh(o_it);
733  icpContext.sourceID_ = o_it->id();
734  icpContext.sourceObject_ = PluginFunctions::triMeshObject(o_it->id());
735 
736  doICP(&icpContext);
737  } else {
738  emit log(LOGERR, "DataType not supported.");
739  }
740  }
741 }
742 
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:60
OMTriangleBSP * requestTriangleBsp()
void doICP(ICPContext *_context)
Definition: ICPPlugin.cc:576
Kernel::Normal Normal
Normal type.
Definition: PolyMeshT.hh:114
void transpose()
transpose matrix
Kernel::Point Point
Coordinate type.
Definition: PolyMeshT.hh:112
void translate(Scalar _x, Scalar _y, Scalar _z, MultiplyFrom _mult_from=MULT_FROM_RIGHT)
multiply self with translation matrix (x,y,z)
void icp(int _maxIterations, double _errorThreshold, double _distanceThreshold, double _normalDotThreshold, bool _debugOutput, bool _doResize, bool _interpolateNormals, int _sourceObjectID, int _targetObjectID)
Definition: ICPPlugin.cc:658
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
const QStringList SOURCE_OBJECTS("source")
Iterable object range.
void identity()
setup an identity matrix
VectorT< T, 3 > transform_point(const VectorT< T, 3 > &_v) const
transform point (x&#39;,y&#39;,z&#39;,1) = M * (x,y,z,1)
const UpdateType UPDATE_GEOMETRY(UpdateTypeSet(4))
Geometry updated.
void transformMesh(ACG::Matrix4x4d _matrix, MeshT &_mesh)
bool invert()
matrix inversion (returns true on success)
void toolbarICP()
Definition: ICPPlugin.cc:686
bool baryCoord(const VectorT< Scalar, 3 > &_p, const VectorT< Scalar, 3 > &_u, const VectorT< Scalar, 3 > &_v, const VectorT< Scalar, 3 > &_w, VectorT< Scalar, 3 > &_result)
Definition: Algorithms.cc:83
VectorT< T, 3 > transform_vector(const VectorT< T, 3 > &_v) const
transform vector (x&#39;,y&#39;,z&#39;,0) = A * (x,y,z,0)
TriMeshObject * triMeshObject(BaseObjectData *_object)
Cast an BaseObject to a TriMeshObject if possible.
const QStringList TARGET_OBJECTS("target")
Iterable object range.
NearestNeighbor nearest(const Point &_p) const
Return handle of the nearest neighbor face.
ObjectRange objects(IteratorRestriction _restriction, DataType _dataType)
Iterable object range.
VectorT projectToPlane(const VectorT &_porigin, const VectorT &_pnormal, const VectorT &_point)
projects a point to a plane
Definition: Algorithms.cc:894