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
25#include <pcl/common/transforms.h>
26#include <pcl/filters/statistical_outlier_removal.h>
27
28#include <SimoxUtility/meta/type_name.h>
29
31
33
35
36#include <VisionX/interface/components/Calibration.h>
37
38//gui
39namespace armarx
40{
41 RemoteGui::WidgetPtr
43 {
44 const auto& buf = _cfgBuf.getWriteBuffer();
47 .cols(2)
48
49 .addTextLabel("outlierMeanK")
50 .addChild(
51 RemoteGui::makeIntSpinBox("outlierMeanK").value(buf.outlierMeanK).min(1).max(1000))
52
53 .addTextLabel("outlierStddevMulThresh")
54 .addChild(RemoteGui::makeFloatSpinBox("outlierStddevMulThresh")
55 .value(buf.outlierStddevMulThresh)
56 .min(0)
57 .max(10))
58
61
62 .addTextLabel("ransacEpsilon")
64 .value(buf.ransacEpsilon)
65 .min(1)
66 .max(1000))
67
68 .addTextLabel("ransacBitmapEpsilon")
69 .addChild(RemoteGui::makeFloatSpinBox("ransacBitmapEpsilon")
70 .value(buf.ransacBitmapEpsilon)
71 .min(1)
72 .max(10000))
73
74 .addTextLabel("ransacNormalThresh")
75 .addChild(RemoteGui::makeFloatSpinBox("ransacNormalThresh")
76 .value(buf.ransacNormalThresh)
77 .min(0)
78 .max(1)
79 .decimals(3))
80
81 .addTextLabel("ransacProbability")
82 .addChild(RemoteGui::makeFloatSpinBox("ransacProbability")
83 .value(buf.ransacProbability)
84 .min(0)
85 .max(1)
86 .decimals(3))
87
88 .addTextLabel("ransacMinSupport")
89 .addChild(RemoteGui::makeIntSpinBox("ransacMinSupport")
90 .value(buf.ransacMinSupport)
91 .min(1)
92 .max(1000))
93
94 .addTextLabel("ransacMaxruntimeMs")
95 .addChild(RemoteGui::makeIntSpinBox("ransacMaxruntimeMs")
96 .value(buf.ransacMaxruntimeMs)
97 .min(1)
98 .max(10000));
99 }
100
101 void
103 {
105 prx.receiveUpdates();
106
107 auto& buf = _cfgBuf.getWriteBuffer();
108 prx.getValue(buf.outlierMeanK, "outlierMeanK");
109 prx.getValue(buf.outlierStddevMulThresh, "outlierStddevMulThresh");
110
111 prx.getValue(buf.ransacEpsilon, "ransacEpsilon");
112 prx.getValue(buf.ransacNormalThresh, "ransacNormalThresh");
113 prx.getValue(buf.ransacMinSupport, "ransacMinSupport");
114 prx.getValue(buf.ransacBitmapEpsilon, "ransacBitmapEpsilon");
115 prx.getValue(buf.ransacProbability, "ransacProbability");
116 prx.getValue(buf.ransacMaxruntimeMs, "ransacMaxruntimeMs");
117 _cfgBuf.commitWrite();
118 prx.sendUpdates();
119 }
120} // namespace armarx
121
122#define result_name_transformed getName() + "_Transformed"
123#define result_name_filtered getName() + "_Filtered"
124
125namespace armarx
126{
130 {
132 "ProviderReferenceFrame",
133 "",
134 "Name of the point cloud refence frame (empty = autodetect)");
135 }
136
137 std::string
139 {
140 return "EfficientRANSACPrimitiveExtractor";
141 }
142
143 void
145 {
146 _pointCloudProviderName = getProperty<std::string>("ProviderName");
147 usingPointCloudProvider(_pointCloudProviderName);
148 }
149
150 void
152 {
153 //provider
154 {
157
158 _pointCloudProviderInfo = getPointCloudProvider(_pointCloudProviderName, true);
159 _pointCloudProvider = _pointCloudProviderInfo.proxy;
160 _pointCloudProviderRefFrame = getProperty<std::string>("ProviderReferenceFrame");
161 if (_pointCloudProviderRefFrame.empty())
162 {
163 _pointCloudProviderRefFrame = "root";
164 auto frameprov =
165 visionx::ReferenceFrameInterfacePrx::checkedCast(_pointCloudProvider);
166 if (frameprov)
167 {
168 _pointCloudProviderRefFrame = frameprov->getReferenceFrame();
169 }
170 }
171 ARMARX_INFO << VAROUT(_pointCloudProviderRefFrame);
172 }
173 //robot
174 {
175 addRobot("_localRobot", VirtualRobot::RobotIO::eStructure);
176 _localRobot = getRobotData("_localRobot");
177 }
178 //gui
180 [this](RemoteGui::TabProxy& prx) { processGui(prx); });
182 }
183
184 void
188
189 void
193
194 void
196 {
197
198#define call_template_function(F) \
199 switch (_pointCloudProviderInfo.pointCloudFormat->type) \
200 { \
201 case visionx::PointContentType::ePoints: \
202 F<pcl::PointXYZ>(); \
203 break; \
204 case visionx::PointContentType::eColoredPoints: \
205 F<pcl::PointXYZRGBA>(); \
206 break; \
207 case visionx::PointContentType::eColoredOrientedPoints: \
208 F<pcl::PointXYZRGBNormal>(); \
209 break; \
210 case visionx::PointContentType::eLabeledPoints: \
211 F<pcl::PointXYZL>(); \
212 break; \
213 case visionx::PointContentType::eColoredLabeledPoints: \
214 F<pcl::PointXYZRGBL>(); \
215 break; \
216 case visionx::PointContentType::eIntensity: \
217 F<pcl::PointXYZI>(); \
218 break; \
219 case visionx::PointContentType::eOrientedPoints: \
220 ARMARX_ERROR << "eOrientedPoints NOT HANDLED IN VISIONX"; \
221 [[fallthrough]]; \
222 default: \
223 ARMARX_ERROR << "Could not process point cloud, because format '" \
224 << _pointCloudProviderInfo.pointCloudFormat->type << "' is unknown"; \
225 } \
226 do \
227 { \
228 } while (false)
230 }
231
238
239} // namespace armarx
240
253
254namespace armarx
255{
256 template <class PointType>
257 void
259 {
260 const auto& buf = _cfgBuf.getUpToDateReadBuffer();
261 using cloud_t = pcl::PointCloud<PointType>;
262 using cloud_ptr_t = typename cloud_t::Ptr;
263 const auto now = [] { return std::chrono::high_resolution_clock::now(); };
264 const auto timeBeg = now();
266 {
267 const auto dt = now() - timeBeg;
268 const int us = std::chrono::duration_cast<std::chrono::microseconds>(dt).count();
269 setDebugObserverDatafield("time_full_process_call_s", us / 1e6f);
271 };
272 //get cloud + transform to global
273 cloud_ptr_t transformedCloud;
274 {
275 if (!waitForPointClouds(_pointCloudProviderName, 100))
276 {
277 ARMARX_INFO << deactivateSpam(10, _pointCloudProviderName)
278 << "Timeout or error while waiting for point cloud data of provider "
279 << _pointCloudProviderName;
280 return;
281 }
282 cloud_ptr_t inputCloud(new cloud_t);
283 getPointClouds(_pointCloudProviderName, inputCloud);
284 setDebugObserverDatafield("size_cloud_in", inputCloud->size());
285 synchronizeLocalClone(_localRobot);
286
287 const Eigen::Matrix4f providerInRootFrame = [&]() -> Eigen::Matrix4f
288 {
289 FramedPose fp{Eigen::Matrix4f::Identity(),
290 _pointCloudProviderRefFrame,
291 _localRobot.robot->getName()};
292 //fp.changeFrame(_localRobot.robot, _localRobot.robot->getRootNode()->getName());
293 fp.changeFrame(_localRobot.robot, "Global");
294 return fp.toEigen();
295 }();
296
297 transformedCloud = cloud_ptr_t(new cloud_t);
298 pcl::transformPointCloud(*inputCloud, *transformedCloud, providerInRootFrame);
299
301 }
302
303 //remove outliers
304 {
305 cloud_ptr_t filtered(new cloud_t);
306 pcl::StatisticalOutlierRemoval<PointType> sor;
307 sor.setInputCloud(transformedCloud);
308 sor.setMeanK(buf.outlierMeanK);
309 sor.setStddevMulThresh(buf.outlierStddevMulThresh);
310 sor.filter(*filtered);
311 setDebugObserverDatafield("size_cloud_filtered", filtered->size());
313 std::swap(filtered, transformedCloud);
314 }
315
316 //ransac stuff
317 {
318 ARMARX_INFO << "creating point vec";
319 std::vector<Point> points;
320 points.reserve(transformedCloud->size());
321 static constexpr float inf = std::numeric_limits<float>::infinity();
322 float minX = inf;
323 float minY = inf;
324 float minZ = inf;
325 float maxX = -inf;
326 float maxY = -inf;
327 float maxZ = -inf;
328 for (const auto& p : *transformedCloud)
329 {
330 points.emplace_back(Vec3f(p.x, p.y, p.z));
331 std::tie(minX, maxX) = std::minmax({minX, maxX, p.x});
332 std::tie(minY, maxY) = std::minmax({minY, maxY, p.y});
333 std::tie(minZ, maxZ) = std::minmax({minZ, maxZ, p.z});
334 }
335 ARMARX_INFO << "creating cloud";
336 PointCloud pc(points.data(), points.size());
337 pc.setBBox({minX, minY, minZ}, {maxX, maxY, maxZ});
338 ARMARX_INFO << "calc cloud normals";
339 pc.calcNormals(100);
340 ARMARX_INFO << VAROUT(pc.getScale());
341
342 ARMARX_INFO << "configure ransac";
343 RansacShapeDetector::Options ransacOptions;
344 {
345 ransacOptions.m_epsilon =
346 buf.ransacEpsilon; // set distance threshold to .01f of bounding box width
347 // NOTE: Internally the distance threshold is taken as 3 * ransacOptions.m_epsilon!!!
348 ransacOptions.m_bitmapEpsilon =
349 buf.ransacBitmapEpsilon; // set bitmap resolution to .02f of bounding box width
350 // NOTE: This threshold is NOT multiplied internally!
351 ransacOptions.m_normalThresh =
352 buf.ransacNormalThresh; // this is the cos of the maximal normal deviation
353 ransacOptions.m_minSupport =
354 buf.ransacMinSupport; // this is the minimal numer of points required for a primitive
355 ransacOptions.m_probability =
356 buf.ransacProbability; // this is the "probability" with which a primitive is overlooked
357 ransacOptions.m_maxruntime = std::chrono::milliseconds{buf.ransacMaxruntimeMs};
358 }
359
360 RansacShapeDetector detector(ransacOptions); // the detector object
361 {
362 // set which primitives are to be detected by adding the respective constructors
363 detector.Add(new PlanePrimitiveShapeConstructor());
364 detector.Add(new SpherePrimitiveShapeConstructor());
366 //detector.Add(new ConePrimitiveShapeConstructor());
367 //detector.Add(new TorusPrimitiveShapeConstructor());
368 }
369
370
371 ARMARX_INFO << "run ransac";
373 shapes; // stores the detected shapes
374 size_t remaining = detector.Detect(pc, 0, pc.size(), &shapes); // run detection
375 // returns number of unassigned points
376 // the array shapes is filled with pointers to the detected shapes
377 // the second element per shapes gives the number of points assigned to that primitive (the support)
378 // the points belonging to the first shape (shapes[0]) have been sorted to the end of pc,
379 // i.e. into the range [ pc.size() - shapes[0].second, pc.size() )
380 // the points of shape i are found in the range
381 // [ pc.size() - \sum_{j=0..i} shapes[j].second, pc.size() - \sum_{j=0..i-1} shapes[j].second )
382
383 ARMARX_INFO << "remaining unassigned points " << remaining << std::endl;
384
385 setDebugObserverDatafield("ransac_num_remaining_points", remaining);
386 setDebugObserverDatafield("ransac_num_shapes", shapes.size());
387
388
389 viz::Layer cloudLayer = arviz.layer("Cloud");
390 {
391 cloudLayer.add(viz::PointCloud("points")
392 .position(Eigen::Vector3f::Zero())
393 .transparency(0.0f)
394 .pointSizeInPixels(8)
395 .pointCloud(*transformedCloud));
396 }
397 viz::Layer ransacLayer = arviz.layer("Ransac");
398 //draw shapes and cloud
399 std::size_t numCyl = 0;
400 std::size_t numSph = 0;
401 std::size_t numPla = 0;
402 std::size_t numUnk = 0;
403
404 static int maxnum = 0;
405 int i = 0;
406 int idx = 0;
407
408 const auto draw_cloud = [&](std::size_t num, std::string desc)
409 {
410 viz::Layer cloudLayer = arviz.layer("Cloud " + std::to_string(i) + " " + desc);
411 viz::PointCloud cloud("points");
412 cloud.position(Eigen::Vector3f::Zero());
413 cloud.transparency(0.0f);
414 cloud.pointSizeInPixels(8);
415
416 for (std::size_t i2 = 0; i2 < num; ++i2, ++idx)
417 {
418 const auto& pt = pc.at(pc.size() - 1 - idx);
419 cloud.addPoint(
420 pt.pos.getValue()[0], pt.pos.getValue()[1], pt.pos.getValue()[2], i);
421 }
422 cloud.colorGlasbeyLUT(i);
423 cloudLayer.add(cloud);
424 arviz.commit({cloudLayer});
425 };
426
427 for (; i < shapes.size(); ++i)
428 {
429 using plane_t = const PlanePrimitiveShape;
430 using sphere_t = const SpherePrimitiveShape;
431 using cylinder_t = const CylinderPrimitiveShape;
432
433 const auto& [s, sz] = shapes.at(i);
434 const std::string name = std::to_string(i);
435
436 if (plane_t* ptr = dynamic_cast<plane_t*>(s.Ptr()); ptr)
437 {
438 ++numPla;
439 Eigen::Vector3f c;
440 ptr->Internal().getPosition().getValue(c.x(), c.y(), c.z());
441
442 Eigen::Vector3f n;
443 ptr->Internal().getNormal().getValue(n.x(), n.y(), n.z());
444
445 ransacLayer.add(viz::Polygon(name)
446 .colorGlasbeyLUT(i)
447 .lineColorGlasbeyLUT(i)
448 .lineWidth(1.0f)
449 .circle(c, n, 200, 64));
450 }
451 else if (sphere_t* ptr = dynamic_cast<sphere_t*>(s.Ptr()); ptr)
452 {
453 ++numSph;
454 Eigen::Vector3f c;
455 ptr->Internal().Center().getValue(c.x(), c.y(), c.z());
456
457 ransacLayer.add(viz::Sphere(name).colorGlasbeyLUT(i).position(c).radius(
458 ptr->Internal().Radius()));
459 }
460 else if (cylinder_t* ptr = dynamic_cast<cylinder_t*>(s.Ptr()); ptr)
461 {
462 ++numCyl;
463 Eigen::Vector3f c;
464 ptr->Internal().AxisPosition().getValue(c.x(), c.y(), c.z());
465
466 ransacLayer.add(viz::Cylinder(name)
467 .colorGlasbeyLUT(i)
468 .position(c)
469 .color(viz::Color::green())
470 .radius(ptr->Internal().Radius())
471 .height(ptr->Height()));
472 }
473 else
474 {
475 ++numUnk;
476 ARMARX_WARNING << "UNKNOWN shape " << simox::meta::get_type_name(s.Ptr());
477 }
478 //std::string desc;
479 //s->Description(&desc);
480 draw_cloud(sz, "dummy");
481 }
482 draw_cloud(remaining, "dummy");
483 for (; i < maxnum; ++i)
484 {
485 draw_cloud(0, "dummy");
486 }
487
488 setDebugObserverDatafield("ransac_num_cylinders", numCyl);
489 setDebugObserverDatafield("ransac_num_spheres", numSph);
490 setDebugObserverDatafield("ransac_num_planes", numPla);
491 setDebugObserverDatafield("ransac_num_unknown", numUnk);
492 arviz.commit({cloudLayer, ransacLayer});
493 }
494 }
495} // namespace armarx
#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.
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