PrimitiveExtractionWidgetController.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * \package VisionX::gui-plugins::PrimitiveExtractionWidgetController
17 * \author Peter Kaiser ( peter dot kaiser at kit dot edu )
18 * \date 2016
19 * \copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
24
25#include <filesystem>
26#include <memory>
27#include <string>
28
29#include <QCheckBox>
30#include <QComboBox>
31#include <QDoubleSpinBox>
32
33#include <pcl/common/transforms.h>
34#include <pcl/io/pcd_io.h>
35#include <pcl/point_types.h>
36
39
40#include <VisionX/interface/core/PointCloudProviderInterface.h>
42
43#include <AffordanceKit/primitives/Plane.h>
44
45#define PI 3.141592654
46
47namespace armarx
48{
53
57
58 void
60 {
61 pointCloudProviderName =
62 settings->value("pointCloudProviderName", "").toString().toStdString();
63 pointCloudSegmenterName =
64 settings->value("pointCloudSegmenterName", "").toString().toStdString();
65 primitiveExtractorName =
66 settings->value("primitiveExtractorName", "").toString().toStdString();
67 pipelineVisualizationName =
68 settings->value("pipelineVisualizationName", "").toString().toStdString();
69 workingMemoryName = settings->value("workingMemoryName", "").toString().toStdString();
70 }
71
72 void
74 {
75 settings->setValue("pointCloudProviderName",
76 QString::fromStdString(pointCloudProviderName));
77 settings->setValue("pointCloudSegmenterName",
78 QString::fromStdString(pointCloudSegmenterName));
79 settings->setValue("primitiveExtractorName",
80 QString::fromStdString(primitiveExtractorName));
81 settings->setValue("pipelineVisualizationName",
82 QString::fromStdString(pipelineVisualizationName));
83 settings->setValue("workingMemoryName", QString::fromStdString(workingMemoryName));
84 }
85
86 QPointer<QDialog>
88 {
89 if (!configDialog)
90 {
91 configDialog = new PrimitiveExtractionConfigDialog(parent);
92 }
93
94 return qobject_cast<PrimitiveExtractionConfigDialog*>(configDialog);
95 }
96
97 void
99 {
100 pointCloudProviderName =
101 configDialog->pointCloudProviderProxyFinder->getSelectedProxyName().toStdString();
102 pointCloudSegmenterName =
103 configDialog->pointCloudSegmenterProxyFinder->getSelectedProxyName().toStdString();
104 primitiveExtractorName =
105 configDialog->primitiveExtractorProxyFinder->getSelectedProxyName().toStdString();
106 pipelineVisualizationName =
107 configDialog->pipelineVisualizationProxyFinder->getSelectedProxyName().toStdString();
108 workingMemoryName =
109 configDialog->pipelineVisualizationProxyFinder->getSelectedProxyName().toStdString();
110 }
111
112 void
116
117 void
119 {
120 pointCloudProvider =
122 if (!pointCloudProvider)
123 {
124 ARMARX_ERROR << "Could not obtain point cloud provider proxy";
125 return;
126 }
127
128 visionx::FakePointCloudProviderInterfacePrx _fakePointCloudProvider =
129 visionx::FakePointCloudProviderInterfacePrx::checkedCast(pointCloudProvider);
130 if (_fakePointCloudProvider)
131 {
132 widget.pushButtonLoadPointCloud->setEnabled(true);
133 }
134 else
135 {
136 widget.pushButtonLoadPointCloud->setEnabled(false);
137 }
138
139 // The segmenter is optional (it does not exist when processing ground truth segmentations)
140 if (pointCloudSegmenterName != "")
141 {
142 try
143 {
144 pointCloudSegmenter =
146 }
147 catch (const armarx::UserException& e)
148 {
149 ARMARX_INFO << "No segmenter found, assuming ground truth segmentation";
150 }
151 }
152
153 primitiveExtractor = getProxy<visionx::PrimitiveMapperInterfacePrx>(primitiveExtractorName);
154 if (!primitiveExtractor)
155 {
156 ARMARX_ERROR << "Could not obtain primitive extractor proxy";
157 return;
158 }
159
160 pipelineVisualization =
162 if (!pipelineVisualization)
163 {
164 ARMARX_ERROR << "Could not obtain affordance pipeline visualization proxy";
165 return;
166 }
167
168 workingMemory = getProxy<memoryx::WorkingMemoryInterfacePrx>(workingMemoryName);
169 if (!workingMemory)
170 {
171 ARMARX_ERROR << "Could not obtain point working memory proxy";
172 return;
173 }
174
175 debugDrawer = getTopic<DebugDrawerInterfacePrx>("DebugDrawerUpdates");
176 if (!debugDrawer)
177 {
178 ARMARX_ERROR << "Failed to obtain debug drawer proxy";
179 return;
180 }
181
182 connect(widget.pushButtonLoadPointCloud, SIGNAL(clicked()), this, SLOT(loadPointCloud()));
183 connect(widget.pushButtonRun, SIGNAL(clicked()), this, SLOT(run()));
184 connect(widget.pushButtonSetParam, SIGNAL(clicked()), this, SLOT(setParameters()));
185 connect(widget.pushButtonApplyTransformation,
186 SIGNAL(clicked()),
187 this,
189 connect(
190 widget.pushButtonApplyAutoRotate, SIGNAL(clicked()), this, SLOT(applyAutoRotation()));
191 connect(widget.checkBoxShowPrimitives,
192 SIGNAL(toggled(bool)),
193 this,
195 connect(widget.checkBoxShowRootPose,
196 SIGNAL(toggled(bool)),
197 this,
199 connect(widget.pushButtonImportConfig, SIGNAL(clicked()), this, SLOT(importConfig()));
200 connect(widget.pushButtonExportConfig, SIGNAL(clicked()), this, SLOT(exportConfig()));
201
202 if (pointCloudSegmenter)
203 {
204 visionx::LccpParameters lccpParameters = pointCloudSegmenter->getLccpParameters();
205 widget.doubleSpinBoxLccpMinSegmentSize->setValue(lccpParameters.minSegmentSize);
206 widget.doubleSpinBoxLccpVoxelResolution->setValue(lccpParameters.voxelResolution);
207 widget.doubleSpinBoxLccpSeedResolution->setValue(lccpParameters.seedResolution);
208 widget.doubleSpinBoxLccpColorImportance->setValue(lccpParameters.colorImportance);
209 widget.doubleSpinBoxLccpSpatialImportance->setValue(lccpParameters.spatialImportance);
210 widget.doubleSpinBoxLccpNormalImportance->setValue(lccpParameters.normalImportance);
211 widget.doubleSpinBoxLccpConcavityThreshold->setValue(lccpParameters.concavityThreshold);
212 widget.doubleSpinBoxLccpSmoothnessThreshold->setValue(
213 lccpParameters.smoothnessThreshold);
214 }
215 else
216 {
217 widget.doubleSpinBoxLccpMinSegmentSize->setEnabled(false);
218 widget.doubleSpinBoxLccpVoxelResolution->setEnabled(false);
219 widget.doubleSpinBoxLccpSeedResolution->setEnabled(false);
220 widget.doubleSpinBoxLccpColorImportance->setEnabled(false);
221 widget.doubleSpinBoxLccpSpatialImportance->setEnabled(false);
222 widget.doubleSpinBoxLccpNormalImportance->setEnabled(false);
223 widget.doubleSpinBoxLccpConcavityThreshold->setEnabled(false);
224 widget.doubleSpinBoxLccpSmoothnessThreshold->setEnabled(false);
225 }
226
227 visionx::PrimitiveExtractorParameters primtiveParameters =
228 primitiveExtractor->getParameters();
229 ARMARX_LOG << "parameters " << primtiveParameters.minSegmentSize;
230 widget.doubleSpinBoxMinPrimitiveSize->setValue(primtiveParameters.minSegmentSize);
231 widget.doubleSpinBoxMaxPrimitiveSize->setValue(primtiveParameters.maxSegmentSize);
232 widget.doubleSpinBoxEuclideanClusteringTolerance->setValue(
233 primtiveParameters.euclideanClusteringTolerance);
234 widget.doubleSpinBoxOutlierDistanceThreshold->setValue(primtiveParameters.outlierThreshold);
235
236 widget.doubleSpinBoxPlaneMaxIterations->setValue(primtiveParameters.planeMaxIterations);
237 widget.doubleSpinBoxPlaneDistanceThreshold->setValue(
238 primtiveParameters.planeDistanceThreshold);
239 widget.doubleSpinBoxPlaneNormalDistance->setValue(primtiveParameters.planeNormalDistance);
240 widget.doubleSpinBoxPlaneCircularDistanceThreshold->setValue(
241 primtiveParameters.circularDistanceThreshold);
242
243 widget.doubleSpinBoxCylinderMaxIterations->setValue(
244 primtiveParameters.cylinderMaxIterations);
245 widget.doubleSpinBoxCylinderDistanceThreshold->setValue(
246 primtiveParameters.cylinderDistanceThreshold);
247 widget.doubleSpinBoxCylinderRadiusLimit->setValue(primtiveParameters.cylinderRadiusLimit);
248
249 widget.doubleSpinBoxSphereMaxIterations->setValue(primtiveParameters.sphereMaxIterations);
250 widget.doubleSpinBoxSphereDistanceThreshold->setValue(
251 primtiveParameters.sphereDistanceThreshold);
252 widget.doubleSpinBoxSphereNormalDistance->setValue(primtiveParameters.sphereNormalDistance);
253 }
254
255 void
257 {
258 QObject::disconnect(this, SLOT(loadPointCloud()));
259 QObject::disconnect(this, SLOT(run()));
260 QObject::disconnect(this, SLOT(setParameters()));
261 QObject::disconnect(this, SLOT(visualizationOptionsChanged()));
262 QObject::disconnect(this, SLOT(importConfig()));
263 QObject::disconnect(this, SLOT(exportConfig()));
264 }
265
266 void
268 {
269 QString filename =
270 QFileDialog::getOpenFileName(NULL, "Open Point Cloud", "", "Point Cloud Files (*.pcd)");
271 if (filename != "")
272 {
273 widget.lineEditPointCloud->setText(filename);
274 }
275 }
276
277 void
279 {
280 visionx::FakePointCloudProviderInterfacePrx _fakePointCloudProvider =
281 visionx::FakePointCloudProviderInterfacePrx::checkedCast(pointCloudProvider);
282 if (_fakePointCloudProvider)
283 {
284 std::string path = widget.lineEditPointCloud->text().toStdString();
285 _fakePointCloudProvider->setPointCloudFilename(path);
286 }
287
288 if (pointCloudSegmenter)
289 {
290 visionx::LccpParameters segmenterPrm;
291 segmenterPrm.minSegmentSize = widget.doubleSpinBoxLccpMinSegmentSize->value();
292 segmenterPrm.voxelResolution = widget.doubleSpinBoxLccpVoxelResolution->value();
293 segmenterPrm.seedResolution = widget.doubleSpinBoxLccpSeedResolution->value();
294 segmenterPrm.colorImportance = widget.doubleSpinBoxLccpColorImportance->value();
295 segmenterPrm.spatialImportance = widget.doubleSpinBoxLccpSpatialImportance->value();
296 segmenterPrm.normalImportance = widget.doubleSpinBoxLccpNormalImportance->value();
297 segmenterPrm.concavityThreshold = widget.doubleSpinBoxLccpConcavityThreshold->value();
298 segmenterPrm.smoothnessThreshold = widget.doubleSpinBoxLccpSmoothnessThreshold->value();
299 pointCloudSegmenter->setLccpParameters(segmenterPrm);
300 }
301
302 visionx::PrimitiveExtractorParameters primitiveExtractionPrm;
303 primitiveExtractionPrm.minSegmentSize = widget.doubleSpinBoxMinPrimitiveSize->value();
304 primitiveExtractionPrm.maxSegmentSize = widget.doubleSpinBoxMaxPrimitiveSize->value();
305 primitiveExtractionPrm.planeMaxIterations = widget.doubleSpinBoxPlaneMaxIterations->value();
306 primitiveExtractionPrm.planeDistanceThreshold =
307 widget.doubleSpinBoxPlaneDistanceThreshold->value();
308 primitiveExtractionPrm.planeNormalDistance =
309 widget.doubleSpinBoxPlaneNormalDistance->value();
310 primitiveExtractionPrm.cylinderMaxIterations =
311 widget.doubleSpinBoxCylinderMaxIterations->value();
312 primitiveExtractionPrm.cylinderDistanceThreshold =
313 widget.doubleSpinBoxCylinderDistanceThreshold->value();
314 primitiveExtractionPrm.cylinderRadiusLimit =
315 widget.doubleSpinBoxCylinderRadiusLimit->value();
316 primitiveExtractionPrm.sphereMaxIterations =
317 widget.doubleSpinBoxSphereMaxIterations->value();
318 primitiveExtractionPrm.sphereDistanceThreshold =
319 widget.doubleSpinBoxSphereDistanceThreshold->value();
320 primitiveExtractionPrm.sphereNormalDistance =
321 widget.doubleSpinBoxSphereNormalDistance->value();
322 primitiveExtractionPrm.euclideanClusteringTolerance =
323 widget.doubleSpinBoxEuclideanClusteringTolerance->value();
324 primitiveExtractionPrm.outlierThreshold =
325 widget.doubleSpinBoxOutlierDistanceThreshold->value();
326 primitiveExtractionPrm.circularDistanceThreshold =
327 widget.doubleSpinBoxPlaneCircularDistanceThreshold->value();
328 primitiveExtractor->setParameters(primitiveExtractionPrm);
329 }
330
331 void
333 {
335
336 visionx::CapturingPointCloudProviderInterfacePrx cx =
337 visionx::CapturingPointCloudProviderInterfacePrx::checkedCast(pointCloudProvider);
338 if (cx)
339 {
340 cx->begin_startCaptureForNumFrames(1);
341 }
342 }
343
344 void
346 {
347 pipelineVisualization->begin_enableVisualization(
348 widget.checkBoxShowPrimitives->isChecked(), false, false);
349
350 if (widget.checkBoxShowRootPose->isChecked())
351 {
352 debugDrawer->setPoseVisu(
353 "PrimitiveExtractorGuiLayer", "RootPose", new Pose(Eigen::Matrix4f::Identity()));
354 }
355 else
356 {
357 debugDrawer->removePoseVisu("PrimitiveExtractorGuiLayer", "RootPose");
358 }
359 }
360
361 void
363 {
364 std::string filename = widget.lineEditPointCloud->text().toStdString();
365
366 if (!boost::algorithm::ends_with(filename, ".pcd") ||
367 !std::filesystem::is_regular_file(filename))
368 {
369 ARMARX_WARNING << "Unable to load point cloud " << filename;
370 return;
371 }
372
373 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr pc(new pcl::PointCloud<pcl::PointXYZRGBA>());
374 if (pcl::io::loadPCDFile<pcl::PointXYZRGBA>(filename.c_str(), *pc) == -1)
375 {
376 ARMARX_WARNING << "Unable to load point cloud " << filename;
377 return;
378 }
379
380 ARMARX_INFO << "Transforming point cloud: " << filename;
381 Eigen::Affine3f scaling(Eigen::Scaling((float)widget.doubleSpinBoxScaling->value()));
382 pcl::transformPointCloud(*pc, *pc, scaling);
383
384 std::string out_filename =
385 QFileDialog::getSaveFileName(
386 NULL, "Save transformed PCD", filename.c_str(), "Point Cloud Files (*.pcd)")
387 .toStdString();
388 if (filename != "")
389 {
390 pcl::io::savePCDFileASCII(out_filename, *pc);
391 ARMARX_INFO << "Point cloud saved";
392 }
393 }
394
395 void
397 {
398 QString filename = QFileDialog::getOpenFileName(
399 NULL, "Open Pipeline Configuration file", "", "Config file (*.cfg)");
400 if (filename == "")
401 {
402 return;
403 }
404
405 QSettings config(filename, QSettings::IniFormat);
406
407 config.beginGroup("LCCP");
408 widget.doubleSpinBoxLccpMinSegmentSize->setValue(
409 config.value("MinSegmentSize", widget.doubleSpinBoxLccpMinSegmentSize->value())
410 .toDouble());
411 widget.doubleSpinBoxLccpVoxelResolution->setValue(
412 config.value("VoxelResolution", widget.doubleSpinBoxLccpVoxelResolution->value())
413 .toDouble());
414 widget.doubleSpinBoxLccpSeedResolution->setValue(
415 config.value("SeedResolution", widget.doubleSpinBoxLccpSeedResolution->value())
416 .toDouble());
417 widget.doubleSpinBoxLccpColorImportance->setValue(
418 config.value("ColorImportance", widget.doubleSpinBoxLccpColorImportance->value())
419 .toDouble());
420 widget.doubleSpinBoxLccpSpatialImportance->setValue(
421 config.value("SpatialImportance", widget.doubleSpinBoxLccpSpatialImportance->value())
422 .toDouble());
423 widget.doubleSpinBoxLccpNormalImportance->setValue(
424 config.value("NormalImportance", widget.doubleSpinBoxLccpNormalImportance->value())
425 .toDouble());
426 widget.doubleSpinBoxLccpConcavityThreshold->setValue(
427 config.value("ConcavityThreshold", widget.doubleSpinBoxLccpConcavityThreshold->value())
428 .toDouble());
429 widget.doubleSpinBoxLccpSmoothnessThreshold->setValue(
430 config
431 .value("SmoothnessThreshold", widget.doubleSpinBoxLccpSmoothnessThreshold->value())
432 .toDouble());
433 config.endGroup();
434
435 config.beginGroup("PrimitiveExtraction");
436 widget.doubleSpinBoxMinPrimitiveSize->setValue(
437 config.value("MinPrimitiveSize", widget.doubleSpinBoxMinPrimitiveSize->value())
438 .toDouble());
439 widget.doubleSpinBoxMaxPrimitiveSize->setValue(
440 config.value("MaxPrimitiveSize", widget.doubleSpinBoxMaxPrimitiveSize->value())
441 .toDouble());
442 widget.doubleSpinBoxPlaneMaxIterations->setValue(
443 config.value("PlaneMaxIterations", widget.doubleSpinBoxPlaneMaxIterations->value())
444 .toDouble());
445 widget.doubleSpinBoxPlaneDistanceThreshold->setValue(
446 config
447 .value("PlaneDistanceThreshold",
448 widget.doubleSpinBoxPlaneDistanceThreshold->value())
449 .toDouble());
450 widget.doubleSpinBoxPlaneNormalDistance->setValue(
451 config.value("PlaneNormalDistance", widget.doubleSpinBoxPlaneNormalDistance->value())
452 .toDouble());
453 widget.doubleSpinBoxPlaneCircularDistanceThreshold->setValue(
454 config
455 .value("PlaneCircularDistanceThreshold",
456 widget.doubleSpinBoxPlaneCircularDistanceThreshold->value())
457 .toDouble());
458 widget.doubleSpinBoxCylinderMaxIterations->setValue(
459 config
460 .value("CylinderMaxIterations", widget.doubleSpinBoxCylinderMaxIterations->value())
461 .toDouble());
462 widget.doubleSpinBoxCylinderDistanceThreshold->setValue(
463 config
464 .value("CylinderDistanceThreshold",
465 widget.doubleSpinBoxCylinderDistanceThreshold->value())
466 .toDouble());
467 widget.doubleSpinBoxCylinderRadiusLimit->setValue(
468 config.value("CylinderRadiusLimit", widget.doubleSpinBoxSphereMaxIterations->value())
469 .toDouble());
470 widget.doubleSpinBoxSphereMaxIterations->setValue(
471 config.value("SphereMaxIterations", widget.doubleSpinBoxSphereMaxIterations->value())
472 .toDouble());
473 widget.doubleSpinBoxSphereDistanceThreshold->setValue(
474 config
475 .value("SphereDistanceThreshold",
476 widget.doubleSpinBoxSphereDistanceThreshold->value())
477 .toDouble());
478 widget.doubleSpinBoxSphereNormalDistance->setValue(
479 config.value("SphereNormalDistance", widget.doubleSpinBoxSphereNormalDistance->value())
480 .toDouble());
481 widget.doubleSpinBoxEuclideanClusteringTolerance->setValue(
482 config
483 .value("EuclideanClusteringTolerance",
484 widget.doubleSpinBoxEuclideanClusteringTolerance->value())
485 .toDouble());
486 widget.doubleSpinBoxOutlierDistanceThreshold->setValue(
487 config
488 .value("OutlierDistanceThreshold",
489 widget.doubleSpinBoxOutlierDistanceThreshold->value())
490 .toDouble());
491 config.endGroup();
492
493 ARMARX_INFO << "Current settings read from '" << filename << "'";
494 }
495
496 void
498 {
499 QString filename = QFileDialog::getSaveFileName(
500 NULL, "Save Pipeline Configuration file", "", "Config file (*.cfg)");
501 if (filename == "")
502 {
503 return;
504 }
505
506 QSettings config(filename, QSettings::IniFormat);
507
508 config.beginGroup("LCCP");
509 config.setValue("MinSegmentSize", widget.doubleSpinBoxLccpMinSegmentSize->value());
510 config.setValue("VoxelResolution", widget.doubleSpinBoxLccpVoxelResolution->value());
511 config.setValue("SeedResolution", widget.doubleSpinBoxLccpSeedResolution->value());
512 config.setValue("ColorImportance", widget.doubleSpinBoxLccpColorImportance->value());
513 config.setValue("SpatialImportance", widget.doubleSpinBoxLccpSpatialImportance->value());
514 config.setValue("NormalImportance", widget.doubleSpinBoxLccpNormalImportance->value());
515 config.setValue("ConcavityThreshold", widget.doubleSpinBoxLccpConcavityThreshold->value());
516 config.setValue("SmoothnessThreshold",
517 widget.doubleSpinBoxLccpSmoothnessThreshold->value());
518 config.endGroup();
519
520 config.beginGroup("PrimitiveExtraction");
521 config.setValue("MinPrimitiveSize", widget.doubleSpinBoxMinPrimitiveSize->value());
522 config.setValue("MaxPrimitiveSize", widget.doubleSpinBoxMaxPrimitiveSize->value());
523 config.setValue("PlaneMaxIterations", widget.doubleSpinBoxPlaneMaxIterations->value());
524 config.setValue("PlaneDistanceThreshold",
525 widget.doubleSpinBoxPlaneDistanceThreshold->value());
526 config.setValue("PlaneNormalDistance", widget.doubleSpinBoxPlaneNormalDistance->value());
527 config.setValue("PlaneCircularDistanceThreshold",
528 widget.doubleSpinBoxPlaneCircularDistanceThreshold->value());
529 config.setValue("CylinderMaxIterations",
530 widget.doubleSpinBoxCylinderMaxIterations->value());
531 config.setValue("CylinderDistanceThreshold",
532 widget.doubleSpinBoxCylinderDistanceThreshold->value());
533 config.setValue("CylinderRadiusLimit", widget.doubleSpinBoxSphereMaxIterations->value());
534 config.setValue("SphereMaxIterations", widget.doubleSpinBoxSphereMaxIterations->value());
535 config.setValue("SphereDistanceThreshold",
536 widget.doubleSpinBoxSphereDistanceThreshold->value());
537 config.setValue("SphereNormalDistance", widget.doubleSpinBoxSphereNormalDistance->value());
538 config.setValue("EuclideanClusteringTolerance",
539 widget.doubleSpinBoxEuclideanClusteringTolerance->value());
540 config.setValue("OutlierDistanceThreshold",
541 widget.doubleSpinBoxOutlierDistanceThreshold->value());
542 config.endGroup();
543
544 config.sync();
545
546 ARMARX_INFO << "Current settings saved to '" << filename << "'";
547 }
548
549 // Apply a rotation to the pointCloud corresponding to the inverse of the rotational component of the biggest plane primitive.
550 // The should result in a point cloud in which the ground is fitted exactly into the x/y-plane.
551 void
553 {
555 primitives.reset(new AffordanceKitArmarX::PrimitiveSetArmarX(
556 workingMemory->getEnvironmentalPrimitiveSegment()));
557
558 bool found = false;
559 unsigned int indexOfBiggestPrimitive;
560 unsigned int maxSampleSize = 0;
561
562 using boost::dynamic_pointer_cast;
563 using std::dynamic_pointer_cast;
564
565 // Find biggest plane primitive.
566 for (unsigned int i = 0; i < primitives->size(); i++)
567 {
568 AffordanceKit::PrimitivePtr primitive = primitives->at(i);
569 if (!dynamic_pointer_cast<AffordanceKit::Plane>(primitive))
570 {
571 continue;
572 }
573 AffordanceKit::PlanePtr plane = dynamic_pointer_cast<AffordanceKit::Plane>(primitive);
574 plane->sample(20, 1);
575 unsigned int curPlaneSize = plane->getSamplingSize();
576 if (curPlaneSize > maxSampleSize)
577 {
578 found = true;
579 maxSampleSize = curPlaneSize;
580 indexOfBiggestPrimitive = i;
581 }
582 }
583
584 if (!found)
585 {
586 ARMARX_WARNING << "Did not find any plane-shaped primitives to rotate upon!";
587 return;
588 }
589
590 // If found: get rotation of biggest plane primitive.
591 AffordanceKit::PrimitivePtr primitive = primitives->at(indexOfBiggestPrimitive);
592 if (!dynamic_pointer_cast<AffordanceKit::Plane>(primitive))
593 {
594 ARMARX_ERROR << "Could not convert to planar primitive";
595 return;
596 }
597 AffordanceKit::PlanePtr plane = dynamic_pointer_cast<AffordanceKit::Plane>(primitive);
598 Eigen::Matrix4f gp = plane->getPose();
599
600 debugDrawer->setPoseVisu(
601 "PrimitiveExtractorGuiLayer", "BiggestPlaneRotation", new Pose(gp));
602
603 Eigen::Matrix4f gpi = gp.inverse();
604
605 // Rotate the point cloud around the inverse of the ground's rotation matrix.
606 std::string filename = widget.lineEditPointCloud->text().toStdString();
607 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr pc(new pcl::PointCloud<pcl::PointXYZRGBA>());
608 if (pcl::io::loadPCDFile<pcl::PointXYZRGBA>(filename.c_str(), *pc) == -1)
609 {
610 ARMARX_WARNING << "Unable to load point cloud " << filename << " for auto-rotation.";
611 return;
612 }
613
614 // Apply transformation
615 pcl::transformPointCloud(*pc, *pc, gpi);
616
617 // Use a simple heuristic to check if z points in the right direction (upwards)
618 int balance = 0;
619 for (unsigned int i = 0; i < pc->points.size(); i++)
620 {
621 balance += (pc->points[i].z >= 0) ? 1 : -1;
622 }
623
624 ARMARX_INFO << "Z-axis balance after transformation: " << balance;
625 if (balance < 0)
626 {
627 // z seems to point in the wrong direction (most point have negative z-value)
628 Eigen::Affine3f rotation(Eigen::AngleAxisf(M_PI, Eigen::Vector3f::UnitX()));
629 pcl::transformPointCloud(*pc, *pc, rotation);
630
631 ARMARX_INFO << "Flipping Z axis";
632
633 // Update visualization
634 Eigen::Matrix4f T = rotation.matrix();
635 debugDrawer->setPoseVisu(
636 "PrimitiveExtractorGuiLayer", "BiggestPlaneRotation", new Pose(T * gp));
637 }
638
639 std::string out_filename =
640 QFileDialog::getSaveFileName(
641 NULL, "Save transformed PCD", filename.c_str(), "Point Cloud Files (*.pcd)")
642 .toStdString();
643 if (filename != "")
644 {
645 pcl::io::savePCDFileASCII(out_filename, *pc);
646 ARMARX_INFO << "Point cloud saved";
647 }
648 }
649} // namespace armarx
#define M_PI
Definition MathTools.h:17
virtual QPointer< QWidget > getWidget()
getWidget returns a pointer to the a widget of this controller.
TopicProxyType getTopic(const std::string &name)
Returns a proxy of the specified topic.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
The Pose class.
Definition Pose.h:243
QPointer< QDialog > getConfigDialog(QWidget *parent=0) override
getConfigDialog returns a pointer to the a configuration widget of this controller.
void configured() override
This function must be implemented by the user, if he supplies a config dialog.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_ERROR
The logging level for unexpected behaviour, that must be fixed.
Definition Logging.h:196
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_LOG
Definition Logging.h:165
std::shared_ptr< PrimitiveSetArmarX > PrimitiveSetArmarXPtr
This file offers overloads of toIce() and fromIce() functions for STL container types.