PrimitiveExtractor.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * Copyright (C) 2011-2016, High Performance Humanoid Technologies (H2T), Karlsruhe Institute of Technology (KIT), all rights reserved.
5 *
6 * ArmarX is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 *
10 * ArmarX is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * @package VisionX
19 * @author Eren Aksoy ( eren dot aksoy at kit dot edu )
20 * @date 2015
21 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
22 * GNU General Public License
23 */
24
25#include "PrimitiveExtractor.h"
26
27#include <set>
28
29#include <boost/lexical_cast.hpp>
30
31#include <QSettings>
32
33#include <pcl/common/colors.h>
34
38
40
41#include <AffordanceKit/PrimitiveExtractor.h>
42#include <AffordanceKit/primitives/Box.h>
43#include <AffordanceKit/primitives/Cylinder.h>
44#include <AffordanceKit/primitives/Plane.h>
45#include <AffordanceKit/primitives/Sphere.h>
46
47using namespace memoryx;
48using namespace armarx;
49using namespace pcl;
50
51void
53{
54 lastProcessedTimestamp = 0;
55
56 providerName = getProperty<std::string>("segmenterName").getValue();
57
58 // get required primitive parameters
59 minSegmentSize = getProperty<double>("minimumSegmentSize").getValue();
60 maxSegmentSize = getProperty<double>("maximumSegmentSize").getValue();
61 pMaxIter = getProperty<double>("planeMaximumIteration").getValue();
62 pDistThres = getProperty<float>("planeDistanceThreshold").getValue();
63 pNorDist = getProperty<float>("planeNormalDistance").getValue();
64 cMaxIter = getProperty<double>("cylinderMaximumIteration").getValue();
65 cDistThres = getProperty<float>("cylinderDistanceThreshold").getValue();
66 cRadLim = getProperty<float>("cylinderRadiousLimit").getValue();
67 sMaxIter = getProperty<double>("sphereMaximumIteration").getValue();
68 sDistThres = getProperty<float>("sphereDistanceThreshold").getValue();
69 sNorDist = getProperty<float>("sphereNormalDistance").getValue();
70 verbose = getProperty<bool>("vebose").getValue();
71 euclideanClusteringTolerance = getProperty<float>("euclideanClusteringTolerance").getValue();
72 outlierThreshold = getProperty<float>("outlierThreshold").getValue();
73 circleDistThres = getProperty<float>("circularDistanceThreshold").getValue();
74
75 spatialSamplingDistance = getProperty<float>("SpatialSamplingDistance").getValue();
76 numOrientationalSamplings = getProperty<int>("NumOrientationalSamplings").getValue();
77
78 // reset all point cloud pointers
79 labeledCloudPtr.reset(new PointCloud<PointXYZL>);
80 inliersCloudPtr.reset(new PointCloud<PointXYZL>);
81 graspCloudPtr.reset(new PointCloud<PointXYZL>);
82
83 // init the primitive class
84 primitiveExtractor.reset(new AffordanceKit::PrimitiveExtractor());
85 primitiveExtractor->updateParameters(verbose,
86 minSegmentSize,
87 maxSegmentSize,
88 pMaxIter,
89 pDistThres,
90 pNorDist,
91 cMaxIter,
92 cDistThres,
93 cRadLim,
94 sMaxIter,
95 sDistThres,
96 sNorDist,
97 euclideanClusteringTolerance,
98 outlierThreshold,
99 circleDistThres);
100 usingPointCloudProvider(providerName);
101
102 if (getProperty<std::string>("RobotStateComponentName").getValue() != "")
103 {
104 usingProxy(getProperty<std::string>("RobotStateComponentName").getValue());
105 sourceFrameName = getProperty<std::string>("sourceFrameName").getValue();
106 }
107 usingProxy(getProperty<std::string>("WorkingMemoryName").getValue());
108
109 mappingEnabled = true;
110
111 enablePrimitiveFusion = getProperty<bool>("enablePrimitiveFusion").getValue();
112
113 offeringTopic(getProperty<std::string>("DebugDrawerTopicName").getValue());
114 offeringTopic(getProperty<std::string>("SegmentedPointCloudTopicName").getValue());
115
116 offeringTopic(getProperty<std::string>("DebugObserverName").getValue());
117}
118
119void
121{
122 enableResultPointClouds<PointXYZL>("GraspPointCloudResult");
123 enableResultPointClouds<PointXYZL>("PrimitiveExtractorResult");
125
127 getProperty<std::string>("WorkingMemoryName").getValue());
128 if (!workingMemory)
129 {
130 ARMARX_ERROR << "Failed to obtain working memory proxy";
131 }
132
133 environmentalPrimitiveSegment = workingMemory->getEnvironmentalPrimitiveSegment();
134 if (!environmentalPrimitiveSegment)
135 {
136 ARMARX_ERROR << "Failed to obtain environmental primitive segment pointer";
137 }
138
140 getProperty<std::string>("SegmentedPointCloudTopicName").getValue());
141
142 debugDrawerTopicPrx = getTopic<DebugDrawerInterfacePrx>(
143 getProperty<std::string>("DebugDrawerTopicName").getValue());
144
145 if (getProperty<std::string>("RobotStateComponentName").getValue() != "")
146 {
147 robotStateComponent = getProxy<RobotStateComponentInterfacePrx>(
148 getProperty<std::string>("RobotStateComponentName").getValue());
149 if (!robotStateComponent->getSynchronizedRobot()->hasRobotNode(sourceFrameName))
150 {
151 ARMARX_ERROR << "source node does not exist!";
152 robotStateComponent = nullptr;
153 }
154 }
155
156 lastUsedLabel = 0;
157
158 debugDrawerTopicPrx->clearLayer("primitiveMapper");
159
161 getProperty<std::string>("DebugObserverName").getValue());
162 numViews = 0;
163
164
165 StringVariantBaseMap debugValues;
166 debugValues["processingTime"] = new Variant(0.0);
167 debugValues["oldPrimitives"] = new Variant(0.0);
168 debugValues["visiblePrimitives"] = new Variant(0.0);
169 debugValues["newPrimitives"] = new Variant(0.0);
170 debugValues["modifiedPrimitives"] = new Variant(0.0);
171 debugValues["unmodifiedPrimitives"] = new Variant(0.0);
172 debugValues["removedPrimitives"] = new Variant(0.0);
173 debugValues["numViews"] = new Variant(0.0);
174 debugObserver->setDebugChannel(getName(), debugValues);
175}
176
177void
181
182void
186
187void
189{
190 std::unique_lock lock(enableMutex);
191 mappingEnabled = true;
192}
193
194void
196{
197 std::unique_lock lock(enableMutex);
198 mappingEnabled = false;
199}
200
201void
203{
204 if (!waitForPointClouds(providerName, 10000))
205 {
206 return;
207 }
208
209 {
210 std::unique_lock lock(enableMutex);
211
212 if (!mappingEnabled)
213 {
214 return;
215 }
216 }
217
218 // start timer
219 IceUtil::Time startTime = IceUtil::Time::now();
220
221 pcl::PointCloud<pcl::PointXYZRGBL>::Ptr temp(new pcl::PointCloud<pcl::PointXYZRGBL>());
222 if (!getPointClouds(providerName, temp))
223 {
224 ARMARX_WARNING << "Unable to get point cloud data.";
225 return;
226 }
227
228 pcl::copyPointCloud(*temp, *labeledCloudPtr);
229 if (labeledCloudPtr->size() == 0)
230 {
231 ARMARX_WARNING << "input point cloud is empty";
232
233 provideResultPointClouds(labeledCloudPtr, "GraspPointCloudResult");
234 provideResultPointClouds(labeledCloudPtr, "PrimitiveExtractorResult");
235
236 return;
237 }
238
239
240 inliersCloudPtr = primitiveExtractor->computeScenePrimitives(labeledCloudPtr);
241 graspCloudPtr = primitiveExtractor->getDetectedGraspPoints();
242
243
244 const std::vector<pcl::PointCloud<pcl::PointXYZL>::Ptr>& planeInliers =
245 primitiveExtractor->getPlaneInliers();
246 const std::vector<pcl::PointCloud<pcl::PointXYZL>::Ptr>& cylinderInliers =
247 primitiveExtractor->getCylinderInliers();
248 const std::vector<pcl::PointCloud<pcl::PointXYZL>::Ptr>& sphereInliers =
249 primitiveExtractor->getSphereInliers();
250 const std::vector<float>& circularityProbabilities =
251 primitiveExtractor->getCircularityProbabilities();
252
253 ARMARX_INFO << " ********* Detected Plane number: " << planeInliers.size();
254 ARMARX_INFO << " ********* Detected Cylinder number: " << cylinderInliers.size();
255 ARMARX_INFO << " ********* Detected Sphere number: " << sphereInliers.size();
256 ARMARX_INFO << " ********* Detected Circle Probabilities: " << circularityProbabilities.size();
257
258 // publish the extracted inliers
259 provideResultPointClouds(graspCloudPtr, "GraspPointCloudResult");
260 provideResultPointClouds(inliersCloudPtr, "PrimitiveExtractorResult");
261
262 ARMARX_INFO << "Primitive Extraction took "
263 << (IceUtil::Time::now() - startTime).toSecondsDouble() << " secs";
264
265 // Push each extracted primitive to the memory
266 startTime = IceUtil::Time::now();
267
268 bool enabled;
269 {
270 std::unique_lock lock(enableMutex);
271 enabled = mappingEnabled;
272 }
273
274 if (enabled)
275 {
276 if (getProperty<bool>("UseAffordanceExtractionLibExport").getValue())
277 {
278 pushPrimitivesToMemoryAffordanceKit(temp->header.stamp);
279 }
280 else
281 {
282 pushPrimitivesToMemory(temp->header.stamp);
283 }
284
285 {
286 std::unique_lock lock(timestampMutex);
287 lastProcessedTimestamp = temp->header.stamp;
288 }
289 }
290
291 // Signal the availability of a new segmentation
292 pointCloudSegmentationPrx->reportNewPointCloudSegmentation();
293
294
295 ARMARX_INFO << "Mapping took " << (IceUtil::Time::now() - startTime).toSecondsDouble()
296 << " secs";
297}
298
299float
300PrimitiveExtractor::getGraspPointInlierRatio(memoryx::EnvironmentalPrimitiveBasePtr leftPrimitive,
301 memoryx::EnvironmentalPrimitiveBasePtr rightPrimitive)
302{
303 FramedPosePtr pose = FramedPosePtr::dynamicCast(rightPrimitive->getPose());
304 Eigen::Matrix4f worldToPrimitive = pose->toEigen().inverse();
305
306 Eigen::Vector3f oBBDimensions =
307 Vector3Ptr::dynamicCast(rightPrimitive->getOBBDimensions())->toEigen();
308 Eigen::Vector3f extent = 0.5 * oBBDimensions + (Eigen::Vector3f::Ones() * 50);
309
310 float numPoints = 0.0;
311 memoryx::PointList graspPoints = leftPrimitive->getGraspPoints();
312
313 for (Vector3BasePtr& p : graspPoints)
314 {
315 Eigen::Vector4f temp = Eigen::Vector4f(p->x, p->y, p->z, 1.0);
316 Eigen::Vector3f point = (worldToPrimitive * temp).head<3>();
317
318 if ((point.array().abs() < extent.array()).all())
319 {
320 numPoints++;
321 }
322 }
323
324 return numPoints / graspPoints.size();
325}
326
327void
328PrimitiveExtractor::fusePrimitives(std::vector<memoryx::EntityBasePtr>& newPrimitives,
329 long timestamp)
330{
331 // memoryx::SegmentLockBasePtr segmentLock = environmentalPrimitiveSegment->lockSegment();
332 std::vector<EnvironmentalPrimitiveBasePtr> primitives =
333 environmentalPrimitiveSegment->getEnvironmentalPrimitives();
334 // environmentalPrimitiveSegment->unlockSegment(segmentLock);
335
336 std::vector<memoryx::EnvironmentalPrimitiveBasePtr> nonVisiblePrimitives;
337 std::vector<memoryx::EnvironmentalPrimitiveBasePtr> visiblePrimitives;
338
339 std::vector<memoryx::EnvironmentalPrimitiveBasePtr> modifiedPrimitives;
340 std::set<std::string> processedPrimitives;
341 std::set<std::string> primitivesToRemove;
342
343 if (robotStateComponent)
344 {
345 Eigen::Matrix4f cameraToWorld = Eigen::Matrix4f::Identity();
346 if (sourceFrameName != armarx::GlobalFrame)
347 {
348 auto snapshot = robotStateComponent->getRobotSnapshotAtTimestamp(timestamp);
349 snapshot->ref();
350 FramedPoseBasePtr pose = snapshot->getRobotNode(sourceFrameName)->getGlobalPose();
351 snapshot->unref();
352 cameraToWorld = armarx::FramedPosePtr::dynamicCast(pose)->toEigen();
353 }
354
355 primitiveFilter.updateCameraPosition(cameraToWorld);
356 // if (verbose)
357 // {
358 primitiveFilter.visualizeFrustum(debugDrawerTopicPrx);
359 // }
360 primitiveFilter.getPrimitivesInFieldOfView(
361 primitives, visiblePrimitives, nonVisiblePrimitives);
362 }
363 else
364 {
365
366 ARMARX_ERROR << "Frustum culling not possible without RobotStateComponent assuming that "
367 "all primitives are visible";
368 visiblePrimitives = primitives;
369 }
370
371
372 if (getProperty<bool>("EnableBoxPrimitives").getValue())
373 {
374
375 // fusionStrategy.findBoxPrimitives(primitives, newPrimitives, environmentalPrimitiveSegment);
376 for (const EnvironmentalPrimitiveBasePtr& p : primitives)
377 {
378 if (p->ice_isA(memoryx::BoxPrimitiveBase::ice_staticId()))
379 {
380 primitivesToRemove.insert(p->getId());
381 }
382 }
383 }
384
385 if (nonVisiblePrimitives.size())
386 {
387 ARMARX_INFO << "found " << nonVisiblePrimitives.size()
388 << " non visible primitives in field of view.";
389 for (memoryx::EnvironmentalPrimitiveBasePtr& p : nonVisiblePrimitives)
390 {
391 p->setProbability(std::max(0.0, p->getProbability() - 0.1));
392 if (p->getProbability() <= 0.4)
393 {
394 primitivesToRemove.insert(p->getId());
395 }
396 else
397 {
398 modifiedPrimitives.push_back(p);
399 }
400 }
401 }
402
403
404 ARMARX_DEBUG << "total primitives " << primitives.size() << " visible primitives "
405 << visiblePrimitives.size() << "new primitives" << newPrimitives.size();
406
407 std::vector<memoryx::EntityBasePtr> uniqueEntities;
408 for (const memoryx::EntityBasePtr& newEntity : newPrimitives)
409 {
410 memoryx::EnvironmentalPrimitiveBasePtr newPrimitive =
411 memoryx::EnvironmentalPrimitiveBasePtr::dynamicCast(newEntity);
412
413 std::vector<EnvironmentalPrimitiveBasePtr> intersectingPrimitives;
414 fusionStrategy.getIntersectingSimilarPrimitives(
415 primitives, newPrimitive, intersectingPrimitives, 50.0);
416 // fusionStrategy.getIntersectingSimilarPrimitives(visiblePrimitives, newPrimitive, intersectingPrimitives, 50.0);
417
418
419 bool addPrimitive = true;
420 for (EnvironmentalPrimitiveBasePtr& intersectingPrimitive : intersectingPrimitives)
421 {
422 if (primitivesToRemove.count(intersectingPrimitive->getId()))
423 {
424 continue;
425 }
426
427 int currentLabel = 0;
428 float inlierRatioLeft = getGraspPointInlierRatio(newPrimitive, intersectingPrimitive);
429 float inlierRatioRight = getGraspPointInlierRatio(intersectingPrimitive, newPrimitive);
430
431 if (inlierRatioLeft <= 0.1 && inlierRatioRight <= 0.1)
432 {
433 processedPrimitives.insert(intersectingPrimitive->getId());
434 }
435
436 if (inlierRatioLeft >= 0.9 && inlierRatioRight >= 0.9)
437 {
438 intersectingPrimitive->setTime(newPrimitive->getTime());
439 float newProbability = std::fmax(newPrimitive->getProbability(),
440 intersectingPrimitive->getProbability() + 0.15);
441 intersectingPrimitive->setProbability(std::fmin(1.0, newProbability));
442 modifiedPrimitives.push_back(intersectingPrimitive);
443
444 addPrimitive = false;
445 break;
446 }
447 else if (inlierRatioLeft <= 0.3 && inlierRatioRight >= 0.7)
448 {
449
450 primitivesToRemove.insert(intersectingPrimitive->getId());
451 }
452 else if (inlierRatioLeft >= 0.7 && inlierRatioRight <= 0.3)
453 {
454
455 addPrimitive = false;
456 break;
457 }
458 else if ((inlierRatioLeft + inlierRatioRight) >= 1.4)
459 {
460 if ((newPrimitive->getOBBDimensions()->x * newPrimitive->getOBBDimensions()->y) >
461 (intersectingPrimitive->getOBBDimensions()->x *
462 intersectingPrimitive->getOBBDimensions()->y))
463 {
464 primitivesToRemove.insert(intersectingPrimitive->getId());
465 }
466 else
467 {
468 addPrimitive = false;
469 break;
470 }
471 }
472
473 if (currentLabel)
474 {
475 }
476 else if (intersectingPrimitive->getLabel())
477 {
478 currentLabel = intersectingPrimitive->getLabel();
479 }
480
481 if (currentLabel == 0 && intersectingPrimitives.size())
482 {
483 currentLabel = ++lastUsedLabel;
484 }
485 newPrimitive->setLabel(currentLabel);
486
487 for (EnvironmentalPrimitiveBasePtr& x : intersectingPrimitives)
488 {
489 x->setLabel(currentLabel);
490 modifiedPrimitives.push_back(x);
491 }
492 }
493 if (addPrimitive)
494 {
495 uniqueEntities.push_back(newPrimitive);
496 }
497 }
498
499 if (robotStateComponent)
500 {
501
502 for (memoryx::EnvironmentalPrimitiveBasePtr& p : visiblePrimitives)
503 {
504
505 if (processedPrimitives.count(p->getId()) || primitivesToRemove.count(p->getId()))
506 {
507 continue;
508 }
509
510 float probability = p->getProbability();
511 p->setProbability(std::max(0.0, p->getProbability() - 0.5));
512 ARMARX_LOG << "reducing probability for primitive in field of view: " << p->getId()
513 << " from " << probability << " to " << p->getProbability();
514
515 if (p->getProbability() <= 0.4)
516 {
517 ARMARX_LOG << "scheduling primitive " << p->getId() << " for removal";
518 primitivesToRemove.insert(p->getId());
519 }
520 else
521 {
522 modifiedPrimitives.push_back(p);
523 }
524 }
525 }
526
527
528 ARMARX_INFO << "removing " << primitivesToRemove.size() << " primitives";
529
530 for (const std::string& x : primitivesToRemove)
531 {
532 environmentalPrimitiveSegment->removeEntity(x);
533 }
534
535 int modified = 0;
536 processedPrimitives.clear();
537 for (EnvironmentalPrimitiveBasePtr& x : modifiedPrimitives)
538 {
539 if (!primitivesToRemove.count(x->getId()) && !processedPrimitives.count(x->getId()))
540 {
541 modified++;
542 environmentalPrimitiveSegment->updateEntity(x->getId(), x);
543 processedPrimitives.insert(x->getId());
544 }
545 }
546
547 float unmodified = primitives.size() - modified - primitivesToRemove.size();
548
549 ARMARX_INFO << "Writing segmentation (" << uniqueEntities.size() << " primitives) to memory";
550
551 environmentalPrimitiveSegment->addEntityList(uniqueEntities);
552
553 StringVariantBaseMap debugValues;
554 debugValues["processingTime"] =
555 new Variant((TimeUtil::GetTime().toMicroSeconds() - timestamp) / (1000.0 * 1000.0));
556 debugValues["oldPrimitives"] = new Variant((float)primitives.size());
557 debugValues["visiblePrimitives"] = new Variant((float)visiblePrimitives.size());
558 debugValues["newPrimitives"] = new Variant((float)uniqueEntities.size());
559 debugValues["modifiedPrimitives"] = new Variant(modified);
560 debugValues["unmodifiedPrimitives"] = new Variant(unmodified);
561 debugValues["removedPrimitives"] = new Variant((float)primitivesToRemove.size());
562 numViews++;
563 debugValues["numViews"] = new Variant((int)numViews);
564 debugObserver->setDebugChannel(getName(), debugValues);
565
566 pcl::PointCloud<pcl::PointXYZL>::Ptr inliers(new pcl::PointCloud<pcl::PointXYZL>());
567 for (const EnvironmentalPrimitiveBasePtr& x : primitives)
568 {
569 if (primitivesToRemove.count(x->getId()))
570 {
571 continue;
572 }
573
574 for (const armarx::Vector3BasePtr& v : x->getInliers())
575 {
576 pcl::PointXYZL p;
577 p.label = x->getLabel();
578 p.x = v->x;
579 p.y = v->y;
580 p.z = v->z;
581 inliers->points.push_back(p);
582 }
583 }
584
585 for (const memoryx::EntityBasePtr& e : uniqueEntities)
586 {
587 EnvironmentalPrimitivePtr x = EnvironmentalPrimitivePtr::dynamicCast(e);
588
589 for (const armarx::Vector3BasePtr& v : x->getInliers())
590 {
591 pcl::PointXYZL p;
592 p.label = x->getLabel();
593 p.x = v->x;
594 p.y = v->y;
595 p.z = v->z;
596 inliers->points.push_back(p);
597 }
598 }
599
600
601 inliers->height = 1;
602 inliers->width = inliers->points.size();
603
604
605 provideResultPointClouds(inliers, "Inliers");
606
607
608 //primitiveExtractorPrx->reportNewPrimitiveSet(newPrimitives);
609}
610
611void
612PrimitiveExtractor::pushPrimitivesToMemory(IceUtil::Int64 originalTimestamp)
613{
614 std::vector<memoryx::EntityBasePtr> newPrimitives;
615
616 const std::vector<pcl::ModelCoefficients::Ptr>& planeCoefficients =
617 primitiveExtractor->getPlaneCoefficients();
618 const std::vector<pcl::ModelCoefficients::Ptr>& cylinderCoefficients =
619 primitiveExtractor->getCylinderCoefficients();
620 const std::vector<pcl::ModelCoefficients::Ptr>& sphereCoefficients =
621 primitiveExtractor->getSphereCoefficients();
622 const std::vector<float>& circularityProbabilities =
623 primitiveExtractor->getCircularityProbabilities();
624
625 const std::vector<pcl::PointCloud<pcl::PointXYZL>::Ptr>& planeInliers =
626 primitiveExtractor->getPlaneInliers();
627 const std::vector<pcl::PointCloud<pcl::PointXYZL>::Ptr>& cylinderInliers =
628 primitiveExtractor->getCylinderInliers();
629 const std::vector<pcl::PointCloud<pcl::PointXYZL>::Ptr>& sphereInliers =
630 primitiveExtractor->getSphereInliers();
631
632 const std::vector<pcl::PointCloud<pcl::PointXYZL>::Ptr>& cylinderGraspPoints =
633 primitiveExtractor->getCylinderGraspPoints();
634 const std::vector<pcl::PointCloud<pcl::PointXYZL>::Ptr>& sphereGraspPoints =
635 primitiveExtractor->getSphereGraspPoints();
636
637 int label = 0;
638 // for each detected plane primitive
639 //#pragma omp parallel for
640 for (size_t i = 0; i < planeCoefficients.size(); i++)
641 {
642 // coefficients
643 pcl::ModelCoefficients::Ptr coefficients = planeCoefficients[i];
644 pcl::PointCloud<pcl::PointXYZL>::Ptr inliers = planeInliers[i];
645 // pcl::PointCloud<pcl::PointXYZL>::Ptr graspPoints = planeGraspPoints[i];
646 pcl::PointCloud<pcl::PointXYZL>::Ptr graspPoints(new pcl::PointCloud<pcl::PointXYZL>());
647
648 pcl::PointCloud<pcl::PointXYZL>::Ptr temp(new pcl::PointCloud<pcl::PointXYZL>());
649 pcl::ProjectInliers<PointL> proj;
650 proj.setModelType(pcl::SACMODEL_PLANE);
651 proj.setInputCloud(inliers);
652 proj.setModelCoefficients(coefficients);
653 proj.filter(*temp);
654
655 ARMARX_LOG << __LINE__ << "computing hull";
656
657 pcl::ConcaveHull<PointL> concaveHull;
658 concaveHull.setInputCloud(temp);
659 concaveHull.setDimension(2);
660 concaveHull.setAlpha(69.0);
661 // concaveHull.setKeepInformation(true);
662 concaveHull.reconstruct(*graspPoints);
663
664
665 ARMARX_LOG << __LINE__ << " done computing hull";
666
667 // bounding boxes
668 pcl::MomentOfInertiaEstimation<pcl::PointXYZL> bb;
669 pcl::PointXYZL min_point_OBB, max_point_OBB, position_OBB;
670 Eigen::Matrix3f rotational_matrix_OBB;
671
672 bb.setInputCloud(graspPoints);
673 bb.compute();
674 bb.getOBB(min_point_OBB, max_point_OBB, position_OBB, rotational_matrix_OBB);
675
676 Eigen::Vector3f position(position_OBB.x, position_OBB.y, position_OBB.z);
677 Vector3Ptr dimension =
678 new Vector3(max_point_OBB.x * 2, max_point_OBB.y * 2, max_point_OBB.z * 2);
680 new FramedPose(rotational_matrix_OBB, position, armarx::GlobalFrame, "");
681
682 // computing the length
683 pcl::PointXYZL pointMin, pointMax;
684 pcl::getMinMax3D<pcl::PointXYZL>(*inliers, pointMin, pointMax);
685 float length = (pointMax.getVector3fMap() - pointMin.getVector3fMap()).norm();
686
687 // setting primitives
688 EnvironmentalPrimitiveBasePtr primitive;
689 primitive = new PlanePrimitive(coefficients->values);
690 primitive->setOBBDimensions(dimension);
691 primitive->setPose(pose);
692 primitive->setProbability(0.75); // dummy value
693 primitive->setLabel(0);
694 primitive->setTime(new TimestampVariant(originalTimestamp));
695 primitive->setLength(length);
696 primitive->setCircularityProbability(circularityProbabilities[i]);
697 primitive->setSampling(new armarx::MatrixFloat(4, 0));
698 primitive->setName("environmentalPrimitive_" + boost::lexical_cast<std::string>(label++));
699
700 // convert pcl grasp points into PointList
701 PointList graspPointList(graspPoints->points.size());
702 for (size_t p = 0; p < graspPoints->points.size(); p++)
703 {
704 Vector3Ptr currPoint = new Vector3(
705 graspPoints->points[p].x, graspPoints->points[p].y, graspPoints->points[p].z);
706 graspPointList[p] = currPoint;
707 }
708 primitive->setGraspPoints(graspPointList);
709
710 PointList inlierList(inliers->points.size());
711 for (size_t p = 0; p < inliers->points.size(); p++)
712 {
713 const Eigen::Vector3f& f = inliers->points[p].getVector3fMap();
714 inlierList[p] = new Vector3(f);
715 }
716 primitive->setInliers(inlierList);
717
718
719 //#pragma omp critical
720
721 // Generate sampling
722 std::vector<Eigen::Vector3f> convexHull(graspPointList.size());
723 for (unsigned int i = 0; i < graspPointList.size(); i++)
724 {
725 convexHull[i] = armarx::Vector3Ptr::dynamicCast(graspPointList[i])->toEigen();
726 }
727 AffordanceKit::PlanePtr plane(new AffordanceKit::Plane(convexHull));
728 plane->sample(spatialSamplingDistance, numOrientationalSamplings);
729 armarx::MatrixFloatBasePtr s(new armarx::MatrixFloat(plane->getSampling()));
730 primitive->setSampling(s);
731
732 //#pragma omp critical
733 {
734 newPrimitives.push_back(primitive);
735 }
736 }
737
738 // for each detected cylinder primitive
739 //#pragma omp parallel for
740 for (size_t i = 0; i < cylinderCoefficients.size(); i++)
741 {
742 // coefficients
743 pcl::ModelCoefficients::Ptr coefficients = cylinderCoefficients[i];
744 pcl::PointCloud<pcl::PointXYZL>::Ptr inliers = cylinderInliers[i];
745 pcl::PointCloud<pcl::PointXYZL>::Ptr graspPoints = cylinderGraspPoints[i];
746
747 pcl::PointCloud<pcl::PointXYZL>::Ptr temp(new pcl::PointCloud<pcl::PointXYZL>());
748 pcl::ProjectInliers<PointL> proj;
749 proj.setModelType(pcl::SACMODEL_PLANE);
750 proj.setInputCloud(inliers);
751 proj.setModelCoefficients(coefficients);
752 proj.filter(*temp);
753
754 pcl::ConcaveHull<PointL> concaveHull;
755 concaveHull.setInputCloud(temp);
756 concaveHull.setDimension(2);
757 concaveHull.setAlpha(69.0);
758 concaveHull.setKeepInformation(true);
759 concaveHull.reconstruct(*graspPoints);
760
761 // bounding boxes
762 pcl::MomentOfInertiaEstimation<pcl::PointXYZL> bb;
763 pcl::PointXYZL min_point_OBB, max_point_OBB, position_OBB;
764 Eigen::Matrix3f rotational_matrix_OBB;
765
766 bb.setInputCloud(inliers);
767 bb.compute();
768 bb.getOBB(min_point_OBB, max_point_OBB, position_OBB, rotational_matrix_OBB);
769
770 Eigen::Vector3f position(position_OBB.x, position_OBB.y, position_OBB.z);
771 Vector3Ptr dimension =
772 new Vector3(max_point_OBB.x * 2, max_point_OBB.y * 2, max_point_OBB.z * 2);
774 new FramedPose(rotational_matrix_OBB, position, armarx::GlobalFrame, "");
775
776 // Projected inliers to determine cylinder base point and length
777 // (These are not properly encoded in the RANSAC coefficients)
778 float min_t = std::numeric_limits<float>::infinity();
779 float max_t = -std::numeric_limits<float>::infinity();
780
781 Eigen::Vector3f b(
782 coefficients->values[0], coefficients->values[1], coefficients->values[2]);
783 Eigen::Vector3f d(
784 coefficients->values[3], coefficients->values[4], coefficients->values[5]);
785 d.normalize();
786
787 for (unsigned int i = 0; i < inliers->points.size(); i++)
788 {
789 Eigen::Vector3f v(inliers->points[i].x, inliers->points[i].y, inliers->points[i].z);
790
791 float t = (v - b).dot(d) / d.dot(d);
792
793 min_t = std::min(min_t, t);
794 max_t = std::max(max_t, t);
795 }
796
797 float length = max_t - min_t;
798 Eigen::Vector3f base = b + min_t * d + 0.5 * length * d;
799 Eigen::Vector3f direction = d;
800 float radius = coefficients->values[6];
801
802 // Create MemoryX cylinder primitive instance
803 CylinderPrimitiveBasePtr primitive;
804 primitive = new CylinderPrimitive;
805 primitive->setCylinderPoint(new Vector3(base));
806 primitive->setCylinderAxisDirection(new Vector3(direction));
807 primitive->setCylinderRadius(radius);
808 primitive->setLength(length);
809 primitive->setOBBDimensions(dimension);
810 primitive->setPose(pose);
811 primitive->setProbability(0.75); // dummy value
812 primitive->setLabel(0);
813 primitive->setTime(new TimestampVariant(originalTimestamp));
814 primitive->setCircularityProbability(0.0); // it is always zero for non-planar surfaces
815 primitive->setSampling(new armarx::MatrixFloat(4, 0));
816
817 // convert pcl grasp points into PointList
818 PointList graspPointList(graspPoints->points.size());
819
820 for (size_t p = 0; p < graspPoints->points.size(); p++)
821 {
822 Vector3Ptr currPoint = new Vector3(
823 graspPoints->points[p].x, graspPoints->points[p].y, graspPoints->points[p].z);
824 graspPointList[p] = currPoint;
825 }
826
827 primitive->setName("environmentalPrimitive_" +
828 boost::lexical_cast<std::string>(planeCoefficients.size() + i));
829 primitive->setGraspPoints(graspPointList);
830
831 // Generate sampling
832 AffordanceKit::CylinderPtr cylinder(
833 new AffordanceKit::Cylinder(base, direction, length, radius));
834 cylinder->sample(spatialSamplingDistance, numOrientationalSamplings);
835 armarx::MatrixFloatBasePtr s(new armarx::MatrixFloat(cylinder->getSampling()));
836 primitive->setSampling(s);
837
838 //#pragma omp critical
839 {
840 newPrimitives.push_back(primitive);
841 }
842 }
843
844 // for each detected sphere primitive
845 //#pragma omp parallel for
846 for (size_t i = 0; i < sphereCoefficients.size(); i++)
847 {
848 // coefficients
849 pcl::ModelCoefficients::Ptr coefficients = sphereCoefficients[i];
850 pcl::PointCloud<pcl::PointXYZL>::Ptr inliers = sphereInliers[i];
851 pcl::PointCloud<pcl::PointXYZL>::Ptr graspPoints = sphereGraspPoints[i];
852
853 // apply transformation to each inlier and grasp point
854 //pcl::transformPointCloud(*inliers, *inliers, transform);
855 //pcl::transformPointCloud(*graspPoints, *graspPoints, transform);
856
857 // bounding boxes
858 pcl::MomentOfInertiaEstimation<pcl::PointXYZL> bb;
859 pcl::PointXYZL min_point_OBB, max_point_OBB, position_OBB;
860 Eigen::Matrix3f rotational_matrix_OBB;
861
862 bb.setInputCloud(inliers);
863 bb.compute();
864 bb.getOBB(min_point_OBB, max_point_OBB, position_OBB, rotational_matrix_OBB);
865
866 Eigen::Vector3f position(position_OBB.x, position_OBB.y, position_OBB.z);
867 Vector3Ptr dimension =
868 new Vector3(max_point_OBB.x * 2, max_point_OBB.y * 2, max_point_OBB.z * 2);
870 new FramedPose(rotational_matrix_OBB, position, armarx::GlobalFrame, "");
871
872
873 //computing the length
874 pcl::PointXYZL pointMin, pointMax;
875 pcl::getMinMax3D<pcl::PointXYZL>(*inliers, pointMin, pointMax);
876 float length = (pointMax.getVector3fMap() - pointMin.getVector3fMap()).norm();
877
878 // setting primitives
879 EnvironmentalPrimitiveBasePtr primitive = {};
880 primitive = new SpherePrimitive(coefficients->values);
881 primitive->setOBBDimensions(dimension);
882 primitive->setPose(pose);
883 primitive->setLabel(0);
884 primitive->setProbability(0.75); // dummy value
885 primitive->setTime(new TimestampVariant(originalTimestamp));
886 primitive->setLength(length);
887 primitive->setCircularityProbability(0.0); // it is always zero for non-planar surfaces
888 primitive->setSampling(new armarx::MatrixFloat(4, 0));
889
890 // convert pcl grasp points into PointList
891 PointList graspPointList(graspPoints->points.size());
892
893 for (size_t p = 0; p < graspPoints->points.size(); p++)
894 {
895 Vector3Ptr currPoint = new Vector3(
896 graspPoints->points[p].x, graspPoints->points[p].y, graspPoints->points[p].z);
897 graspPointList[p] = currPoint;
898 }
899
900 primitive->setName("environmentalPrimitive_" +
901 boost::lexical_cast<std::string>(cylinderCoefficients.size() +
902 planeCoefficients.size() + i));
903 primitive->setGraspPoints(graspPointList);
904
905 // Generate sampling
906 Eigen::Vector3f center(
907 coefficients->values[0], coefficients->values[1], coefficients->values[2]);
908 float radius = coefficients->values[3];
909 AffordanceKit::SpherePtr sphere(new AffordanceKit::Sphere(center, radius));
910 sphere->sample(spatialSamplingDistance, numOrientationalSamplings);
911 armarx::MatrixFloatBasePtr s(new armarx::MatrixFloat(sphere->getSampling()));
912 primitive->setSampling(s);
913
914 //#pragma omp critical
915 {
916 newPrimitives.push_back(primitive);
917 }
918 }
919
920 ARMARX_LOG << __LINE__ << " writing primitives to memory.";
921
922 if (enablePrimitiveFusion)
923 {
924 fusePrimitives(newPrimitives, originalTimestamp);
925 }
926 else
927 {
928 ARMARX_LOG << __LINE__ << " removing previous primitives from memory.";
929 environmentalPrimitiveSegment->removeAllEntities();
930 ARMARX_LOG << __LINE__ << " adding " << VAROUT(newPrimitives.size());
931 environmentalPrimitiveSegment->addEntityList(newPrimitives);
932 }
933
934
935 ARMARX_LOG << __LINE__ << "done writing primitives to memory.";
936
937 if (getProperty<bool>("EnableBoxPrimitives").getValue())
938 {
939 newPrimitives.clear();
940
941 std::vector<EnvironmentalPrimitiveBasePtr> primitives =
942 environmentalPrimitiveSegment->getEnvironmentalPrimitives();
943
944
945 unsigned int sz = newPrimitives.size();
946 fusionStrategy.findBoxPrimitives(primitives, newPrimitives, environmentalPrimitiveSegment);
947
948 // Generate sampling
949 for (auto& primitive : newPrimitives)
950 {
951 memoryx::BoxPrimitivePtr box = memoryx::BoxPrimitivePtr::dynamicCast(primitive);
952 if (!box)
953 {
954 continue;
955 }
956
957 Eigen::Matrix4f pose = PosePtr::dynamicCast(box->getPose())->toEigen();
958 Eigen::Vector3f dimensions =
959 Vector3Ptr::dynamicCast(box->getOBBDimensions())->toEigen();
960 AffordanceKit::BoxPtr b(new AffordanceKit::Box(pose, dimensions));
961 b->sample(getProperty<float>("SpatialSamplingDistance").getValue(),
962 getProperty<int>("NumOrientationalSamplings").getValue());
963 armarx::MatrixFloatBasePtr s(new armarx::MatrixFloat(b->getSampling()));
964 box->setSampling(s);
965 }
966
967 ARMARX_INFO << "Found " << (newPrimitives.size() - sz) << " Box primitives";
968
969 environmentalPrimitiveSegment->addEntityList(newPrimitives);
970 }
971}
972
973void
974PrimitiveExtractor::pushPrimitivesToMemoryAffordanceKit(IceUtil::Int64 originalTimestamp)
975{
976 AffordanceKit::PrimitiveSetPtr ps = primitiveExtractor->getPrimitiveSet(
977 getProperty<float>("SpatialSamplingDistance").getValue(),
978 getProperty<int>("NumOrientationalSamplings").getValue(),
979 originalTimestamp);
980 AffordanceKitArmarX::PrimitiveSetArmarX primitiveSet(ps);
981 primitiveSet.writeToMemory(environmentalPrimitiveSegment);
982}
983
989
990bool
992{
993 std::unique_lock lock(enableMutex);
994 return mappingEnabled;
995}
996
997armarx::TimestampBasePtr
999{
1000 std::unique_lock lock(timestampMutex);
1001 return new TimestampVariant(lastProcessedTimestamp);
1002}
1003
1004void
1005armarx::PrimitiveExtractor::setParameters(const visionx::PrimitiveExtractorParameters& parameters,
1006 const Ice::Current& c)
1007{
1008 ARMARX_INFO << "Updating parameter setup";
1009 primitiveExtractor->updateParameters(verbose,
1010 parameters.minSegmentSize,
1011 parameters.maxSegmentSize,
1012 parameters.planeMaxIterations,
1013 parameters.planeDistanceThreshold,
1014 parameters.planeNormalDistance,
1015 parameters.cylinderMaxIterations,
1016 parameters.cylinderDistanceThreshold,
1017 parameters.cylinderRadiusLimit,
1018 parameters.sphereMaxIterations,
1019 parameters.sphereDistanceThreshold,
1020 parameters.sphereNormalDistance,
1021 parameters.euclideanClusteringTolerance,
1022 parameters.outlierThreshold,
1023 parameters.circularDistanceThreshold);
1024}
1025
1026void
1027PrimitiveExtractor::loadParametersFromFile(const std::string& filename, const Ice::Current& c)
1028{
1029 std::string absolute_filename;
1030 ArmarXDataPath::getAbsolutePath(filename, absolute_filename);
1031
1032 ARMARX_INFO << "Loading parameter setup from " << absolute_filename;
1033 QSettings config(QString::fromStdString(absolute_filename), QSettings::IniFormat);
1034
1035 config.beginGroup("PrimitiveExtraction");
1036
1037 primitiveExtractor->updateParameters(
1038 verbose,
1039 config.value("MinPrimitiveSize", getProperty<double>("minimumSegmentSize").getValue())
1040 .toDouble(),
1041 config.value("MaxPrimitiveSize", getProperty<double>("maximumSegmentSize").getValue())
1042 .toDouble(),
1043 config.value("PlaneMaxIterations", getProperty<double>("planeMaximumIteration").getValue())
1044 .toDouble(),
1045 config
1046 .value("PlaneDistanceThreshold",
1047 getProperty<float>("planeDistanceThreshold").getValue())
1048 .toFloat(),
1049 config.value("PlaneNormalDistance", getProperty<float>("planeNormalDistance").getValue())
1050 .toFloat(),
1051 config
1052 .value("CylinderMaxIterations",
1053 getProperty<double>("cylinderMaximumIteration").getValue())
1054 .toDouble(),
1055 config
1056 .value("CylinderDistanceThreshold",
1057 getProperty<float>("cylinderDistanceThreshold").getValue())
1058 .toFloat(),
1059 config.value("CylinderRadiusLimit", getProperty<float>("cylinderRadiousLimit").getValue())
1060 .toFloat(),
1061 config
1062 .value("SphereMaxIterations", getProperty<double>("sphereMaximumIteration").getValue())
1063 .toDouble(),
1064 config
1065 .value("SphereDistanceThreshold",
1066 getProperty<float>("sphereDistanceThreshold").getValue())
1067 .toFloat(),
1068 config.value("SphereNormalDistance", getProperty<float>("sphereNormalDistance").getValue())
1069 .toFloat(),
1070 config
1071 .value("EuclideanClusteringTolerance",
1072 getProperty<float>("euclideanClusteringTolerance").getValue())
1073 .toFloat(),
1074 config.value("OutlierDistanceThreshold", getProperty<float>("outlierThreshold").getValue())
1075 .toFloat(),
1076 config
1077 .value("PlaneCircularDistanceThreshold",
1078 getProperty<float>("circularDistanceThreshold").getValue())
1079 .toFloat());
1080
1081 config.endGroup();
1082}
1083
1084visionx::PrimitiveExtractorParameters
1086{
1087 visionx::PrimitiveExtractorParameters parameters;
1088 parameters.minSegmentSize = primitiveExtractor->minSegmentSize;
1089 parameters.maxSegmentSize = primitiveExtractor->maxSegmentSize;
1090 parameters.planeMaxIterations = primitiveExtractor->planeMaxIteration;
1091 parameters.planeDistanceThreshold = primitiveExtractor->planeMaxIteration;
1092 parameters.planeNormalDistance = primitiveExtractor->planeNormalDistanceWeight;
1093 parameters.cylinderMaxIterations = primitiveExtractor->cylinderMaxIteration;
1094 parameters.cylinderDistanceThreshold = primitiveExtractor->cylinderDistanceThreshold;
1095 parameters.cylinderRadiusLimit = primitiveExtractor->cylinderRadiusLimit;
1096
1097 parameters.sphereMaxIterations = primitiveExtractor->sphereMaxIteration;
1098 parameters.sphereDistanceThreshold = primitiveExtractor->sphereDistanceThreshold;
1099 parameters.sphereNormalDistance = primitiveExtractor->sphereNormalDistanceWeight;
1100 parameters.euclideanClusteringTolerance = primitiveExtractor->euclideanClusteringTolerance;
1101 parameters.outlierThreshold = primitiveExtractor->outlierThreshold;
1102 parameters.circularDistanceThreshold = primitiveExtractor->circleDistanceThreshold;
1103
1104 return parameters;
1105}
std::string timestamp()
#define VAROUT(x)
constexpr T c
static bool getAbsolutePath(const std::string &relativeFilename, std::string &storeAbsoluteFilename, const std::vector< std::string > &additionalSearchPaths={}, bool verbose=true)
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
Property< PropertyType > getProperty(const std::string &name)
void offeringTopic(const std::string &name)
Registers a topic for retrival after initialization.
TopicProxyType getTopic(const std::string &name)
Returns a proxy of the specified topic.
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
std::string getName() const
Retrieve name of object.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
visionx::PrimitiveExtractorParameters getParameters(const Ice::Current &c=Ice::emptyCurrent) override
void setParameters(const visionx::PrimitiveExtractorParameters &parameters, const Ice::Current &c=Ice::emptyCurrent) override
void enablePipelineStep(const Ice::Current &c=Ice::emptyCurrent) override
void onConnectPointCloudProcessor() override
void disablePipelineStep(const Ice::Current &c=Ice::emptyCurrent) override
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void onDisconnectPointCloudProcessor() override
void loadParametersFromFile(const std::string &filename, const Ice::Current &c=Ice::emptyCurrent) override
void onInitPointCloudProcessor() override
armarx::TimestampBasePtr getLastProcessedTimestamp(const Ice::Current &c=Ice::emptyCurrent) override
bool isPipelineStepEnabled(const Ice::Current &c=Ice::emptyCurrent) override
Property< PropertyType > getProperty(const std::string &name)
Property creation and retrieval.
static IceUtil::Time GetTime(TimeMode timeMode=TimeMode::VirtualTime)
Get the current time.
Definition TimeUtil.cpp:42
Implements a Variant type for timestamps.
The Variant class is described here: Variants.
Definition Variant.h:224
void enableResultPointClouds(std::string resultProviderName="")
Enables visualization.
int getPointClouds(const PointCloudPtrT &pointCloudPtr)
Poll PointClouds from provider.
void usingPointCloudProvider(std::string providerName)
Registers a delayed topic subscription and a delayed provider proxy retrieval which will be available...
bool waitForPointClouds(int milliseconds=1000)
Wait for new PointClouds.
void provideResultPointClouds(const PointCloudPtrT &pointClouds, std::string providerName="")
sends result PointClouds for visualization
#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_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#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::string const GlobalFrame
Variable of the global coordinate system.
Definition FramedPose.h:65
ConvexHull< Point >::type convexHull(const std::vector< Point > &points)
const VariantTypeId FramedPose
Definition FramedPose.h:36
T getValue(nlohmann::json &userConfig, nlohmann::json &defaultConfig, const std::string &entryName)
Definition utils.h:80
double s(double t, double s0, double v0, double a0, double j)
Definition CtrlUtil.h:33
double v(double t, double v0, double a0, double j)
Definition CtrlUtil.h:39
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceInternal::Handle< Vector3 > Vector3Ptr
Definition Pose.h:165
std::map< std::string, VariantBasePtr > StringVariantBaseMap
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
IceInternal::Handle< FramedPose > FramedPosePtr
Definition FramedPose.h:272
Eigen::Vector3d Vector3
Definition kbm.h:43
const armarx::VariantTypeId PlanePrimitive
const armarx::VariantTypeId SpherePrimitive
const armarx::VariantTypeId CylinderPrimitive
VirtualRobot headers.
IceInternal::Handle< EnvironmentalPrimitive > EnvironmentalPrimitivePtr
IceInternal::Handle< BoxPrimitive > BoxPrimitivePtr
double dot(const Point &x, const Point &y)
Definition point.hpp:57