EfficientRANSACPrimitiveExtractor.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::ArmarXObjects::EfficientRANSACPrimitiveExtractor
17 * @author Raphael Grimm ( raphael dot grimm at kit dot edu )
18 * @date 2020
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
24
26
27#include <pcl/common/transforms.h>
28#include <pcl/filters/statistical_outlier_removal.h>
29
30#include <SimoxUtility/meta/type_name.h>
31
33
35
37
38#include <VisionX/interface/components/Calibration.h>
39
40//gui
41namespace armarx
42{
43 RemoteGui::WidgetPtr
45 {
46 const auto& buf = _cfgBuf.getWriteBuffer();
49 .cols(2)
50
51 .addTextLabel("outlierMeanK")
52 .addChild(
53 RemoteGui::makeIntSpinBox("outlierMeanK").value(buf.outlierMeanK).min(1).max(1000))
54
55 .addTextLabel("outlierStddevMulThresh")
56 .addChild(RemoteGui::makeFloatSpinBox("outlierStddevMulThresh")
57 .value(buf.outlierStddevMulThresh)
58 .min(0)
59 .max(10))
60
63
64 .addTextLabel("ransacEpsilon")
66 .value(buf.ransacEpsilon)
67 .min(1)
68 .max(1000))
69
70 .addTextLabel("ransacBitmapEpsilon")
71 .addChild(RemoteGui::makeFloatSpinBox("ransacBitmapEpsilon")
72 .value(buf.ransacBitmapEpsilon)
73 .min(1)
74 .max(10000))
75
76 .addTextLabel("ransacNormalThresh")
77 .addChild(RemoteGui::makeFloatSpinBox("ransacNormalThresh")
78 .value(buf.ransacNormalThresh)
79 .min(0)
80 .max(1)
81 .decimals(3))
82
83 .addTextLabel("ransacProbability")
84 .addChild(RemoteGui::makeFloatSpinBox("ransacProbability")
85 .value(buf.ransacProbability)
86 .min(0)
87 .max(1)
88 .decimals(3))
89
90 .addTextLabel("ransacMinSupport")
91 .addChild(RemoteGui::makeIntSpinBox("ransacMinSupport")
92 .value(buf.ransacMinSupport)
93 .min(1)
94 .max(1000))
95
96 .addTextLabel("ransacMaxruntimeMs")
97 .addChild(RemoteGui::makeIntSpinBox("ransacMaxruntimeMs")
98 .value(buf.ransacMaxruntimeMs)
99 .min(1)
100 .max(10000));
101 }
102
103 void
105 {
107 prx.receiveUpdates();
108
109 auto& buf = _cfgBuf.getWriteBuffer();
110 prx.getValue(buf.outlierMeanK, "outlierMeanK");
111 prx.getValue(buf.outlierStddevMulThresh, "outlierStddevMulThresh");
112
113 prx.getValue(buf.ransacEpsilon, "ransacEpsilon");
114 prx.getValue(buf.ransacNormalThresh, "ransacNormalThresh");
115 prx.getValue(buf.ransacMinSupport, "ransacMinSupport");
116 prx.getValue(buf.ransacBitmapEpsilon, "ransacBitmapEpsilon");
117 prx.getValue(buf.ransacProbability, "ransacProbability");
118 prx.getValue(buf.ransacMaxruntimeMs, "ransacMaxruntimeMs");
119 _cfgBuf.commitWrite();
120 prx.sendUpdates();
121 }
122} // namespace armarx
123
124#define result_name_transformed getName() + "_Transformed"
125#define result_name_filtered getName() + "_Filtered"
126
127namespace armarx
128{
132 {
134 "ProviderReferenceFrame",
135 "",
136 "Name of the point cloud refence frame (empty = autodetect)");
137 }
138
139 std::string
144
145 std::string
147 {
148 return "EfficientRANSACPrimitiveExtractor";
149 }
150
151 void
153 {
154 _pointCloudProviderName = getProperty<std::string>("ProviderName");
155 usingPointCloudProvider(_pointCloudProviderName);
156 }
157
158 void
160 {
161 //provider
162 {
165
166 _pointCloudProviderInfo = getPointCloudProvider(_pointCloudProviderName, true);
167 _pointCloudProvider = _pointCloudProviderInfo.proxy;
168 _pointCloudProviderRefFrame = getProperty<std::string>("ProviderReferenceFrame");
169 if (_pointCloudProviderRefFrame.empty())
170 {
171 _pointCloudProviderRefFrame = "root";
172 auto frameprov =
173 visionx::ReferenceFrameInterfacePrx::checkedCast(_pointCloudProvider);
174 if (frameprov)
175 {
176 _pointCloudProviderRefFrame = frameprov->getReferenceFrame();
177 }
178 }
179 ARMARX_INFO << VAROUT(_pointCloudProviderRefFrame);
180 }
181 //robot
182 {
183 addRobot("_localRobot", VirtualRobot::RobotIO::eStructure);
184 _localRobot = getRobotData("_localRobot");
185 }
186 //gui
188 [this](RemoteGui::TabProxy& prx) { processGui(prx); });
190 }
191
192 void
196
197 void
201
202 void
204 {
205
206#define call_template_function(F) \
207 switch (_pointCloudProviderInfo.pointCloudFormat->type) \
208 { \
209 case visionx::PointContentType::ePoints: \
210 F<pcl::PointXYZ>(); \
211 break; \
212 case visionx::PointContentType::eColoredPoints: \
213 F<pcl::PointXYZRGBA>(); \
214 break; \
215 case visionx::PointContentType::eColoredOrientedPoints: \
216 F<pcl::PointXYZRGBNormal>(); \
217 break; \
218 case visionx::PointContentType::eLabeledPoints: \
219 F<pcl::PointXYZL>(); \
220 break; \
221 case visionx::PointContentType::eColoredLabeledPoints: \
222 F<pcl::PointXYZRGBL>(); \
223 break; \
224 case visionx::PointContentType::eIntensity: \
225 F<pcl::PointXYZI>(); \
226 break; \
227 case visionx::PointContentType::eOrientedPoints: \
228 ARMARX_ERROR << "eOrientedPoints NOT HANDLED IN VISIONX"; \
229 [[fallthrough]]; \
230 default: \
231 ARMARX_ERROR << "Could not process point cloud, because format '" \
232 << _pointCloudProviderInfo.pointCloudFormat->type << "' is unknown"; \
233 } \
234 do \
235 { \
236 } while (false)
238 }
239
246
247} // namespace armarx
248
261
262namespace armarx
263{
264 template <class PointType>
265 void
267 {
268 const auto& buf = _cfgBuf.getUpToDateReadBuffer();
269 using cloud_t = pcl::PointCloud<PointType>;
270 using cloud_ptr_t = typename cloud_t::Ptr;
271 const auto now = [] { return std::chrono::high_resolution_clock::now(); };
272 const auto timeBeg = now();
274 {
275 const auto dt = now() - timeBeg;
276 const int us = std::chrono::duration_cast<std::chrono::microseconds>(dt).count();
277 setDebugObserverDatafield("time_full_process_call_s", us / 1e6f);
279 };
280 //get cloud + transform to global
281 cloud_ptr_t transformedCloud;
282 {
283 if (!waitForPointClouds(_pointCloudProviderName, 100))
284 {
285 ARMARX_INFO << deactivateSpam(10, _pointCloudProviderName)
286 << "Timeout or error while waiting for point cloud data of provider "
287 << _pointCloudProviderName;
288 return;
289 }
290 cloud_ptr_t inputCloud(new cloud_t);
291 getPointClouds(_pointCloudProviderName, inputCloud);
292 setDebugObserverDatafield("size_cloud_in", inputCloud->size());
293 synchronizeLocalClone(_localRobot);
294
295 const Eigen::Matrix4f providerInRootFrame = [&]() -> Eigen::Matrix4f
296 {
297 FramedPose fp{Eigen::Matrix4f::Identity(),
298 _pointCloudProviderRefFrame,
299 _localRobot.robot->getName()};
300 //fp.changeFrame(_localRobot.robot, _localRobot.robot->getRootNode()->getName());
301 fp.changeFrame(_localRobot.robot, "Global");
302 return fp.toEigen();
303 }();
304
305 transformedCloud = cloud_ptr_t(new cloud_t);
306 pcl::transformPointCloud(*inputCloud, *transformedCloud, providerInRootFrame);
307
309 }
310
311 //remove outliers
312 {
313 cloud_ptr_t filtered(new cloud_t);
314 pcl::StatisticalOutlierRemoval<PointType> sor;
315 sor.setInputCloud(transformedCloud);
316 sor.setMeanK(buf.outlierMeanK);
317 sor.setStddevMulThresh(buf.outlierStddevMulThresh);
318 sor.filter(*filtered);
319 setDebugObserverDatafield("size_cloud_filtered", filtered->size());
321 std::swap(filtered, transformedCloud);
322 }
323
324 //ransac stuff
325 {
326 ARMARX_INFO << "creating point vec";
327 std::vector<Point> points;
328 points.reserve(transformedCloud->size());
329 static constexpr float inf = std::numeric_limits<float>::infinity();
330 float minX = inf;
331 float minY = inf;
332 float minZ = inf;
333 float maxX = -inf;
334 float maxY = -inf;
335 float maxZ = -inf;
336 for (const auto& p : *transformedCloud)
337 {
338 points.emplace_back(Vec3f(p.x, p.y, p.z));
339 std::tie(minX, maxX) = std::minmax({minX, maxX, p.x});
340 std::tie(minY, maxY) = std::minmax({minY, maxY, p.y});
341 std::tie(minZ, maxZ) = std::minmax({minZ, maxZ, p.z});
342 }
343 ARMARX_INFO << "creating cloud";
344 PointCloud pc(points.data(), points.size());
345 pc.setBBox({minX, minY, minZ}, {maxX, maxY, maxZ});
346 ARMARX_INFO << "calc cloud normals";
347 pc.calcNormals(100);
348 ARMARX_INFO << VAROUT(pc.getScale());
349
350 ARMARX_INFO << "configure ransac";
351 RansacShapeDetector::Options ransacOptions;
352 {
353 ransacOptions.m_epsilon =
354 buf.ransacEpsilon; // set distance threshold to .01f of bounding box width
355 // NOTE: Internally the distance threshold is taken as 3 * ransacOptions.m_epsilon!!!
356 ransacOptions.m_bitmapEpsilon =
357 buf.ransacBitmapEpsilon; // set bitmap resolution to .02f of bounding box width
358 // NOTE: This threshold is NOT multiplied internally!
359 ransacOptions.m_normalThresh =
360 buf.ransacNormalThresh; // this is the cos of the maximal normal deviation
361 ransacOptions.m_minSupport =
362 buf.ransacMinSupport; // this is the minimal numer of points required for a primitive
363 ransacOptions.m_probability =
364 buf.ransacProbability; // this is the "probability" with which a primitive is overlooked
365 ransacOptions.m_maxruntime = std::chrono::milliseconds{buf.ransacMaxruntimeMs};
366 }
367
368 RansacShapeDetector detector(ransacOptions); // the detector object
369 {
370 // set which primitives are to be detected by adding the respective constructors
371 detector.Add(new PlanePrimitiveShapeConstructor());
372 detector.Add(new SpherePrimitiveShapeConstructor());
374 //detector.Add(new ConePrimitiveShapeConstructor());
375 //detector.Add(new TorusPrimitiveShapeConstructor());
376 }
377
378
379 ARMARX_INFO << "run ransac";
381 shapes; // stores the detected shapes
382 size_t remaining = detector.Detect(pc, 0, pc.size(), &shapes); // run detection
383 // returns number of unassigned points
384 // the array shapes is filled with pointers to the detected shapes
385 // the second element per shapes gives the number of points assigned to that primitive (the support)
386 // the points belonging to the first shape (shapes[0]) have been sorted to the end of pc,
387 // i.e. into the range [ pc.size() - shapes[0].second, pc.size() )
388 // the points of shape i are found in the range
389 // [ pc.size() - \sum_{j=0..i} shapes[j].second, pc.size() - \sum_{j=0..i-1} shapes[j].second )
390
391 ARMARX_INFO << "remaining unassigned points " << remaining << std::endl;
392
393 setDebugObserverDatafield("ransac_num_remaining_points", remaining);
394 setDebugObserverDatafield("ransac_num_shapes", shapes.size());
395
396
397 viz::Layer cloudLayer = arviz.layer("Cloud");
398 {
399 cloudLayer.add(viz::PointCloud("points")
400 .position(Eigen::Vector3f::Zero())
401 .transparency(0.0f)
402 .pointSizeInPixels(8)
403 .pointCloud(*transformedCloud));
404 }
405 viz::Layer ransacLayer = arviz.layer("Ransac");
406 //draw shapes and cloud
407 std::size_t numCyl = 0;
408 std::size_t numSph = 0;
409 std::size_t numPla = 0;
410 std::size_t numUnk = 0;
411
412 static int maxnum = 0;
413 int i = 0;
414 int idx = 0;
415
416 const auto draw_cloud = [&](std::size_t num, std::string desc)
417 {
418 viz::Layer cloudLayer = arviz.layer("Cloud " + std::to_string(i) + " " + desc);
419 viz::PointCloud cloud("points");
420 cloud.position(Eigen::Vector3f::Zero());
421 cloud.transparency(0.0f);
422 cloud.pointSizeInPixels(8);
423
424 for (std::size_t i2 = 0; i2 < num; ++i2, ++idx)
425 {
426 const auto& pt = pc.at(pc.size() - 1 - idx);
427 cloud.addPoint(
428 pt.pos.getValue()[0], pt.pos.getValue()[1], pt.pos.getValue()[2], i);
429 }
430 cloud.colorGlasbeyLUT(i);
431 cloudLayer.add(cloud);
432 arviz.commit({cloudLayer});
433 };
434
435 for (; i < shapes.size(); ++i)
436 {
437 using plane_t = const PlanePrimitiveShape;
438 using sphere_t = const SpherePrimitiveShape;
439 using cylinder_t = const CylinderPrimitiveShape;
440
441 const auto& [s, sz] = shapes.at(i);
442 const std::string name = std::to_string(i);
443
444 if (plane_t* ptr = dynamic_cast<plane_t*>(s.Ptr()); ptr)
445 {
446 ++numPla;
447 Eigen::Vector3f c;
448 ptr->Internal().getPosition().getValue(c.x(), c.y(), c.z());
449
450 Eigen::Vector3f n;
451 ptr->Internal().getNormal().getValue(n.x(), n.y(), n.z());
452
453 ransacLayer.add(viz::Polygon(name)
454 .colorGlasbeyLUT(i)
455 .lineColorGlasbeyLUT(i)
456 .lineWidth(1.0f)
457 .circle(c, n, 200, 64));
458 }
459 else if (sphere_t* ptr = dynamic_cast<sphere_t*>(s.Ptr()); ptr)
460 {
461 ++numSph;
462 Eigen::Vector3f c;
463 ptr->Internal().Center().getValue(c.x(), c.y(), c.z());
464
465 ransacLayer.add(viz::Sphere(name).colorGlasbeyLUT(i).position(c).radius(
466 ptr->Internal().Radius()));
467 }
468 else if (cylinder_t* ptr = dynamic_cast<cylinder_t*>(s.Ptr()); ptr)
469 {
470 ++numCyl;
471 Eigen::Vector3f c;
472 ptr->Internal().AxisPosition().getValue(c.x(), c.y(), c.z());
473
474 ransacLayer.add(viz::Cylinder(name)
475 .colorGlasbeyLUT(i)
476 .position(c)
477 .color(viz::Color::green())
478 .radius(ptr->Internal().Radius())
479 .height(ptr->Height()));
480 }
481 else
482 {
483 ++numUnk;
484 ARMARX_WARNING << "UNKNOWN shape " << simox::meta::get_type_name(s.Ptr());
485 }
486 //std::string desc;
487 //s->Description(&desc);
488 draw_cloud(sz, "dummy");
489 }
490 draw_cloud(remaining, "dummy");
491 for (; i < maxnum; ++i)
492 {
493 draw_cloud(0, "dummy");
494 }
495
496 setDebugObserverDatafield("ransac_num_cylinders", numCyl);
497 setDebugObserverDatafield("ransac_num_spheres", numSph);
498 setDebugObserverDatafield("ransac_num_planes", numPla);
499 setDebugObserverDatafield("ransac_num_unknown", numUnk);
500 arviz.commit({cloudLayer, ransacLayer});
501 }
502 }
503
507
508} // namespace armarx
#define ARMARX_REGISTER_COMPONENT_EXECUTABLE(ComponentT, applicationName)
Definition Decoupled.h:29
#define call_template_function(F)
#define VAROUT(x)
constexpr T c
constexpr T dt
T & at(size_type i)
Definition Vector.h:314
size_type size() const
Definition Vector.h:215
void calcNormals(float radius, unsigned int kNN=20, unsigned int maxTries=100)
void setBBox(Vec3f bbl, float size)
Definition PointCloud.h:116
float getScale() const
Definition PointCloud.h:138
size_t Detect(PointCloud &pc, size_t begin, size_t end, MiscLib::Vector< std::pair< MiscLib::RefCountPtr< PrimitiveShape >, size_t > > *shapes)
void Add(PrimitiveShapeConstructor *c)
Definition basic.h:18
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
Property< PropertyType > getProperty(const std::string &name)
Property definitions of EfficientRANSACPrimitiveExtractor.
Brief description of class EfficientRANSACPrimitiveExtractor.
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
void process() override
Process the vision component.
void onExitPointCloudProcessor() override
Exit the ImapeProcessor component.
void onConnectPointCloudProcessor() override
Implement this method in the PointCloudProcessor in order execute parts when the component is fully i...
void onDisconnectPointCloudProcessor() override
Implement this method in the PointCloudProcessor in order execute parts when the component looses net...
void onInitPointCloudProcessor() override
Setup the vision component.
The FramedPose class.
Definition FramedPose.h:281
void changeFrame(const SharedRobotInterfacePrx &referenceRobot, const std::string &newFrame)
SpamFilterDataPtr deactivateSpam(float deactivationDurationSec=10.0f, const std::string &identifier="", bool deactivate=true) const
disables the logging for the current line for the given amount of seconds.
Definition Logging.cpp:99
std::string prefix
Prefix of the properties such as namespace, domain, component name, etc.
PropertyDefinition< PropertyType > & defineOptionalProperty(const std::string &name, PropertyType defaultValue, const std::string &description="", PropertyDefinitionBase::PropertyConstness constness=PropertyDefinitionBase::eConstant)
ValueProxy< T > getValue(std::string const &name)
RobotStateComponentPlugin::RobotData getRobotData(const std::string &id) const
bool synchronizeLocalClone(const VirtualRobot::RobotPtr &robot) const
VirtualRobot::RobotPtr addRobot(const std::string &id, const VirtualRobot::RobotPtr &robot, const VirtualRobot::RobotNodeSetPtr &rns={}, const VirtualRobot::RobotNodePtr &node={})
DerivedT & colorGlasbeyLUT(std::size_t id, int alpha=255)
Definition ElementOps.h:233
DerivedT & position(float x, float y, float z)
Definition ElementOps.h:136
PointCloud & pointSizeInPixels(float s)
Definition PointCloud.h:53
PointCloud & addPoint(ColoredPoint const &p)
Definition PointCloud.h:81
PointCloud & transparency(float t)
Definition PointCloud.h:45
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...
PointCloudProviderInfo getPointCloudProvider(std::string name, bool waitForProxy=false)
Select an PointCloudProvider.
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_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
#define ARMARX_ON_SCOPE_EXIT
Executes given code when the enclosing scope is left.
detail::IntSpinBoxBuilder makeIntSpinBox(std::string const &name)
detail::FloatSpinBoxBuilder makeFloatSpinBox(std::string const &name)
detail::SimpleGridLayoutBuilder makeSimpleGridLayout(std::string const &name="")
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
ArmarX headers.
std::chrono::milliseconds m_maxruntime
SimpleGridLayoutBuilder & addTextLabel(std::string const &text, int colspan)
SimpleGridLayoutBuilder & addChild(WidgetPtr const &child, int colspan)
SimpleGridLayoutBuilder & addEmptyWidget(int colspan)
Cylinder & height(float h)
Definition Elements.h:84
void add(ElementT const &element)
Definition Layer.h:31
#define ARMARX_TRACE
Definition trace.h:77