40 #ifndef PCL_SEGMENTATION_REGION_GROWING_RGB_HPP_
41 #define PCL_SEGMENTATION_REGION_GROWING_RGB_HPP_
43 #include <pcl/console/print.h>
44 #include <pcl/segmentation/region_growing_rgb.h>
45 #include <pcl/search/search.h>
46 #include <pcl/search/kdtree.h>
51 template <
typename Po
intT,
typename NormalT>
53 color_p2p_threshold_ (1225.0f),
54 color_r2r_threshold_ (10.0f),
55 distance_threshold_ (0.05f),
56 region_neighbour_number_ (100),
58 segment_neighbours_ (0),
59 segment_distances_ (0),
69 template <
typename Po
intT,
typename NormalT>
72 point_distances_.clear ();
73 segment_neighbours_.clear ();
74 segment_distances_.clear ();
75 segment_labels_.clear ();
79 template <
typename Po
intT,
typename NormalT>
float
82 return (powf (color_p2p_threshold_, 0.5f));
86 template <
typename Po
intT,
typename NormalT>
void
89 color_p2p_threshold_ = thresh * thresh;
93 template <
typename Po
intT,
typename NormalT>
float
96 return (powf (color_r2r_threshold_, 0.5f));
100 template <
typename Po
intT,
typename NormalT>
void
103 color_r2r_threshold_ = thresh * thresh;
107 template <
typename Po
intT,
typename NormalT>
float
110 return (powf (distance_threshold_, 0.5f));
114 template <
typename Po
intT,
typename NormalT>
void
117 distance_threshold_ = thresh * thresh;
121 template <
typename Po
intT,
typename NormalT>
unsigned int
124 return (region_neighbour_number_);
128 template <
typename Po
intT,
typename NormalT>
void
131 region_neighbour_number_ = nghbr_number;
135 template <
typename Po
intT,
typename NormalT>
bool
138 return (normal_flag_);
142 template <
typename Po
intT,
typename NormalT>
void
145 normal_flag_ = value;
149 template <
typename Po
intT,
typename NormalT>
void
152 curvature_flag_ = value;
156 template <
typename Po
intT,
typename NormalT>
void
159 residual_flag_ = value;
163 template <
typename Po
intT,
typename NormalT>
void
168 point_neighbours_.clear ();
169 point_labels_.clear ();
170 num_pts_in_segment_.clear ();
171 point_distances_.clear ();
172 segment_neighbours_.clear ();
173 segment_distances_.clear ();
174 segment_labels_.clear ();
175 number_of_segments_ = 0;
177 bool segmentation_is_possible = initCompute ();
178 if ( !segmentation_is_possible )
184 segmentation_is_possible = prepareForSegmentation ();
185 if ( !segmentation_is_possible )
191 findPointNeighbours ();
192 applySmoothRegionGrowingAlgorithm ();
195 findSegmentNeighbours ();
196 applyRegionMergingAlgorithm ();
198 auto cluster_iter = clusters_.begin ();
199 while (cluster_iter != clusters_.end ())
201 if (cluster_iter->indices.size () < min_pts_per_cluster_ ||
202 cluster_iter->indices.size () > max_pts_per_cluster_)
204 cluster_iter = clusters_.erase (cluster_iter);
210 clusters.reserve (clusters_.size ());
211 std::copy (clusters_.cbegin (), clusters_.cend (), std::back_inserter (clusters));
217 template <
typename Po
intT,
typename NormalT>
bool
221 if ( input_->points.empty () )
229 if ( !normals_ || input_->size () != normals_->size () )
236 if (residual_threshold_ <= 0.0f)
248 if ( region_neighbour_number_ == 0 || color_p2p_threshold_ < 0.0f || color_r2r_threshold_ < 0.0f || distance_threshold_ < 0.0f )
252 if (neighbour_number_ == 0)
261 if (indices_->empty ())
262 PCL_ERROR (
"[pcl::RegionGrowingRGB::prepareForSegmentation] Empty given indices!\n");
263 search_->setInputCloud (input_, indices_);
266 search_->setInputCloud (input_);
272 template <
typename Po
intT,
typename NormalT>
void
276 std::vector<float> distances;
278 point_neighbours_.resize (input_->size (), neighbours);
279 point_distances_.resize (input_->size (), distances);
281 for (
const auto& point_index: (*indices_))
285 search_->nearestKSearch (point_index, region_neighbour_number_, neighbours, distances);
286 point_neighbours_[point_index].swap (neighbours);
287 point_distances_[point_index].swap (distances);
292 template <
typename Po
intT,
typename NormalT>
void
296 std::vector<float> distances;
297 segment_neighbours_.resize (number_of_segments_, neighbours);
298 segment_distances_.resize (number_of_segments_, distances);
300 for (
int i_seg = 0; i_seg < number_of_segments_; i_seg++)
303 std::vector<float> dist;
304 findRegionsKNN (i_seg, region_neighbour_number_, nghbrs, dist);
305 segment_neighbours_[i_seg].swap (nghbrs);
306 segment_distances_[i_seg].swap (dist);
311 template <
typename Po
intT,
typename NormalT>
void
314 std::vector<float> distances;
315 float max_dist = std::numeric_limits<float>::max ();
316 distances.resize (clusters_.size (), max_dist);
318 const auto number_of_points = num_pts_in_segment_[index];
320 for (
pcl::uindex_t i_point = 0; i_point < number_of_points; i_point++)
322 const auto point_index = clusters_[index].indices[i_point];
323 const auto number_of_neighbours = point_neighbours_[point_index].size ();
326 for (std::size_t i_nghbr = 0; i_nghbr < number_of_neighbours; i_nghbr++)
329 const pcl::index_t segment_index = point_labels_[ point_neighbours_[point_index][i_nghbr] ];
331 if ( segment_index != index )
334 if (distances[segment_index] > point_distances_[point_index][i_nghbr])
335 distances[segment_index] = point_distances_[point_index][i_nghbr];
340 std::priority_queue<std::pair<float, int> > segment_neighbours;
341 for (
int i_seg = 0; i_seg < number_of_segments_; i_seg++)
343 if (distances[i_seg] < max_dist)
345 segment_neighbours.push (std::make_pair (distances[i_seg], i_seg) );
346 if (segment_neighbours.size () > nghbr_number)
347 segment_neighbours.pop ();
351 const std::size_t size = std::min<std::size_t> (segment_neighbours.size (),
static_cast<std::size_t
>(nghbr_number));
352 nghbrs.resize (size, 0);
353 dist.resize (size, 0);
355 while ( !segment_neighbours.empty () && counter < nghbr_number )
357 dist[counter] = segment_neighbours.top ().first;
358 nghbrs[counter] = segment_neighbours.top ().second;
359 segment_neighbours.pop ();
365 template <
typename Po
intT,
typename NormalT>
void
369 std::vector< std::vector<unsigned int> > segment_color;
370 std::vector<unsigned int> color;
372 segment_color.resize (number_of_segments_, color);
374 for (
const auto& point_index : (*indices_))
376 int segment_index = point_labels_[point_index];
377 segment_color[segment_index][0] += (*input_)[point_index].r;
378 segment_color[segment_index][1] += (*input_)[point_index].g;
379 segment_color[segment_index][2] += (*input_)[point_index].b;
381 for (
int i_seg = 0; i_seg < number_of_segments_; i_seg++)
383 segment_color[i_seg][0] =
static_cast<unsigned int> (
static_cast<float> (segment_color[i_seg][0]) /
static_cast<float> (num_pts_in_segment_[i_seg]));
384 segment_color[i_seg][1] =
static_cast<unsigned int> (
static_cast<float> (segment_color[i_seg][1]) /
static_cast<float> (num_pts_in_segment_[i_seg]));
385 segment_color[i_seg][2] =
static_cast<unsigned int> (
static_cast<float> (segment_color[i_seg][2]) /
static_cast<float> (num_pts_in_segment_[i_seg]));
390 std::vector<unsigned int> num_pts_in_homogeneous_region;
391 std::vector<int> num_seg_in_homogeneous_region;
393 segment_labels_.resize (number_of_segments_, -1);
395 float dist_thresh = distance_threshold_;
396 int homogeneous_region_number = 0;
397 for (
int i_seg = 0; i_seg < number_of_segments_; i_seg++)
399 int curr_homogeneous_region = 0;
400 if (segment_labels_[i_seg] == -1)
402 segment_labels_[i_seg] = homogeneous_region_number;
403 curr_homogeneous_region = homogeneous_region_number;
404 num_pts_in_homogeneous_region.push_back (num_pts_in_segment_[i_seg]);
405 num_seg_in_homogeneous_region.push_back (1);
406 homogeneous_region_number++;
409 curr_homogeneous_region = segment_labels_[i_seg];
411 unsigned int i_nghbr = 0;
412 while ( i_nghbr < region_neighbour_number_ && i_nghbr < segment_neighbours_[i_seg].size () )
414 int index = segment_neighbours_[i_seg][i_nghbr];
415 if (segment_distances_[i_seg][i_nghbr] > dist_thresh)
420 if ( segment_labels_[index] == -1 )
422 float difference = calculateColorimetricalDifference (segment_color[i_seg], segment_color[index]);
423 if (difference < color_r2r_threshold_)
425 segment_labels_[index] = curr_homogeneous_region;
426 num_pts_in_homogeneous_region[curr_homogeneous_region] += num_pts_in_segment_[index];
427 num_seg_in_homogeneous_region[curr_homogeneous_region] += 1;
434 segment_color.clear ();
437 std::vector< std::vector<int> > final_segments;
438 std::vector<int> region;
439 final_segments.resize (homogeneous_region_number, region);
440 for (
int i_reg = 0; i_reg < homogeneous_region_number; i_reg++)
442 final_segments[i_reg].resize (num_seg_in_homogeneous_region[i_reg], 0);
445 std::vector<int> counter;
446 counter.resize (homogeneous_region_number, 0);
447 for (
int i_seg = 0; i_seg < number_of_segments_; i_seg++)
449 int index = segment_labels_[i_seg];
450 final_segments[ index ][ counter[index] ] = i_seg;
454 std::vector< std::vector< std::pair<float, pcl::index_t> > > region_neighbours;
455 findRegionNeighbours (region_neighbours, final_segments);
457 int final_segment_number = homogeneous_region_number;
458 for (
int i_reg = 0; i_reg < homogeneous_region_number; i_reg++)
460 if (num_pts_in_homogeneous_region[i_reg] < min_pts_per_cluster_)
462 if ( region_neighbours[i_reg].empty () )
464 int nearest_neighbour = region_neighbours[i_reg][0].second;
465 if ( region_neighbours[i_reg][0].first == std::numeric_limits<float>::max () )
467 int reg_index = segment_labels_[nearest_neighbour];
468 int num_seg_in_reg = num_seg_in_homogeneous_region[i_reg];
469 for (
int i_seg = 0; i_seg < num_seg_in_reg; i_seg++)
471 int segment_index = final_segments[i_reg][i_seg];
472 final_segments[reg_index].push_back (segment_index);
473 segment_labels_[segment_index] = reg_index;
475 final_segments[i_reg].clear ();
476 num_pts_in_homogeneous_region[reg_index] += num_pts_in_homogeneous_region[i_reg];
477 num_pts_in_homogeneous_region[i_reg] = 0;
478 num_seg_in_homogeneous_region[reg_index] += num_seg_in_homogeneous_region[i_reg];
479 num_seg_in_homogeneous_region[i_reg] = 0;
480 final_segment_number -= 1;
482 for (
auto& nghbr : region_neighbours[reg_index])
484 if ( segment_labels_[ nghbr.second ] == reg_index )
486 nghbr.first = std::numeric_limits<float>::max ();
490 for (
const auto& nghbr : region_neighbours[i_reg])
492 if ( segment_labels_[ nghbr.second ] != reg_index )
494 region_neighbours[reg_index].push_back (nghbr);
497 region_neighbours[i_reg].clear ();
498 std::sort (region_neighbours[reg_index].begin (), region_neighbours[reg_index].end (),
comparePair);
502 assembleRegions (num_pts_in_homogeneous_region,
static_cast<int> (num_pts_in_homogeneous_region.size ()));
504 number_of_segments_ = final_segment_number;
508 template <
typename Po
intT,
typename NormalT>
float
511 float difference = 0.0f;
512 difference +=
static_cast<float>((first_color[0] - second_color[0]) * (first_color[0] - second_color[0]));
513 difference +=
static_cast<float>((first_color[1] - second_color[1]) * (first_color[1] - second_color[1]));
514 difference +=
static_cast<float>((first_color[2] - second_color[2]) * (first_color[2] - second_color[2]));
519 template <
typename Po
intT,
typename NormalT>
void
522 int region_number =
static_cast<int> (regions_in.size ());
523 neighbours_out.clear ();
524 neighbours_out.resize (region_number);
526 for (
int i_reg = 0; i_reg < region_number; i_reg++)
528 neighbours_out[i_reg].reserve (regions_in[i_reg].size () * region_neighbour_number_);
529 for (
const auto& curr_segment : regions_in[i_reg])
531 const std::size_t nghbr_number = segment_neighbours_[curr_segment].size ();
532 std::pair<float, pcl::index_t> pair;
533 for (std::size_t i_nghbr = 0; i_nghbr < nghbr_number; i_nghbr++)
535 const auto segment_index = segment_neighbours_[curr_segment][i_nghbr];
536 if ( segment_distances_[curr_segment][i_nghbr] == std::numeric_limits<float>::max () )
538 if (segment_labels_[segment_index] != i_reg)
540 pair.first = segment_distances_[curr_segment][i_nghbr];
541 pair.second = segment_index;
542 neighbours_out[i_reg].push_back (pair);
546 std::sort (neighbours_out[i_reg].begin (), neighbours_out[i_reg].end (),
comparePair);
551 template <
typename Po
intT,
typename NormalT>
void
556 clusters_.resize (num_regions, segment);
557 for (
int i_seg = 0; i_seg < num_regions; i_seg++)
559 clusters_[i_seg].
indices.resize (num_pts_in_region[i_seg]);
562 std::vector<int> counter;
563 counter.resize (num_regions, 0);
564 for (
const auto& point_index : (*indices_))
566 int index = point_labels_[point_index];
567 index = segment_labels_[index];
568 clusters_[index].indices[ counter[index] ] = point_index;
573 if (clusters_.empty ())
576 std::vector<pcl::PointIndices>::iterator itr1, itr2;
577 itr1 = clusters_.begin ();
578 itr2 = clusters_.end () - 1;
582 while (!(itr1->indices.empty ()) && itr1 < itr2)
584 while ( itr2->indices.empty () && itr1 < itr2)
588 itr1->indices.swap (itr2->indices);
591 if (itr2->indices.empty ())
592 clusters_.erase (itr2, clusters_.end ());
596 template <
typename Po
intT,
typename NormalT>
bool
602 std::vector<unsigned int> point_color;
603 point_color.resize (3, 0);
604 std::vector<unsigned int> nghbr_color;
605 nghbr_color.resize (3, 0);
606 point_color[0] = (*input_)[point].r;
607 point_color[1] = (*input_)[point].g;
608 point_color[2] = (*input_)[point].b;
609 nghbr_color[0] = (*input_)[nghbr].r;
610 nghbr_color[1] = (*input_)[nghbr].g;
611 nghbr_color[2] = (*input_)[nghbr].b;
612 float difference = calculateColorimetricalDifference (point_color, nghbr_color);
613 if (difference > color_p2p_threshold_)
616 float cosine_threshold = std::cos (theta_threshold_);
622 data[0] = (*input_)[point].data[0];
623 data[1] = (*input_)[point].data[1];
624 data[2] = (*input_)[point].data[2];
625 data[3] = (*input_)[point].data[3];
627 Eigen::Map<Eigen::Vector3f> initial_point (
static_cast<float*
> (data));
628 Eigen::Map<Eigen::Vector3f> initial_normal (
static_cast<float*
> ((*normals_)[point].normal));
629 if (smooth_mode_flag_ ==
true)
631 Eigen::Map<Eigen::Vector3f> nghbr_normal (
static_cast<float*
> ((*normals_)[nghbr].normal));
632 float dot_product = std::abs (nghbr_normal.dot (initial_normal));
633 if (dot_product < cosine_threshold)
638 Eigen::Map<Eigen::Vector3f> nghbr_normal (
static_cast<float*
> ((*normals_)[nghbr].normal));
639 Eigen::Map<Eigen::Vector3f> initial_seed_normal (
static_cast<float*
> ((*normals_)[initial_seed].normal));
640 float dot_product = std::abs (nghbr_normal.dot (initial_seed_normal));
641 if (dot_product < cosine_threshold)
647 if (curvature_flag_ && (*normals_)[nghbr].curvature > curvature_threshold_)
654 data_p[0] = (*input_)[point].data[0];
655 data_p[1] = (*input_)[point].data[1];
656 data_p[2] = (*input_)[point].data[2];
657 data_p[3] = (*input_)[point].data[3];
659 data_n[0] = (*input_)[nghbr].data[0];
660 data_n[1] = (*input_)[nghbr].data[1];
661 data_n[2] = (*input_)[nghbr].data[2];
662 data_n[3] = (*input_)[nghbr].data[3];
663 Eigen::Map<Eigen::Vector3f> nghbr_point (
static_cast<float*
> (data_n));
664 Eigen::Map<Eigen::Vector3f> initial_point (
static_cast<float*
> (data_p));
665 Eigen::Map<Eigen::Vector3f> initial_normal (
static_cast<float*
> ((*normals_)[point].normal));
666 float residual = std::abs (initial_normal.dot (initial_point - nghbr_point));
667 if (residual > residual_threshold_)
675 template <
typename Po
intT,
typename NormalT>
void
680 bool segmentation_is_possible = initCompute ();
681 if ( !segmentation_is_possible )
688 bool point_was_found =
false;
689 for (
const auto& point : (*indices_))
692 point_was_found =
true;
698 if (clusters_.empty ())
701 point_neighbours_.clear ();
702 point_labels_.clear ();
703 num_pts_in_segment_.clear ();
704 point_distances_.clear ();
705 segment_neighbours_.clear ();
706 segment_distances_.clear ();
707 segment_labels_.clear ();
708 number_of_segments_ = 0;
710 segmentation_is_possible = prepareForSegmentation ();
711 if ( !segmentation_is_possible )
717 findPointNeighbours ();
718 applySmoothRegionGrowingAlgorithm ();
721 findSegmentNeighbours ();
722 applyRegionMergingAlgorithm ();
726 for (
const auto& i_segment : clusters_)
728 const auto it = std::find (i_segment.indices.cbegin (), i_segment.indices.cend (), index);
729 if (it != i_segment.indices.cend())
733 cluster.
indices.reserve (i_segment.indices.size ());
734 std::copy (i_segment.indices.begin (), i_segment.indices.end (), std::back_inserter (cluster.
indices));
bool curvature_flag_
If set to true then curvature test will be done during segmentation.
bool normal_flag_
If set to true then normal/smoothness test will be done during segmentation.
bool residual_flag_
If set to true then residual test will be done during segmentation.
pcl::uindex_t min_pts_per_cluster_
Stores the minimum number of points that a cluster needs to contain in order to be considered valid.
void assembleRegions()
This function simply assembles the regions from list of point labels.
void getSegmentFromPoint(index_t index, pcl::PointIndices &cluster) override
For a given point this function builds a segment to which it belongs and returns this segment.
void setDistanceThreshold(float thresh)
Allows to set distance threshold.
void setRegionColorThreshold(float thresh)
This method specifies the threshold value for color test between the regions.
float getPointColorThreshold() const
Returns the color threshold value used for testing if points belong to the same region.
bool prepareForSegmentation() override
This method simply checks if it is possible to execute the segmentation algorithm with the current se...
void findRegionNeighbours(std::vector< std::vector< std::pair< float, pcl::index_t > > > &neighbours_out, std::vector< std::vector< int > > ®ions_in)
This method assembles the array containing neighbours of each homogeneous region.
void applyRegionMergingAlgorithm()
This function implements the merging algorithm described in the article "Color-based segmentation of ...
unsigned int getNumberOfRegionNeighbours() const
Returns the number of nearest neighbours used for searching K nearest segments.
void setCurvatureTestFlag(bool value) override
Allows to turn on/off the curvature test.
void extract(std::vector< pcl::PointIndices > &clusters) override
This method launches the segmentation algorithm and returns the clusters that were obtained during th...
void setNormalTestFlag(bool value)
Allows to turn on/off the smoothness test.
void setNumberOfRegionNeighbours(unsigned int nghbr_number)
This method allows to set the number of neighbours that is used for finding neighbouring segments.
float calculateColorimetricalDifference(std::vector< unsigned int > &first_color, std::vector< unsigned int > &second_color) const
This method calculates the colorimetrical difference between two points.
float getDistanceThreshold() const
Returns the distance threshold.
bool getNormalTestFlag() const
Returns the flag that signalize if the smoothness test is turned on/off.
void findRegionsKNN(pcl::index_t index, pcl::uindex_t nghbr_number, Indices &nghbrs, std::vector< float > &dist)
This method finds K nearest neighbours of the given segment.
RegionGrowingRGB()
Constructor that sets default values for member variables.
void setResidualTestFlag(bool value) override
Allows to turn on/off the residual test.
~RegionGrowingRGB() override
Destructor that frees memory.
void findPointNeighbours() override
This method finds KNN for each point and saves them to the array because the algorithm needs to find ...
float getRegionColorThreshold() const
Returns the color threshold value used for testing if regions can be merged.
void findSegmentNeighbours()
This method simply calls the findRegionsKNN for each segment and saves the results for later use.
void setPointColorThreshold(float thresh)
This method specifies the threshold value for color test between the points.
bool validatePoint(index_t initial_seed, index_t point, index_t nghbr, bool &is_a_seed) const override
This function is checking if the point with index 'nghbr' belongs to the segment.
search::KdTree is a wrapper class which inherits the pcl::KdTree class for performing search function...
detail::int_type_t< detail::index_type_size, false > uindex_t
Type used for an unsigned index in PCL.
bool comparePair(std::pair< float, int > i, std::pair< float, int > j)
This function is used as a comparator for sorting.
detail::int_type_t< detail::index_type_size, detail::index_type_signed > index_t
Type used for an index in PCL.
IndicesAllocator<> Indices
Type used for indices in PCL.