Point Cloud Library (PCL)  1.15.1-dev
region_growing_rgb.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  * Author : Sergey Ushakov
36  * Email : mine_all_mine@bk.ru
37  *
38  */
39 
40 #ifndef PCL_SEGMENTATION_REGION_GROWING_RGB_HPP_
41 #define PCL_SEGMENTATION_REGION_GROWING_RGB_HPP_
42 
43 #include <pcl/console/print.h> // for PCL_ERROR
44 #include <pcl/segmentation/region_growing_rgb.h>
45 #include <pcl/search/search.h>
46 #include <pcl/search/auto.h> // for pcl::search::autoSelectMethod
47 
48 #include <queue>
49 
50 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
51 template <typename PointT, typename NormalT>
53  point_distances_ (0),
54  segment_neighbours_ (0),
55  segment_distances_ (0),
56  segment_labels_ (0)
57 {
58  normal_flag_ = false;
59  curvature_flag_ = false;
60  residual_flag_ = false;
62 }
63 
64 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
65 template <typename PointT, typename NormalT>
67 {
68  point_distances_.clear ();
69  segment_neighbours_.clear ();
70  segment_distances_.clear ();
71  segment_labels_.clear ();
72 }
73 
74 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
75 template <typename PointT, typename NormalT> float
77 {
78  return (powf (color_p2p_threshold_, 0.5f));
79 }
80 
81 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
82 template <typename PointT, typename NormalT> void
84 {
85  color_p2p_threshold_ = thresh * thresh;
86 }
87 
88 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
89 template <typename PointT, typename NormalT> float
91 {
92  return (powf (color_r2r_threshold_, 0.5f));
93 }
94 
95 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
96 template <typename PointT, typename NormalT> void
98 {
99  color_r2r_threshold_ = thresh * thresh;
100 }
101 
102 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
103 template <typename PointT, typename NormalT> float
105 {
106  return (powf (distance_threshold_, 0.5f));
107 }
108 
109 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
110 template <typename PointT, typename NormalT> void
112 {
113  distance_threshold_ = thresh * thresh;
114 }
115 
116 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
117 template <typename PointT, typename NormalT> unsigned int
119 {
120  return (region_neighbour_number_);
121 }
122 
123 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
124 template <typename PointT, typename NormalT> void
126 {
127  region_neighbour_number_ = nghbr_number;
128 }
129 
130 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
131 template <typename PointT, typename NormalT> bool
133 {
134  return (normal_flag_);
135 }
136 
137 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
138 template <typename PointT, typename NormalT> void
140 {
141  normal_flag_ = value;
142 }
143 
144 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
145 template <typename PointT, typename NormalT> void
147 {
148  curvature_flag_ = value;
149 }
150 
151 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
152 template <typename PointT, typename NormalT> void
154 {
155  residual_flag_ = value;
156 }
157 
158 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
159 template <typename PointT, typename NormalT> void
160 pcl::RegionGrowingRGB<PointT, NormalT>::extract (std::vector <pcl::PointIndices>& clusters)
161 {
162  clusters_.clear ();
163  clusters.clear ();
164  point_neighbours_.clear ();
165  point_labels_.clear ();
166  num_pts_in_segment_.clear ();
167  point_distances_.clear ();
168  segment_neighbours_.clear ();
169  segment_distances_.clear ();
170  segment_labels_.clear ();
171  number_of_segments_ = 0;
172 
173  bool segmentation_is_possible = initCompute ();
174  if ( !segmentation_is_possible )
175  {
176  deinitCompute ();
177  return;
178  }
179 
180  segmentation_is_possible = prepareForSegmentation ();
181  if ( !segmentation_is_possible )
182  {
183  deinitCompute ();
184  return;
185  }
186 
187  findPointNeighbours ();
188  applySmoothRegionGrowingAlgorithm ();
190 
191  findSegmentNeighbours ();
192  applyRegionMergingAlgorithm ();
193 
194  auto cluster_iter = clusters_.begin ();
195  while (cluster_iter != clusters_.end ())
196  {
197  if (cluster_iter->indices.size () < min_pts_per_cluster_ ||
198  cluster_iter->indices.size () > max_pts_per_cluster_)
199  {
200  cluster_iter = clusters_.erase (cluster_iter);
201  }
202  else
203  ++cluster_iter;
204  }
205 
206  clusters.reserve (clusters_.size ());
207  std::copy (clusters_.cbegin (), clusters_.cend (), std::back_inserter (clusters));
208 
209  deinitCompute ();
210 }
211 
212 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
213 template <typename PointT, typename NormalT> bool
215 {
216  // if user forgot to pass point cloud or if it is empty
217  if ( input_->points.empty () )
218  return (false);
219 
220  // if normal/smoothness test is on then we need to check if all needed variables and parameters
221  // were correctly initialized
222  if (normal_flag_)
223  {
224  // if user forgot to pass normals or the sizes of point and normal cloud are different
225  if ( !normals_ || input_->size () != normals_->size () )
226  return (false);
227  }
228 
229  // if residual test is on then we need to check if all needed parameters were correctly initialized
230  if (residual_flag_)
231  {
232  if (residual_threshold_ <= 0.0f)
233  return (false);
234  }
235 
236  // if curvature test is on ...
237  // if (curvature_flag_)
238  // {
239  // in this case we do not need to check anything that related to it
240  // so we simply commented it
241  // }
242 
243  // here we check the parameters related to color-based segmentation
244  if ( region_neighbour_number_ == 0 || color_p2p_threshold_ < 0.0f || color_r2r_threshold_ < 0.0f || distance_threshold_ < 0.0f )
245  return (false);
246 
247  // from here we check those parameters that are always valuable
248  if (neighbour_number_ == 0)
249  return (false);
250 
251  if (indices_)
252  {
253  if (indices_->empty ())
254  PCL_ERROR ("[pcl::RegionGrowingRGB::prepareForSegmentation] Empty given indices!\n");
255  if (!search_)
256  search_.reset (pcl::search::autoSelectMethod<PointT>(input_, indices_, true, pcl::search::Purpose::many_knn_search));
257  else
258  search_->setInputCloud (input_, indices_);
259  }
260  else
261  {
262  if (!search_)
263  search_.reset (pcl::search::autoSelectMethod<PointT>(input_, true, pcl::search::Purpose::many_knn_search));
264  else
265  search_->setInputCloud (input_);
266  }
267 
268  return (true);
269 }
270 
271 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
272 template <typename PointT, typename NormalT> void
274 {
275  pcl::Indices neighbours;
276  std::vector<float> distances;
277 
278  point_neighbours_.resize (input_->size (), neighbours);
279  point_distances_.resize (input_->size (), distances);
280 
281  for (const auto& point_index: (*indices_))
282  {
283  neighbours.clear ();
284  distances.clear ();
285  search_->nearestKSearch ((*input_)[point_index], region_neighbour_number_, neighbours, distances);
286  point_neighbours_[point_index].swap (neighbours);
287  point_distances_[point_index].swap (distances);
288  }
289 }
290 
291 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
292 template <typename PointT, typename NormalT> void
294 {
295  pcl::Indices neighbours;
296  std::vector<float> distances;
297  segment_neighbours_.resize (number_of_segments_, neighbours);
298  segment_distances_.resize (number_of_segments_, distances);
299 
300  for (int i_seg = 0; i_seg < number_of_segments_; i_seg++)
301  {
302  pcl::Indices nghbrs;
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);
307  }
308 }
309 
310 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
311 template <typename PointT,typename NormalT> void
313 {
314  std::vector<float> distances;
315  float max_dist = std::numeric_limits<float>::max ();
316  distances.resize (clusters_.size (), max_dist);
317 
318  const auto number_of_points = num_pts_in_segment_[index];
319  //loop through every point in this segment and check neighbours
320  for (pcl::uindex_t i_point = 0; i_point < number_of_points; i_point++)
321  {
322  const auto point_index = clusters_[index].indices[i_point];
323  const auto number_of_neighbours = point_neighbours_[point_index].size ();
324  //loop through every neighbour of the current point, find out to which segment it belongs
325  //and if it belongs to neighbouring segment and is close enough then remember segment and its distance
326  for (std::size_t i_nghbr = 0; i_nghbr < number_of_neighbours; i_nghbr++)
327  {
328  // find segment
329  const pcl::index_t segment_index = point_labels_[ point_neighbours_[point_index][i_nghbr] ];
330 
331  if ( segment_index != index )
332  {
333  // try to push it to the queue
334  if (distances[segment_index] > point_distances_[point_index][i_nghbr])
335  distances[segment_index] = point_distances_[point_index][i_nghbr];
336  }
337  }
338  }// next point
339 
340  std::priority_queue<std::pair<float, int> > segment_neighbours;
341  for (int i_seg = 0; i_seg < number_of_segments_; i_seg++)
342  {
343  if (distances[i_seg] < max_dist)
344  {
345  segment_neighbours.emplace (distances[i_seg], i_seg);
346  if (segment_neighbours.size () > nghbr_number)
347  segment_neighbours.pop ();
348  }
349  }
350 
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);
354  pcl::uindex_t counter = 0;
355  while ( !segment_neighbours.empty () && counter < nghbr_number )
356  {
357  dist[counter] = segment_neighbours.top ().first;
358  nghbrs[counter] = segment_neighbours.top ().second;
359  segment_neighbours.pop ();
360  counter++;
361  }
362 }
363 
364 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
365 template <typename PointT, typename NormalT> void
367 {
368  // calculate color of each segment
369  std::vector< std::vector<unsigned int> > segment_color;
370  std::vector<unsigned int> color;
371  color.resize (3, 0);
372  segment_color.resize (number_of_segments_, color);
373 
374  for (const auto& point_index : (*indices_))
375  {
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;
380  }
381  for (int i_seg = 0; i_seg < number_of_segments_; i_seg++)
382  {
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]));
386  }
387 
388  // now it is time to find out if there are segments with a similar color
389  // and merge them together
390  std::vector<unsigned int> num_pts_in_homogeneous_region;
391  std::vector<int> num_seg_in_homogeneous_region;
392 
393  segment_labels_.resize (number_of_segments_, -1);
394 
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++)
398  {
399  int curr_homogeneous_region = 0;
400  if (segment_labels_[i_seg] == -1)
401  {
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++;
407  }
408  else
409  curr_homogeneous_region = segment_labels_[i_seg];
410 
411  unsigned int i_nghbr = 0;
412  while ( i_nghbr < region_neighbour_number_ && i_nghbr < segment_neighbours_[i_seg].size () )
413  {
414  int index = segment_neighbours_[i_seg][i_nghbr];
415  if (segment_distances_[i_seg][i_nghbr] > dist_thresh)
416  {
417  i_nghbr++;
418  continue;
419  }
420  if ( segment_labels_[index] == -1 )
421  {
422  float difference = calculateColorimetricalDifference (segment_color[i_seg], segment_color[index]);
423  if (difference < color_r2r_threshold_)
424  {
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;
428  }
429  }
430  i_nghbr++;
431  }// next neighbour
432  }// next segment
433 
434  segment_color.clear ();
435  color.clear ();
436 
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++)
441  {
442  final_segments[i_reg].resize (num_seg_in_homogeneous_region[i_reg], 0);
443  }
444 
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++)
448  {
449  int index = segment_labels_[i_seg];
450  final_segments[ index ][ counter[index] ] = i_seg;
451  counter[index] += 1;
452  }
453 
454  std::vector< std::vector< std::pair<float, pcl::index_t> > > region_neighbours;
455  findRegionNeighbours (region_neighbours, final_segments);
456 
457  int final_segment_number = homogeneous_region_number;
458  for (int i_reg = 0; i_reg < homogeneous_region_number; i_reg++)
459  {
460  if (num_pts_in_homogeneous_region[i_reg] < min_pts_per_cluster_)
461  {
462  if ( region_neighbours[i_reg].empty () )
463  continue;
464  int nearest_neighbour = region_neighbours[i_reg][0].second;
465  if ( region_neighbours[i_reg][0].first == std::numeric_limits<float>::max () )
466  continue;
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++)
470  {
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;
474  }
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;
481 
482  const auto filtered_region_neighbours_reg_index_end = std::remove_if (
483  region_neighbours[reg_index].begin (),
484  region_neighbours[reg_index].end (),
485  [this, reg_index] (const auto& nghbr) { return segment_labels_[ nghbr.second ] == reg_index; });
486  const auto filtered_region_neighbours_reg_index_size = std::distance (
487  region_neighbours[reg_index].begin (), filtered_region_neighbours_reg_index_end);
488  region_neighbours[reg_index].resize (filtered_region_neighbours_reg_index_size);
489 
490  for (const auto& nghbr : region_neighbours[i_reg])
491  {
492  if ( segment_labels_[ nghbr.second ] != reg_index )
493  {
494  region_neighbours[reg_index].push_back (nghbr);
495  }
496  }
497  region_neighbours[i_reg].clear ();
498  std::inplace_merge (
499  region_neighbours[reg_index].begin (),
500  std::next (region_neighbours[reg_index].begin (), filtered_region_neighbours_reg_index_size),
501  region_neighbours[reg_index].end (),
502  comparePair);
503  }
504  }
505 
506  assembleRegions (num_pts_in_homogeneous_region, static_cast<int> (num_pts_in_homogeneous_region.size ()));
507 
508  number_of_segments_ = final_segment_number;
509 }
510 
511 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
512 template <typename PointT, typename NormalT> float
513 pcl::RegionGrowingRGB<PointT, NormalT>::calculateColorimetricalDifference (std::vector<unsigned int>& first_color, std::vector<unsigned int>& second_color) const
514 {
515  float difference = 0.0f;
516  difference += static_cast<float>((first_color[0] - second_color[0]) * (first_color[0] - second_color[0]));
517  difference += static_cast<float>((first_color[1] - second_color[1]) * (first_color[1] - second_color[1]));
518  difference += static_cast<float>((first_color[2] - second_color[2]) * (first_color[2] - second_color[2]));
519  return (difference);
520 }
521 
522 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
523 template <typename PointT, typename NormalT> void
524 pcl::RegionGrowingRGB<PointT, NormalT>::findRegionNeighbours (std::vector< std::vector< std::pair<float, pcl::index_t> > >& neighbours_out, std::vector< std::vector<int> >& regions_in)
525 {
526  int region_number = static_cast<int> (regions_in.size ());
527  neighbours_out.clear ();
528  neighbours_out.resize (region_number);
529 
530  for (int i_reg = 0; i_reg < region_number; i_reg++)
531  {
532  neighbours_out[i_reg].reserve (regions_in[i_reg].size () * region_neighbour_number_);
533  for (const auto& curr_segment : regions_in[i_reg])
534  {
535  const std::size_t nghbr_number = segment_neighbours_[curr_segment].size ();
536  std::pair<float, pcl::index_t> pair;
537  for (std::size_t i_nghbr = 0; i_nghbr < nghbr_number; i_nghbr++)
538  {
539  const auto segment_index = segment_neighbours_[curr_segment][i_nghbr];
540  if ( segment_distances_[curr_segment][i_nghbr] == std::numeric_limits<float>::max () )
541  continue;
542  if (segment_labels_[segment_index] != i_reg)
543  {
544  pair.first = segment_distances_[curr_segment][i_nghbr];
545  pair.second = segment_index;
546  neighbours_out[i_reg].push_back (pair);
547  }
548  }// next neighbour
549  }// next segment
550  std::sort (neighbours_out[i_reg].begin (), neighbours_out[i_reg].end (), comparePair);
551  }// next homogeneous region
552 }
553 
554 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
555 template <typename PointT, typename NormalT> void
556 pcl::RegionGrowingRGB<PointT, NormalT>::assembleRegions (std::vector<unsigned int>& num_pts_in_region, int num_regions)
557 {
558  clusters_.clear ();
559  pcl::PointIndices segment;
560  clusters_.resize (num_regions, segment);
561  for (int i_seg = 0; i_seg < num_regions; i_seg++)
562  {
563  clusters_[i_seg].indices.resize (num_pts_in_region[i_seg]);
564  }
565 
566  std::vector<int> counter;
567  counter.resize (num_regions, 0);
568  for (const auto& point_index : (*indices_))
569  {
570  int index = point_labels_[point_index];
571  index = segment_labels_[index];
572  clusters_[index].indices[ counter[index] ] = point_index;
573  counter[index] += 1;
574  }
575 
576  // now we need to erase empty regions
577  if (clusters_.empty ())
578  return;
579 
580  std::vector<pcl::PointIndices>::iterator itr1, itr2;
581  itr1 = clusters_.begin ();
582  itr2 = clusters_.end () - 1;
583 
584  while (itr1 < itr2)
585  {
586  while (!(itr1->indices.empty ()) && itr1 < itr2)
587  ++itr1;
588  while ( itr2->indices.empty () && itr1 < itr2)
589  --itr2;
590 
591  if (itr1 != itr2)
592  itr1->indices.swap (itr2->indices);
593  }
594 
595  if (itr2->indices.empty ())
596  clusters_.erase (itr2, clusters_.end ());
597 }
598 
599 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
600 template <typename PointT, typename NormalT> bool
602 {
603  is_a_seed = true;
604 
605  // check the color difference
606  std::vector<unsigned int> point_color;
607  point_color.resize (3, 0);
608  std::vector<unsigned int> nghbr_color;
609  nghbr_color.resize (3, 0);
610  point_color[0] = (*input_)[point].r;
611  point_color[1] = (*input_)[point].g;
612  point_color[2] = (*input_)[point].b;
613  nghbr_color[0] = (*input_)[nghbr].r;
614  nghbr_color[1] = (*input_)[nghbr].g;
615  nghbr_color[2] = (*input_)[nghbr].b;
616  float difference = calculateColorimetricalDifference (point_color, nghbr_color);
617  if (difference > color_p2p_threshold_)
618  return (false);
619 
620  float cosine_threshold = std::cos (theta_threshold_);
621 
622  // check the angle between normals if needed
623  if (normal_flag_)
624  {
625  float data[4];
626  data[0] = (*input_)[point].data[0];
627  data[1] = (*input_)[point].data[1];
628  data[2] = (*input_)[point].data[2];
629  data[3] = (*input_)[point].data[3];
630 
631  Eigen::Map<Eigen::Vector3f> initial_point (static_cast<float*> (data));
632  Eigen::Map<Eigen::Vector3f> initial_normal (static_cast<float*> ((*normals_)[point].normal));
633  if (smooth_mode_flag_ == true)
634  {
635  Eigen::Map<Eigen::Vector3f> nghbr_normal (static_cast<float*> ((*normals_)[nghbr].normal));
636  float dot_product = std::abs (nghbr_normal.dot (initial_normal));
637  if (dot_product < cosine_threshold)
638  return (false);
639  }
640  else
641  {
642  Eigen::Map<Eigen::Vector3f> nghbr_normal (static_cast<float*> ((*normals_)[nghbr].normal));
643  Eigen::Map<Eigen::Vector3f> initial_seed_normal (static_cast<float*> ((*normals_)[initial_seed].normal));
644  float dot_product = std::abs (nghbr_normal.dot (initial_seed_normal));
645  if (dot_product < cosine_threshold)
646  return (false);
647  }
648  }
649 
650  // check the curvature if needed
651  if (curvature_flag_ && (*normals_)[nghbr].curvature > curvature_threshold_)
652  is_a_seed = false;
653 
654  // check the residual if needed
655  if (residual_flag_)
656  {
657  float data_p[4];
658  data_p[0] = (*input_)[point].data[0];
659  data_p[1] = (*input_)[point].data[1];
660  data_p[2] = (*input_)[point].data[2];
661  data_p[3] = (*input_)[point].data[3];
662  float data_n[4];
663  data_n[0] = (*input_)[nghbr].data[0];
664  data_n[1] = (*input_)[nghbr].data[1];
665  data_n[2] = (*input_)[nghbr].data[2];
666  data_n[3] = (*input_)[nghbr].data[3];
667  Eigen::Map<Eigen::Vector3f> nghbr_point (static_cast<float*> (data_n));
668  Eigen::Map<Eigen::Vector3f> initial_point (static_cast<float*> (data_p));
669  Eigen::Map<Eigen::Vector3f> initial_normal (static_cast<float*> ((*normals_)[point].normal));
670  float residual = std::abs (initial_normal.dot (initial_point - nghbr_point));
671  if (residual > residual_threshold_)
672  is_a_seed = false;
673  }
674 
675  return (true);
676 }
677 
678 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
679 template <typename PointT, typename NormalT> void
681 {
682  cluster.indices.clear ();
683 
684  bool segmentation_is_possible = initCompute ();
685  if ( !segmentation_is_possible )
686  {
687  deinitCompute ();
688  return;
689  }
690 
691  // first of all we need to find out if this point belongs to cloud
692  bool point_was_found = false;
693  for (const auto& point : (*indices_))
694  if (point == index)
695  {
696  point_was_found = true;
697  break;
698  }
699 
700  if (point_was_found)
701  {
702  if (clusters_.empty ())
703  {
704  clusters_.clear ();
705  point_neighbours_.clear ();
706  point_labels_.clear ();
707  num_pts_in_segment_.clear ();
708  point_distances_.clear ();
709  segment_neighbours_.clear ();
710  segment_distances_.clear ();
711  segment_labels_.clear ();
712  number_of_segments_ = 0;
713 
714  segmentation_is_possible = prepareForSegmentation ();
715  if ( !segmentation_is_possible )
716  {
717  deinitCompute ();
718  return;
719  }
720 
721  findPointNeighbours ();
722  applySmoothRegionGrowingAlgorithm ();
724 
725  findSegmentNeighbours ();
726  applyRegionMergingAlgorithm ();
727  }
728  // if we have already made the segmentation, then find the segment
729  // to which this point belongs
730  for (const auto& i_segment : clusters_)
731  {
732  const auto it = std::find (i_segment.indices.cbegin (), i_segment.indices.cend (), index);
733  if (it != i_segment.indices.cend())
734  {
735  // if segment was found
736  cluster.indices.clear ();
737  cluster.indices.reserve (i_segment.indices.size ());
738  std::copy (i_segment.indices.begin (), i_segment.indices.end (), std::back_inserter (cluster.indices));
739  break;
740  }
741  }// next segment
742  }// end if point was found
743 
744  deinitCompute ();
745 }
746 
747 #endif // PCL_SEGMENTATION_REGION_GROWING_RGB_HPP_
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 > > &regions_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.
float distance(const PointT &p1, const PointT &p2)
Definition: geometry.h:60
@ 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, false > uindex_t
Type used for an unsigned index in PCL.
Definition: types.h:120
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.
Definition: types.h:112
IndicesAllocator<> Indices
Type used for indices in PCL.
Definition: types.h:133