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
49ICPPlugin::ICPPlugin() :
50 tool_(nullptr),
51 toolIcon_(nullptr)
52{
53
54}
55
56ICPPlugin::~ICPPlugin()
57{
58 delete toolIcon_;
59}
60
61void 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
81void ICPPlugin::pluginsInitialized() {
82
83
84}
85
89static 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
96static 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
120template< typename MeshT >
121static 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
156static 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
174static 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
205static 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
294static 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
315static 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
326static 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
337template < typename VectorT >
338static 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
352static 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
371static 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
391static 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
454template < typename VectorT >
455static 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
520static 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
554static 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
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
658void 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
@ LOGERR
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:60
void translate(Scalar _x, Scalar _y, Scalar _z, MultiplyFrom _mult_from=MULT_FROM_RIGHT)
multiply self with translation matrix (x,y,z)
VectorT< T, 3 > transform_vector(const VectorT< T, 3 > &_v) const
transform vector (x',y',z',0) = A * (x,y,z,0)
void identity()
setup an identity matrix
bool invert()
matrix inversion (returns true on success)
void transpose()
transpose matrix
VectorT< T, 3 > transform_point(const VectorT< T, 3 > &_v) const
transform point (x',y',z',1) = M * (x,y,z,1)
NearestNeighbor nearest(const Point &_p) const
Return handle of the nearest neighbor face.
void toolbarICP()
Definition: ICPPlugin.cc:686
void icp(int _maxIterations, double _errorThreshold, double _distanceThreshold, double _normalDotThreshold, bool _debugOutput, bool _doResize, bool _interpolateNormals, int _sourceObjectID, int _targetObjectID)
Definition: ICPPlugin.cc:658
void doICP(ICPContext *_context)
Definition: ICPPlugin.cc:576
OMTriangleBSP * requestTriangleBsp()
Kernel::Normal Normal
Normal type.
Definition: PolyMeshT.hh:114
Kernel::FaceHandle FaceHandle
Scalar type.
Definition: PolyMeshT.hh:139
Kernel::Point Point
Coordinate type.
Definition: PolyMeshT.hh:112
PointT normal(HalfFaceHandle _hfh) const
const UpdateType UPDATE_GEOMETRY(UpdateTypeSet(4))
Geometry updated.
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 projectToPlane(const VectorT &_porigin, const VectorT &_pnormal, const VectorT &_point)
projects a point to a plane
Definition: Algorithms.cc:894
void transformMesh(ACG::Matrix4x4d _matrix, MeshT &_mesh)
TriMeshObject * triMeshObject(BaseObjectData *_object)
Cast an BaseObject to a TriMeshObject if possible.
TriMesh * triMesh(BaseObjectData *_object)
Get a triangle mesh from an object.
const QStringList SOURCE_OBJECTS("source")
Iterable object range.
const QStringList TARGET_OBJECTS("target")
Iterable object range.
ObjectRange objects(IteratorRestriction _restriction, DataType _dataType)
Iterable object range.