Point Cloud Library (PCL) 1.15.1-dev
Loading...
Searching...
No Matches
min_cut_segmentation.hpp
1/*
2 * Software License Agreement (BSD License)
3 *
4 * Point Cloud Library (PCL) - www.pointclouds.org
5 *
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
18 * * Neither the name of the copyright holder(s) nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 *
35 * $Id:$
36 *
37 */
38
39#ifndef PCL_SEGMENTATION_MIN_CUT_SEGMENTATION_HPP_
40#define PCL_SEGMENTATION_MIN_CUT_SEGMENTATION_HPP_
41
42#include <boost/graph/boykov_kolmogorov_max_flow.hpp> // for boykov_kolmogorov_max_flow
43#include <pcl/segmentation/min_cut_segmentation.h>
44#include <pcl/search/search.h>
45#include <pcl/search/auto.h>
46#include <cmath>
47
48//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
49template <typename PointT>
51
52//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
53template <typename PointT>
55{
56 foreground_points_.clear ();
57 background_points_.clear ();
58 clusters_.clear ();
59 vertices_.clear ();
60 edge_marker_.clear ();
61}
62
63//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
64template <typename PointT> void
66{
67 input_ = cloud;
68 graph_is_valid_ = false;
69 unary_potentials_are_valid_ = false;
70 binary_potentials_are_valid_ = false;
71}
72
73//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
74template <typename PointT> double
76{
77 return (pow (1.0 / inverse_sigma_, 0.5));
78}
79
80//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
81template <typename PointT> void
83{
84 if (sigma > epsilon_)
85 {
86 inverse_sigma_ = 1.0 / (sigma * sigma);
87 binary_potentials_are_valid_ = false;
88 }
89}
90
91//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
92template <typename PointT> double
94{
95 return (pow (radius_, 0.5));
96}
97
98//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
99template <typename PointT> void
101{
102 if (radius > epsilon_)
103 {
104 radius_ = radius * radius;
105 unary_potentials_are_valid_ = false;
106 }
107}
108
109//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
110template <typename PointT> double
112{
113 return (source_weight_);
114}
115
116//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
117template <typename PointT> void
119{
120 if (weight > epsilon_)
121 {
122 source_weight_ = weight;
123 unary_potentials_are_valid_ = false;
124 }
125}
126
127//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
128template <typename PointT> typename pcl::MinCutSegmentation<PointT>::KdTreePtr
130{
131 return (search_);
132}
133
134//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
135template <typename PointT> void
137{
138 search_ = tree;
139}
140
141//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
142template <typename PointT> unsigned int
144{
145 return (number_of_neighbours_);
146}
147
148//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
149template <typename PointT> void
151{
152 if (number_of_neighbours_ != neighbour_number && neighbour_number != 0)
153 {
154 number_of_neighbours_ = neighbour_number;
155 graph_is_valid_ = false;
156 unary_potentials_are_valid_ = false;
157 binary_potentials_are_valid_ = false;
158 }
159}
160
161//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
162template <typename PointT> std::vector<PointT, Eigen::aligned_allocator<PointT> >
164{
165 return (foreground_points_);
166}
167
168//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
169template <typename PointT> void
171{
172 foreground_points_.clear ();
173 foreground_points_.insert(
174 foreground_points_.end(), foreground_points->cbegin(), foreground_points->cend());
175
176 unary_potentials_are_valid_ = false;
177}
178
179//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
180template <typename PointT> std::vector<PointT, Eigen::aligned_allocator<PointT> >
182{
183 return (background_points_);
184}
185
186//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
187template <typename PointT> void
189{
190 background_points_.clear ();
191 background_points_.insert(
192 background_points_.end(), background_points->cbegin(), background_points->cend());
193
194 unary_potentials_are_valid_ = false;
195}
196
197//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
198template <typename PointT> void
199pcl::MinCutSegmentation<PointT>::extract (std::vector <pcl::PointIndices>& clusters)
200{
201 clusters.clear ();
202
203 bool segmentation_is_possible = initCompute ();
204 if ( !segmentation_is_possible )
205 {
206 deinitCompute ();
207 return;
208 }
209
210 if ( graph_is_valid_ && unary_potentials_are_valid_ && binary_potentials_are_valid_ )
211 {
212 clusters.reserve (clusters_.size ());
213 std::copy (clusters_.cbegin (), clusters_.cend (), std::back_inserter (clusters));
214 deinitCompute ();
215 return;
216 }
217
218 clusters_.clear ();
219
220 if ( !graph_is_valid_ )
221 {
222 bool success = buildGraph ();
223 if (!success)
224 {
225 deinitCompute ();
226 return;
227 }
228 graph_is_valid_ = true;
229 unary_potentials_are_valid_ = true;
230 binary_potentials_are_valid_ = true;
231 }
232
233 if ( !unary_potentials_are_valid_ )
234 {
235 bool success = recalculateUnaryPotentials ();
236 if (!success)
237 {
238 deinitCompute ();
239 return;
240 }
241 unary_potentials_are_valid_ = true;
242 }
243
244 if ( !binary_potentials_are_valid_ )
245 {
246 bool success = recalculateBinaryPotentials ();
247 if (!success)
248 {
249 deinitCompute ();
250 return;
251 }
252 binary_potentials_are_valid_ = true;
253 }
254
255 //IndexMap index_map = boost::get (boost::vertex_index, *graph_);
256 ResidualCapacityMap residual_capacity = boost::get (boost::edge_residual_capacity, *graph_);
257
258 max_flow_ = boost::boykov_kolmogorov_max_flow (*graph_, source_, sink_);
259
260 assembleLabels (residual_capacity);
261
262 clusters.reserve (clusters_.size ());
263 std::copy (clusters_.cbegin (), clusters_.cend (), std::back_inserter (clusters));
264
265 deinitCompute ();
266}
267
268//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
269template <typename PointT> double
271{
272 return (max_flow_);
273}
274
275//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
276template <typename PointT> typename pcl::MinCutSegmentation<PointT>::mGraphPtr
278{
279 return (graph_);
280}
281
282//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
283template <typename PointT> bool
285{
286 const auto number_of_points = input_->size ();
287 const auto number_of_indices = indices_->size ();
288
289 if (input_->points.empty () || number_of_points == 0 || foreground_points_.empty () == true )
290 return (false);
291
292 if (!search_)
293 {
294 search_.reset (pcl::search::autoSelectMethod<PointT>(input_, indices_, true, pcl::search::Purpose::many_knn_search));
295 }
296 else
297 {
298 search_->setInputCloud (input_, indices_);
299 }
300
301 graph_.reset (new mGraph);
302
303 capacity_.reset (new CapacityMap);
304 *capacity_ = boost::get (boost::edge_capacity, *graph_);
305
306 reverse_edges_.reset (new ReverseEdgeMap);
307 *reverse_edges_ = boost::get (boost::edge_reverse, *graph_);
308
309 VertexDescriptor vertex_descriptor(0);
310 vertices_.clear ();
311 vertices_.resize (number_of_points + 2, vertex_descriptor);
312
313 std::set<int> out_edges_marker;
314 edge_marker_.clear ();
315 edge_marker_.resize (number_of_points + 2, out_edges_marker);
316
317 for (std::size_t i_point = 0; i_point < number_of_points + 2; i_point++)
318 vertices_[i_point] = boost::add_vertex (*graph_);
319
320 source_ = vertices_[number_of_points];
321 sink_ = vertices_[number_of_points + 1];
322
323 for (const auto& point_index : (*indices_))
324 {
325 double source_weight = 0.0;
326 double sink_weight = 0.0;
327 calculateUnaryPotential (point_index, source_weight, sink_weight);
328 addEdge (static_cast<int> (source_), point_index, source_weight);
329 addEdge (point_index, static_cast<int> (sink_), sink_weight);
330 }
331
332 pcl::Indices neighbours;
333 std::vector<float> distances;
334 for (std::size_t i_point = 0; i_point < number_of_indices; i_point++)
335 {
336 index_t point_index = (*indices_)[i_point];
337 search_->nearestKSearch (i_point, number_of_neighbours_, neighbours, distances);
338 for (std::size_t i_nghbr = 1; i_nghbr < neighbours.size (); i_nghbr++)
339 {
340 double weight = calculateBinaryPotential (point_index, neighbours[i_nghbr]);
341 addEdge (point_index, neighbours[i_nghbr], weight);
342 addEdge (neighbours[i_nghbr], point_index, weight);
343 }
344 neighbours.clear ();
345 distances.clear ();
346 }
347
348 return (true);
349}
350
351//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
352template <typename PointT> void
353pcl::MinCutSegmentation<PointT>::calculateUnaryPotential (int point, double& source_weight, double& sink_weight) const
354{
355 double min_dist_to_foreground = std::numeric_limits<double>::max ();
356 //double min_dist_to_background = std::numeric_limits<double>::max ();
357 //double closest_background_point[] = {0.0, 0.0};
358 double initial_point[] = {0.0, 0.0};
359
360 initial_point[0] = (*input_)[point].x;
361 initial_point[1] = (*input_)[point].y;
362
363 for (const auto& fg_point : foreground_points_)
364 {
365 double dist = 0.0;
366 dist += (fg_point.x - initial_point[0]) * (fg_point.x - initial_point[0]);
367 dist += (fg_point.y - initial_point[1]) * (fg_point.y - initial_point[1]);
368 if (min_dist_to_foreground > dist)
369 {
370 min_dist_to_foreground = dist;
371 }
372 }
373
374 sink_weight = pow (min_dist_to_foreground / radius_, 0.5);
375
376 source_weight = source_weight_;
377 return;
378/*
379 if (background_points_.size () == 0)
380 return;
381
382 for (const auto& bg_point : background_points_)
383 {
384 double dist = 0.0;
385 dist += (bg_point.x - initial_point[0]) * (bg_point.x - initial_point[0]);
386 dist += (bg_point.y - initial_point[1]) * (bg_point.y - initial_point[1]);
387 if (min_dist_to_background > dist)
388 {
389 min_dist_to_background = dist;
390 closest_background_point[0] = bg_point.x;
391 closest_background_point[1] = bg_point.y;
392 }
393 }
394
395 if (min_dist_to_background <= epsilon_)
396 {
397 source_weight = 0.0;
398 sink_weight = 1.0;
399 return;
400 }
401
402 source_weight = 1.0 / (1.0 + pow (min_dist_to_background / min_dist_to_foreground, 0.5));
403 sink_weight = 1 - source_weight;
404*/
405}
406
407//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
408template <typename PointT> bool
409pcl::MinCutSegmentation<PointT>::addEdge (int source, int target, double weight)
410{
411 auto iter_out = edge_marker_[source].find (target);
412 if ( iter_out != edge_marker_[source].end () )
413 return (false);
414
415 EdgeDescriptor edge;
416 EdgeDescriptor reverse_edge;
417 bool edge_was_added, reverse_edge_was_added;
418
419 boost::tie (edge, edge_was_added) = boost::add_edge ( vertices_[source], vertices_[target], *graph_ );
420 boost::tie (reverse_edge, reverse_edge_was_added) = boost::add_edge ( vertices_[target], vertices_[source], *graph_ );
421 if ( !edge_was_added || !reverse_edge_was_added )
422 return (false);
423
424 (*capacity_)[edge] = weight;
425 (*capacity_)[reverse_edge] = 0.0;
426 (*reverse_edges_)[edge] = reverse_edge;
427 (*reverse_edges_)[reverse_edge] = edge;
428 edge_marker_[source].insert (target);
429
430 return (true);
431}
432
433//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
434template <typename PointT> double
436{
437 double weight = 0.0;
438 double distance = 0.0;
439 distance += ((*input_)[source].x - (*input_)[target].x) * ((*input_)[source].x - (*input_)[target].x);
440 distance += ((*input_)[source].y - (*input_)[target].y) * ((*input_)[source].y - (*input_)[target].y);
441 distance += ((*input_)[source].z - (*input_)[target].z) * ((*input_)[source].z - (*input_)[target].z);
442 distance *= inverse_sigma_;
443 weight = std::exp (-distance);
444
445 return (weight);
446}
447
448//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
449template <typename PointT> bool
451{
452 OutEdgeIterator src_edge_iter;
453 OutEdgeIterator src_edge_end;
454 std::pair<EdgeDescriptor, bool> sink_edge;
455
456 for (boost::tie (src_edge_iter, src_edge_end) = boost::out_edges (source_, *graph_); src_edge_iter != src_edge_end; ++src_edge_iter)
457 {
458 double source_weight = 0.0;
459 double sink_weight = 0.0;
460 sink_edge.second = false;
461 calculateUnaryPotential (static_cast<int> (boost::target (*src_edge_iter, *graph_)), source_weight, sink_weight);
462 sink_edge = boost::lookup_edge (boost::target (*src_edge_iter, *graph_), sink_, *graph_);
463 if (!sink_edge.second)
464 return (false);
465
466 (*capacity_)[*src_edge_iter] = source_weight;
467 (*capacity_)[sink_edge.first] = sink_weight;
468 }
469
470 return (true);
471}
472
473//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
474template <typename PointT> bool
476{
477 VertexIterator vertex_iter;
478 VertexIterator vertex_end;
479 OutEdgeIterator edge_iter;
480 OutEdgeIterator edge_end;
481
482 std::vector< std::set<VertexDescriptor> > edge_marker;
483 std::set<VertexDescriptor> out_edges_marker;
484 edge_marker.clear ();
485 edge_marker.resize (input_->size () + 2, out_edges_marker);
486
487 for (boost::tie (vertex_iter, vertex_end) = boost::vertices (*graph_); vertex_iter != vertex_end; ++vertex_iter)
488 {
489 VertexDescriptor source_vertex = *vertex_iter;
490 if (source_vertex == source_ || source_vertex == sink_)
491 continue;
492 for (boost::tie (edge_iter, edge_end) = boost::out_edges (source_vertex, *graph_); edge_iter != edge_end; ++edge_iter)
493 {
494 //If this is not the edge of the graph, but the reverse fictitious edge that is needed for the algorithm then continue
495 EdgeDescriptor reverse_edge = (*reverse_edges_)[*edge_iter];
496 if ((*capacity_)[reverse_edge] != 0.0)
497 continue;
498
499 //If we already changed weight for this edge then continue
500 VertexDescriptor target_vertex = boost::target (*edge_iter, *graph_);
501 auto iter_out = edge_marker[static_cast<int> (source_vertex)].find (target_vertex);
502 if ( iter_out != edge_marker[static_cast<int> (source_vertex)].end () )
503 continue;
504
505 if (target_vertex != source_ && target_vertex != sink_)
506 {
507 //Change weight and remember that this edges were updated
508 double weight = calculateBinaryPotential (static_cast<int> (target_vertex), static_cast<int> (source_vertex));
509 (*capacity_)[*edge_iter] = weight;
510 edge_marker[static_cast<int> (source_vertex)].insert (target_vertex);
511 }
512 }
513 }
514
515 return (true);
516}
517
518//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
519template <typename PointT> void
521{
522 std::vector<int> labels;
523 labels.resize (input_->size (), 0);
524 for (const auto& i_point : (*indices_))
525 labels[i_point] = 1;
526
527 clusters_.clear ();
528
529 pcl::PointIndices segment;
530 clusters_.resize (2, segment);
531
532 OutEdgeIterator edge_iter, edge_end;
533 for ( boost::tie (edge_iter, edge_end) = boost::out_edges (source_, *graph_); edge_iter != edge_end; ++edge_iter )
534 {
535 if (labels[edge_iter->m_target] == 1)
536 {
537 if (residual_capacity[*edge_iter] > epsilon_)
538 clusters_[1].indices.push_back (static_cast<int> (edge_iter->m_target));
539 else
540 clusters_[0].indices.push_back (static_cast<int> (edge_iter->m_target));
541 }
542 }
543}
544
545//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
546template <typename PointT> pcl::PointCloud<pcl::PointXYZRGB>::Ptr
548{
550
551 if (!clusters_.empty ())
552 {
553 colored_cloud.reset(new pcl::PointCloud<pcl::PointXYZRGB>);
554 unsigned char foreground_color[3] = {255, 255, 255};
555 unsigned char background_color[3] = {255, 0, 0};
556 colored_cloud->width = (clusters_[0].indices.size () + clusters_[1].indices.size ());
557 colored_cloud->height = 1;
558 colored_cloud->is_dense = input_->is_dense;
559
560 pcl::PointXYZRGB point;
561 for (const auto& point_index : (clusters_[0].indices))
562 {
563 point.x = *((*input_)[point_index].data);
564 point.y = *((*input_)[point_index].data + 1);
565 point.z = *((*input_)[point_index].data + 2);
566 point.r = background_color[0];
567 point.g = background_color[1];
568 point.b = background_color[2];
569 colored_cloud->points.push_back (point);
570 }
571
572 for (const auto& point_index : (clusters_[1].indices))
573 {
574 point.x = *((*input_)[point_index].data);
575 point.y = *((*input_)[point_index].data + 1);
576 point.z = *((*input_)[point_index].data + 2);
577 point.r = foreground_color[0];
578 point.g = foreground_color[1];
579 point.b = foreground_color[2];
580 colored_cloud->points.push_back (point);
581 }
582 }
583
584 return (colored_cloud);
585}
586
587#define PCL_INSTANTIATE_MinCutSegmentation(T) template class PCL_EXPORTS pcl::MinCutSegmentation<T>;
588
589#endif // PCL_SEGMENTATION_MIN_CUT_SEGMENTATION_HPP_
KdTreePtr getSearchMethod() const
Returns search method that is used for finding KNN.
void calculateUnaryPotential(int point, double &source_weight, double &sink_weight) const
Returns unary potential(data cost) for the given point index.
void setSigma(double sigma)
Allows to set the normalization value for the binary potentials as described in the article.
double getSigma() const
Returns normalization value for binary potentials.
MinCutSegmentation()
Constructor that sets default values for member variables.
void setRadius(double radius)
Allows to set the radius to the background.
void extract(std::vector< pcl::PointIndices > &clusters)
This method launches the segmentation algorithm and returns the clusters that were obtained during th...
double getSourceWeight() const
Returns weight that every edge from the source point has.
mGraphPtr getGraph() const
Returns the graph that was build for finding the minimum cut.
void setInputCloud(const PointCloudConstPtr &cloud) override
This method simply sets the input point cloud.
void setSourceWeight(double weight)
Allows to set weight for source edges.
~MinCutSegmentation() override
Destructor that frees memory.
void setBackgroundPoints(typename pcl::PointCloud< PointT >::Ptr background_points)
Allows to specify points which are known to be the points of the background.
boost::graph_traits< mGraph >::out_edge_iterator OutEdgeIterator
unsigned int getNumberOfNeighbours() const
Returns the number of neighbours to find.
bool buildGraph()
This method simply builds the graph that will be used during the segmentation.
boost::property_map< mGraph, boost::edge_capacity_t >::type CapacityMap
double getMaxFlow() const
Returns that flow value that was calculated during the segmentation.
bool recalculateUnaryPotentials()
This method recalculates unary potentials(data cost) if some changes were made, instead of creating n...
boost::property_map< mGraph, boost::edge_reverse_t >::type ReverseEdgeMap
shared_ptr< mGraph > mGraphPtr
void setSearchMethod(const KdTreePtr &tree)
Allows to set search method for finding KNN.
double getRadius() const
Returns radius to the background.
double calculateBinaryPotential(int source, int target) const
Returns the binary potential(smooth cost) for the given indices of points.
bool recalculateBinaryPotentials()
This method recalculates binary potentials(smooth cost) if some changes were made,...
std::vector< PointT, Eigen::aligned_allocator< PointT > > getBackgroundPoints() const
Returns the points that must belong to background.
bool addEdge(int source, int target, double weight)
This method simply adds the edge from the source point to the target point with a given weight.
Traits::vertex_descriptor VertexDescriptor
void setNumberOfNeighbours(unsigned int neighbour_number)
Allows to set the number of neighbours to find.
typename PointCloud::ConstPtr PointCloudConstPtr
boost::graph_traits< mGraph >::vertex_iterator VertexIterator
typename KdTree::Ptr KdTreePtr
std::vector< PointT, Eigen::aligned_allocator< PointT > > getForegroundPoints() const
Returns the points that must belong to foreground.
boost::adjacency_list< boost::vecS, boost::vecS, boost::directedS, boost::property< boost::vertex_name_t, std::string, boost::property< boost::vertex_index_t, long, boost::property< boost::vertex_color_t, boost::default_color_type, boost::property< boost::vertex_distance_t, long, boost::property< boost::vertex_predecessor_t, Traits::edge_descriptor > > > > >, boost::property< boost::edge_capacity_t, double, boost::property< boost::edge_residual_capacity_t, double, boost::property< boost::edge_reverse_t, Traits::edge_descriptor > > > > mGraph
boost::graph_traits< mGraph >::edge_descriptor EdgeDescriptor
boost::property_map< mGraph, boost::edge_residual_capacity_t >::type ResidualCapacityMap
void setForegroundPoints(typename pcl::PointCloud< PointT >::Ptr foreground_points)
Allows to specify points which are known to be the points of the object.
pcl::PointCloud< pcl::PointXYZRGB >::Ptr getColoredCloud()
Returns the colored cloud.
void assembleLabels(ResidualCapacityMap &residual_capacity)
This method analyzes the residual network and assigns a label to every point in the cloud.
PointCloud represents the base class in PCL for storing collections of 3D points.
const_iterator cbegin() const noexcept
const_iterator cend() const noexcept
bool is_dense
True if no points are invalid (e.g., have NaN or Inf values in any of their floating point fields).
std::uint32_t width
The point cloud width (if organized as an image-structure).
std::uint32_t height
The point cloud height (if organized as an image-structure).
shared_ptr< PointCloud< PointT > > Ptr
std::vector< PointT, Eigen::aligned_allocator< PointT > > points
The point data.
@ many_knn_search
The search method will mainly be used for nearestKSearch where k is larger than 1.
detail::int_type_t< detail::index_type_size, detail::index_type_signed > index_t
Type used for an index in PCL.
Definition types.h:112
IndicesAllocator<> Indices
Type used for indices in PCL.
Definition types.h:133
A point structure representing Euclidean xyz coordinates, and the RGB color.