PointCloudSegmenter.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 "PointCloudSegmenter.h"
26
28
29#include <QSettings>
30
31#include <pcl/common/colors.h>
32#include <pcl/filters/crop_box.h>
33
36
38
40
41using namespace armarx;
42
43namespace visionx
44{
46 inputCloudPtr(new pcl::PointCloud<PointO>),
47 labeledColoredCloudPtr(new pcl::PointCloud<PointRGBL>)
48 {
49 }
50
51 void
53 {
54 lastProcessedTimestamp = 0;
55
56 // Get point cloud provider name
57 providerName = getProperty<std::string>("ProviderName").getValue();
58 ARMARX_INFO << "Using point cloud provider " << providerName << flush;
59
60
61 // Get segmentation method
62 segmentationMethod = getProperty<std::string>("segmentationMethod").getValue();
63 ARMARX_INFO << "Using the segmentation method " << segmentationMethod << flush;
64
65 offeringTopic(getProperty<std::string>("DebugDrawerTopicName").getValue());
66
67 enabled = getProperty<bool>("StartEnabled").getValue();
68 ARMARX_INFO << "Starting " << (enabled ? "Enabled" : "Disabled");
69
70 // get LCCP segmentation parameters
71 minSegSize = getProperty<int>("lccpMinimumSegmentSize").getValue();
72 voxelRes = getProperty<float>("lccpVoxelResolution").getValue();
73 seedRes = getProperty<float>("lccpSeedResolution").getValue();
74 colorImp = getProperty<float>("lccpColorImportance").getValue();
75 spatialImp = getProperty<float>("lccpSpatialImportance").getValue();
76 normalImp = getProperty<float>("lccpNormalImportance").getValue();
77 concavityThres = getProperty<float>("lccpConcavityThreshold").getValue();
78 smoothnessThes = getProperty<float>("lccpSmoothnessThreshold").getValue();
79
80 // get RG segmentation parameters
81 rgSmoothnessThes = getProperty<float>("rgSmoothnessThreshold").getValue();
82 rgCurvatureThres = getProperty<float>("rgCurvatureThreshold").getValue();
83
84 // init segmenters and update parameters
85 if (segmentationMethod == "LCCP")
86 {
87 pLCCP = new LCCPSegClass();
88 pLCCP->UpdateParameters(voxelRes,
89 seedRes,
90 colorImp,
91 spatialImp,
92 normalImp,
93 concavityThres,
94 smoothnessThes,
95 minSegSize);
96 }
97 else if (segmentationMethod == "RG")
98 {
99 pRGSegmenter = new RGSegClass();
100 pRGSegmenter->UpdateRegionGrowingSegmentationParameters(rgSmoothnessThes,
101 rgCurvatureThres);
102 }
103 else if (segmentationMethod == "EC")
104 {
105 pECSegmenter = new EuclideanBasedClustering();
106 }
107 else if (segmentationMethod == "DS")
108 {
109 usingProxy(getProperty<std::string>("DeepSegmenterTopicName").getValue());
110 pDeepSegmenter = new DeepSegClass();
111 }
112 }
113
114 void
116 {
118 getProperty<std::string>("DebugDrawerTopicName").getValue());
119 if (!debugDrawerTopic)
120 {
121 ARMARX_WARNING << "Failed to obtain debug drawer proxy";
122 }
123
124 if (segmentationMethod == "DS")
125 {
127 getProperty<std::string>("DeepSegmenterTopicName").getValue());
128 if (!deepSegmenterTopic)
129 {
130 ARMARX_WARNING << "Failed to obtain deep segmenter proxy";
131 }
132 }
133
135
138 }
139
140 void
144
145 void
147 {
148 if (pLCCP)
149 {
150 delete pLCCP;
151 }
152
153 if (pRGSegmenter)
154 {
155 delete pRGSegmenter;
156 }
157
158 if (pECSegmenter)
159 {
160 delete pECSegmenter;
161 }
162
163 if (pDeepSegmenter)
164 {
165 delete pDeepSegmenter;
166 }
167 }
168
169 void
171 {
172 {
173 std::unique_lock lock(enableMutex);
174
175 if (!enabled)
176 {
177 return;
178 }
179 }
180
181 if (!waitForPointClouds(providerName, 10000))
182 {
183 ARMARX_WARNING << "Timeout or error while waiting for point cloud data"
184 << armarx::flush;
185 return;
186 }
187
188
189 //if (getPointCloudFormat(providerName)->type == eColoredLabeledPoints)
190 //{
191 // ARMARX_INFO << "Received RGBL point cloud: Passing on already segmented point cloud";
192 //
193 // labeledColoredCloudPtr.reset(new pcl::PointCloud<pcl::PointXYZRGBL>());
194 // getPointClouds(providerName, labeledColoredCloudPtr);
195 // provideResultPointClouds(labeledColoredCloudPtr);
196 //
197 // {
198 // std::unique_lock lock(timestampMutex);
199 // lastProcessedTimestamp = getPointCloudFormat(providerName)->timeProvided;
200 // }
201 //
202 // return;
203 //}
204 //else
205 //{
206 getPointClouds(providerName, inputCloudPtr);
207 ARMARX_INFO << "Point cloud received (size: " << inputCloudPtr->points.size()
208 << ", providerName: " << providerName << ")";
209 //}
210
211 MetaPointCloudFormatPtr info = getPointCloudFormat(providerName);
212 IceUtil::Int64 originalTimestamp = info->timeProvided;
213
214 IceUtil::Time ts = IceUtil::Time::microSeconds(originalTimestamp);
215 std::string timestampString = ts.toDateTime().substr(ts.toDateTime().find(' ') + 1);
216
217 IceUtil::Time start_ts = IceUtil::Time::now();
218
219 ARMARX_INFO << "Point cloud received (timestamp: " << timestampString
220 << ", size: " << inputCloudPtr->points.size()
221 << ", providerName: " << providerName << ")";
222 if (inputCloudPtr->points.size() == 0)
223 {
224 return;
225 }
226
227 if (segmentationMethod == "LCCP")
228 {
229 pcl::PointCloud<pcl::PointXYZL>::Ptr labeledPCPtr =
230 pLCCP->GetLabeledPointCloud(inputCloudPtr);
231 labeledColoredCloudPtr.reset(
232 new pcl::PointCloud<PointRGBL>()); // returns labeled cloud in XYZL format
233
234 if (inputCloudPtr->size() != labeledPCPtr->size())
235 {
236 ARMARX_WARNING << "Point cloud size mismatch (RGB: " << inputCloudPtr->size()
237 << ", XYZL: " << labeledPCPtr->size() << "), skipping current frame";
238 return;
239 }
240
241
242 pcl::concatenateFields(*inputCloudPtr, *labeledPCPtr, *labeledColoredCloudPtr);
243 ARMARX_INFO << " computed supervoxelcluster size: "
244 << pLCCP->GetSuperVoxelClusterSize();
245 ARMARX_INFO << " computed labeledCloudPtr size: "
246 << labeledColoredCloudPtr->points.size();
247 }
248 else if (segmentationMethod == "RG")
249 {
250 pRGSegmenter->UpdateRegionGrowingSegmentationParameters(rgSmoothnessThes,
251 rgCurvatureThres);
252 auto colorizedLabeledCloudPtr = pRGSegmenter->ApplyRegionGrowingSegmentation(
253 inputCloudPtr); // returns labeled cloud in XYZRGBA format
254 pcl::PointCloud<pcl::PointXYZL>::Ptr labeledPCPtr(
255 new pcl::PointCloud<pcl::PointXYZL>());
256 convertFromXYZRGBAtoXYZL(
257 colorizedLabeledCloudPtr,
258 labeledPCPtr); // convert form XYZRGBA to XYZL for the labeling process
259 labeledColoredCloudPtr.reset(new pcl::PointCloud<PointRGBL>());
260 pcl::concatenateFields(*inputCloudPtr, *labeledPCPtr, *labeledColoredCloudPtr);
261 }
262 else if (segmentationMethod == "EC")
263 {
264 labeledColoredCloudPtr = pECSegmenter->GetLabeledPointCloud(inputCloudPtr);
265 }
266 else if (segmentationMethod == "DS")
267 {
268 if (!deepSegmenterTopic) // check the proxy
269 {
270 ARMARX_WARNING << "Failed to obtain deep segmenter proxy";
271 return;
272 }
273
274 if (!inputCloudPtr->isOrganized()) // the input cloud must be organized
275 {
276 ARMARX_WARNING << "Input point cloud is not organized!";
277 return;
278 }
279
280 // first get rgb image from the point cloud data
281 rgbImage = pDeepSegmenter->GetImageFromPointCloud(inputCloudPtr);
282
283 deepSegmenterTopic->setImageDimensions(inputCloudPtr->width, inputCloudPtr->height);
284 armarx::Blob deepSegmentImage = deepSegmenterTopic->segmentImage(rgbImage);
285
286 labeledColoredCloudPtr =
287 pDeepSegmenter->GetLabeledPointCloud(inputCloudPtr, deepSegmentImage);
288 }
289
290 // Provide point cloud referencing the time stamp of the original source point cloud
291 labeledColoredCloudPtr->header.stamp = originalTimestamp;
292
293 {
294 std::unique_lock lock(enableMutex);
295
296 // Do not provide result if segmenter has been switched off in the meantime
297 if (enabled)
298 {
299 provideResultPointClouds(labeledColoredCloudPtr);
300
301 {
302 std::unique_lock lock(timestampMutex);
303 lastProcessedTimestamp = originalTimestamp;
304 }
305 }
306 }
307
308 ARMARX_INFO << "Point cloud segmentation took "
309 << (IceUtil::Time::now() - start_ts).toSecondsDouble() << " secs";
310
311 if (getProperty<bool>("SingleRun").getValue())
312 {
313 ARMARX_WARNING << "PointCloudSegmenter configured to run only once";
314 enabled = false;
315 }
316 }
317
324
325 void
327 {
328 std::unique_lock lock(enableMutex);
329 enabled = true;
330
331 ARMARX_INFO << "Enabling segmentation";
332 }
333
334 void
336 {
337 std::unique_lock lock(enableMutex);
338 enabled = false;
339
340 ARMARX_INFO << "Disabling segmentation";
341 }
342
343 void
344 PointCloudSegmenter::changePointCloudProvider(const std::string& providerName,
345 const Ice::Current& c)
346 {
347 std::unique_lock lock(providerMutex);
348
349 ARMARX_INFO << "Changing point cloud provider to '" << providerName << "'";
350
352
353 releasePointCloudProvider(this->providerName);
354 this->providerName = providerName;
355 usingPointCloudProvider(this->providerName);
356
358 }
359
360 void
361 PointCloudSegmenter::convertFromXYZRGBAtoXYZL(
362 pcl::PointCloud<pcl::PointXYZRGBA>::Ptr& sourceCloudPtr,
363 pcl::PointCloud<pcl::PointXYZL>::Ptr& targetCloudPtr)
364 {
365 targetCloudPtr->resize(sourceCloudPtr->points.size());
366 uint32_t newLabel = 1;
367
368 struct rgbValues
369 {
370 int rVal;
371 int gVal;
372 int bVal;
373 };
374
375 std::map<uint32_t, rgbValues> colorMap;
376
377 // scan the source cloud
378 for (size_t i = 0; i < sourceCloudPtr->points.size(); i++)
379 {
380 rgbValues currRGB;
381 currRGB.rVal = (int)sourceCloudPtr->points[i].r;
382 currRGB.gVal = (int)sourceCloudPtr->points[i].g;
383 currRGB.bVal = (int)sourceCloudPtr->points[i].b;
384
385 bool isFound = false;
386 uint32_t foundLabel = 0;
387
388 // search in the map
389 std::map<uint32_t, rgbValues>::iterator iter_i;
390
391 for (iter_i = colorMap.begin(); iter_i != colorMap.end(); iter_i++)
392 {
393 if (currRGB.rVal == iter_i->second.rVal && currRGB.gVal == iter_i->second.gVal &&
394 currRGB.bVal == iter_i->second.bVal)
395 {
396 foundLabel = iter_i->first;
397 isFound = true;
398 break;
399 }
400 }
401
402 if (!isFound)
403 {
404 colorMap[newLabel] = currRGB;
405 foundLabel = newLabel;
406 newLabel++;
407 }
408
409 targetCloudPtr->points[i].x = sourceCloudPtr->points[i].x;
410 targetCloudPtr->points[i].y = sourceCloudPtr->points[i].y;
411 targetCloudPtr->points[i].z = sourceCloudPtr->points[i].z;
412 targetCloudPtr->points[i].label = foundLabel;
413 }
414 }
415
416 bool
418 {
419 std::unique_lock lock(enableMutex);
420 return enabled;
421 }
422
423 armarx::TimestampBasePtr
425 {
426 std::unique_lock lock(timestampMutex);
427 return new TimestampVariant(lastProcessedTimestamp);
428 }
429
430 void
431 PointCloudSegmenter::setLccpParameters(const LccpParameters& parameters, const Ice::Current& c)
432 {
433 ARMARX_INFO << "Updating LCCP parameter setup (current segmentation method is "
434 << segmentationMethod << ")";
435 pLCCP->UpdateParameters(parameters.voxelResolution,
436 parameters.seedResolution,
437 parameters.colorImportance,
438 parameters.spatialImportance,
439 parameters.normalImportance,
440 parameters.concavityThreshold,
441 parameters.smoothnessThreshold,
442 parameters.minSegmentSize);
443 }
444
445 void
447 const Ice::Current& c)
448 {
449 std::string absolute_filename;
450 ArmarXDataPath::getAbsolutePath(filename, absolute_filename);
451
452 ARMARX_INFO << "Loading LCCP parameter setup from " << absolute_filename;
453 QSettings config(QString::fromStdString(absolute_filename), QSettings::IniFormat);
454
455 config.beginGroup("LCCP");
456 pLCCP->UpdateParameters(
457 config.value("VoxelResolution", getProperty<float>("lccpVoxelResolution").getValue())
458 .toFloat(),
459 config.value("SeedResolution", getProperty<float>("lccpSeedResolution").getValue())
460 .toFloat(),
461 config.value("ColorImportance", getProperty<float>("lccpColorImportance").getValue())
462 .toFloat(),
463 config
464 .value("SpatialImportance", getProperty<float>("lccpSpatialImportance").getValue())
465 .toFloat(),
466 config.value("NormalImportance", getProperty<float>("lccpNormalImportance").getValue())
467 .toFloat(),
468 config
469 .value("ConcavityThreshold",
470 getProperty<float>("lccpConcavityThreshold").getValue())
471 .toFloat(),
472 config
473 .value("SmoothnessThreshold",
474 getProperty<float>("lccpSmoothnessThreshold").getValue())
475 .toFloat(),
476 config.value("MinSegmentSize", getProperty<int>("lccpMinimumSegmentSize").getValue())
477 .toInt());
478 config.endGroup();
479 }
480
481 LccpParameters
483 {
484 LccpParameters parameters;
485
486 parameters.voxelResolution = pLCCP->voxel_resolution;
487 parameters.seedResolution = pLCCP->seed_resolution;
488 parameters.colorImportance = pLCCP->color_importance;
489 parameters.spatialImportance = pLCCP->spatial_importance;
490 parameters.normalImportance = pLCCP->normal_importance;
491 parameters.concavityThreshold = pLCCP->concavity_tolerance_threshold;
492 parameters.smoothnessThreshold = pLCCP->smoothness_threshold;
493 parameters.minSegmentSize = pLCCP->min_segment_size;
494
495
496 return parameters;
497 }
498
499 void
500 PointCloudSegmenter::setRgParameters(float smoothnessThreshold,
501 float curvatureThreshold,
502 const Ice::Current& c)
503 {
504 ARMARX_INFO << "Updating RG parameter setup";
505 pRGSegmenter->UpdateRegionGrowingSegmentationParameters(smoothnessThreshold,
506 curvatureThreshold);
507 }
508
509 void
511 const Ice::Current& c)
512 {
513 std::string absolute_filename;
514 ArmarXDataPath::getAbsolutePath(filename, absolute_filename);
515
516 ARMARX_INFO << "Loading RG parameter setup from " << absolute_filename;
517 QSettings config(QString::fromStdString(absolute_filename), QSettings::IniFormat);
518
519 config.beginGroup("RG");
520 pRGSegmenter->UpdateRegionGrowingSegmentationParameters(
521 config
522 .value("SmoothnessThreshold",
523 getProperty<double>("rgSmoothnessThreshold").getValue())
524 .toDouble(),
525 config
526 .value("CurvatureThreshold", getProperty<double>("rgCurvatureThreshold").getValue())
527 .toDouble());
528 config.endGroup();
529 }
530
531 void
533 {
534 GridLayout grid;
535 int row = 0;
536 {
537 tab.rgSmoothnessThreshold.setRange(1.0, 20.0);
538 tab.rgSmoothnessThreshold.setSteps(200);
539 tab.rgSmoothnessThreshold.setValue(rgSmoothnessThes);
540 tab.rgSmoothnessThreshold.setDecimals(3);
541 grid.add(Label("rgSmoothnessThreshold:"), Pos{row, 0});
542 grid.add(tab.rgSmoothnessThreshold, Pos{row, 1});
543 row += 1;
544 }
545 {
546 tab.rgCurvatureThreshold.setRange(1.0, 50.0);
547 tab.rgCurvatureThreshold.setSteps(200);
548 tab.rgCurvatureThreshold.setValue(rgCurvatureThres);
549 tab.rgCurvatureThreshold.setDecimals(3);
550 grid.add(Label("rgCurvatureThreshold:"), Pos{row, 0});
551 grid.add(tab.rgCurvatureThreshold, Pos{row, 1});
552 row += 1;
553 }
554
555 RemoteGui_createTab("PointCloudSegmenter", grid, &tab);
556 }
557
558 void
560 {
561 rgSmoothnessThes = tab.rgSmoothnessThreshold.getValue();
562 rgCurvatureThres = tab.rgCurvatureThreshold.getValue();
563 }
564
565
566std::string
568{
569 return "PointCloudSegmenter";
570}
571
572std::string
577
578
580} // namespace visionx
int Label(int n[], int size, int *curLabel, MiscLib::Vector< std::pair< int, size_t > > *labels)
Definition Bitmap.cpp:801
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
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.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
Implements a Variant type for timestamps.
void releasePointCloudProvider(std::string providerName)
Removes topic subscription and provider proxy dependency to release a point cloud provider.
void enableResultPointClouds(std::string resultProviderName="")
Enables visualization.
virtual void onDisconnectComponent() override
int getPointClouds(const PointCloudPtrT &pointCloudPtr)
Poll PointClouds from provider.
MetaPointCloudFormatPtr getPointCloudFormat(std::string providerName)
void usingPointCloudProvider(std::string providerName)
Registers a delayed topic subscription and a delayed provider proxy retrieval which will be available...
virtual void onConnectComponent() override
bool waitForPointClouds(int milliseconds=1000)
Wait for new PointClouds.
void provideResultPointClouds(const PointCloudPtrT &pointClouds, std::string providerName="")
sends result PointClouds for visualization
void loadLccpParametersFromFile(const std::string &filename, const Ice::Current &c=Ice::emptyCurrent) override
LccpParameters getLccpParameters(const Ice::Current &c=Ice::emptyCurrent) override
void enablePipelineStep(const Ice::Current &c=Ice::emptyCurrent) override
void disablePipelineStep(const Ice::Current &c=Ice::emptyCurrent) override
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void changePointCloudProvider(const std::string &providerName, const Ice::Current &c=Ice::emptyCurrent) override
void setRgParameters(float smoothnessThreshold, float curvatureThreshold, const Ice::Current &c=Ice::emptyCurrent) override
void setLccpParameters(const LccpParameters &parameters, const Ice::Current &c=Ice::emptyCurrent) override
void loadRgParametersFromFile(const std::string &filename, const Ice::Current &c=Ice::emptyCurrent) override
armarx::TimestampBasePtr getLastProcessedTimestamp(const Ice::Current &c=Ice::emptyCurrent) override
std::string getDefaultName() const override
Retrieve default name of component.
bool isPipelineStepEnabled(const Ice::Current &c=Ice::emptyCurrent) override
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
const LogSender::manipulator flush
Definition LogSender.h:251
ArmarX headers.
pcl::PointXYZRGBL PointRGBL
pcl::PointXYZRGBA PointO
pcl::PointCloud< Point > PointCloud
void RemoteGui_createTab(std::string const &name, RemoteGui::Client::Widget const &rootWidget, RemoteGui::Client::Tab *tab)
GridLayout & add(Widget const &child, Pos pos, Span span=Span{1, 1})
Definition Widgets.cpp:438