Point Cloud Library (PCL) 1.13.1
Loading...
Searching...
No Matches
cpc_segmentation.hpp
1/*
2 * Software License Agreement (BSD License)
3 *
4 * Point Cloud Library (PCL) - www.pointclouds.org
5 * Copyright (c) 2014-, Open Perception, Inc.
6 *
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 *
13 * * Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials provided
18 * with the distribution.
19 * * Neither the name of the copyright holder(s) nor the names of its
20 * contributors may be used to endorse or promote products derived
21 * from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 *
36 */
37
38#ifndef PCL_SEGMENTATION_IMPL_CPC_SEGMENTATION_HPP_
39#define PCL_SEGMENTATION_IMPL_CPC_SEGMENTATION_HPP_
40
41#include <pcl/sample_consensus/sac_model_plane.h> // for SampleConsensusModelPlane
42#include <pcl/segmentation/cpc_segmentation.h>
43
44template <typename PointT>
46 max_cuts_ (20),
47 min_segment_size_for_cutting_ (400),
48 min_cut_score_ (0.16),
49 use_local_constrains_ (true),
50 use_directed_weights_ (true),
51 ransac_itrs_ (10000)
52{
53}
54
55template <typename PointT>
57
58template <typename PointT> void
60{
61 if (supervoxels_set_)
62 {
63 // Calculate for every Edge if the connection is convex or invalid
64 // This effectively performs the segmentation.
65 calculateConvexConnections (sv_adjacency_list_);
66
67 // Correct edge relations using extended convexity definition if k>0
68 applyKconvexity (k_factor_);
69
70 // Determine whether to use cutting planes
71 doGrouping ();
72
73 grouping_data_valid_ = true;
74
75 applyCuttingPlane (max_cuts_);
76
77 // merge small segments
78 mergeSmallSegments ();
79 }
80 else
81 PCL_WARN ("[pcl::CPCSegmentation::segment] WARNING: Call function setInputSupervoxels first. Nothing has been done. \n");
82}
83
84template <typename PointT> void
85pcl::CPCSegmentation<PointT>::applyCuttingPlane (std::uint32_t depth_levels_left)
86{
87 using SegLabel2ClusterMap = std::map<std::uint32_t, pcl::PointCloud<WeightSACPointType>::Ptr>;
88
89 pcl::console::print_info ("Cutting at level %d (maximum %d)\n", max_cuts_ - depth_levels_left + 1, max_cuts_);
90 // stop if we reached the 0 level
91 if (depth_levels_left <= 0)
92 return;
93
94 pcl::IndicesPtr support_indices (new pcl::Indices);
95 SegLabel2ClusterMap seg_to_edge_points_map;
96 std::map<std::uint32_t, std::vector<EdgeID> > seg_to_edgeIDs_map;
97 EdgeIterator edge_itr, edge_itr_end, next_edge;
98 boost::tie (edge_itr, edge_itr_end) = boost::edges (sv_adjacency_list_);
99 for (next_edge = edge_itr; edge_itr != edge_itr_end; edge_itr = next_edge)
100 {
101 next_edge++; // next_edge iterator is necessary, because removing an edge invalidates the iterator to the current edge
102 std::uint32_t source_sv_label = sv_adjacency_list_[boost::source (*edge_itr, sv_adjacency_list_)];
103 std::uint32_t target_sv_label = sv_adjacency_list_[boost::target (*edge_itr, sv_adjacency_list_)];
104
105 std::uint32_t source_segment_label = sv_label_to_seg_label_map_[source_sv_label];
106 std::uint32_t target_segment_label = sv_label_to_seg_label_map_[target_sv_label];
107
108 // do not process edges which already split two segments
109 if (source_segment_label != target_segment_label)
110 continue;
111
112 // if edge has been used for cutting already do not use it again
113 if (sv_adjacency_list_[*edge_itr].used_for_cutting)
114 continue;
115 // get centroids of vertices
116 const pcl::PointXYZRGBA source_centroid = sv_label_to_supervoxel_map_[source_sv_label]->centroid_;
117 const pcl::PointXYZRGBA target_centroid = sv_label_to_supervoxel_map_[target_sv_label]->centroid_;
118
119 // stores the information about the edge cloud (used for the weighted ransac)
120 // we use the normal to express the direction of the connection
121 // we use the intensity to express the normal differences between supervoxel patches. <=0: Convex, >0: Concave
122 WeightSACPointType edge_centroid;
123 edge_centroid.getVector3fMap () = (source_centroid.getVector3fMap () + target_centroid.getVector3fMap ()) / 2;
124
125 // we use the normal to express the direction of the connection!
126 edge_centroid.getNormalVector3fMap () = (target_centroid.getVector3fMap () - source_centroid.getVector3fMap ()).normalized ();
127
128 // we use the intensity to express the normal differences between supervoxel patches. <=0: Convex, >0: Concave
129 edge_centroid.intensity = sv_adjacency_list_[*edge_itr].is_convex ? -sv_adjacency_list_[*edge_itr].normal_difference : sv_adjacency_list_[*edge_itr].normal_difference;
130 if (seg_to_edge_points_map.find (source_segment_label) == seg_to_edge_points_map.end ())
131 {
132 seg_to_edge_points_map[source_segment_label] = pcl::PointCloud<WeightSACPointType>::Ptr (new pcl::PointCloud<WeightSACPointType> ());
133 }
134 seg_to_edge_points_map[source_segment_label]->push_back (edge_centroid);
135 seg_to_edgeIDs_map[source_segment_label].push_back (*edge_itr);
136 }
137 bool cut_found = false;
138 // do the following processing for each segment separately
139 for (const auto &seg_to_edge_points : seg_to_edge_points_map)
140 {
141 // if too small do not process
142 if (seg_to_edge_points.second->size () < min_segment_size_for_cutting_)
143 {
144 continue;
145 }
146
147 std::vector<double> weights;
148 weights.resize (seg_to_edge_points.second->size ());
149 for (std::size_t cp = 0; cp < seg_to_edge_points.second->size (); ++cp)
150 {
151 float& cur_weight = (*seg_to_edge_points.second)[cp].intensity;
152 cur_weight = cur_weight < concavity_tolerance_threshold_ ? 0 : 1;
153 weights[cp] = cur_weight;
154 }
155
156 pcl::PointCloud<WeightSACPointType>::Ptr edge_cloud_cluster = seg_to_edge_points.second;
158
159 WeightedRandomSampleConsensus weight_sac (model_p, seed_resolution_, true);
160
161 weight_sac.setWeights (weights, use_directed_weights_);
162 weight_sac.setMaxIterations (ransac_itrs_);
163
164 // if not enough inliers are found
165 if (!weight_sac.computeModel ())
166 {
167 continue;
168 }
169
170 Eigen::VectorXf model_coefficients;
171 weight_sac.getModelCoefficients (model_coefficients);
172
173 model_coefficients[3] += std::numeric_limits<float>::epsilon ();
174
175 weight_sac.getInliers (*support_indices);
176
177 // the support_indices which are actually cut (if not locally constrain: cut_support_indices = support_indices
178 pcl::Indices cut_support_indices;
179
180 if (use_local_constrains_)
181 {
182 Eigen::Vector3f plane_normal (model_coefficients[0], model_coefficients[1], model_coefficients[2]);
183 // Cut the connections.
184 // We only iterate through the points which are within the support (when we are local, otherwise all points in the segment).
185 // We also just actually cut when the edge goes through the plane. This is why we check the planedistance
186 std::vector<pcl::PointIndices> cluster_indices;
189 tree->setInputCloud (edge_cloud_cluster);
190 euclidean_clusterer.setClusterTolerance (seed_resolution_);
191 euclidean_clusterer.setMinClusterSize (1);
192 euclidean_clusterer.setMaxClusterSize (25000);
193 euclidean_clusterer.setSearchMethod (tree);
194 euclidean_clusterer.setInputCloud (edge_cloud_cluster);
195 euclidean_clusterer.setIndices (support_indices);
196 euclidean_clusterer.extract (cluster_indices);
197// sv_adjacency_list_[seg_to_edgeID_map[seg_to_edge_points.first][point_index]].used_for_cutting = true;
198
199 for (const auto &cluster_index : cluster_indices)
200 {
201 // get centroids of vertices
202 float cluster_score = 0;
203// std::cout << "Cluster has " << cluster_indices[cc].indices.size () << " points" << std::endl;
204 for (const auto &current_index : cluster_index.indices)
205 {
206 double index_score = weights[current_index];
207 if (use_directed_weights_)
208 index_score *= 1.414 * (std::abs (plane_normal.dot (edge_cloud_cluster->at (current_index).getNormalVector3fMap ())));
209 cluster_score += index_score;
210 }
211 // check if the score is below the threshold. If that is the case this segment should not be split
212 cluster_score /= cluster_index.indices.size ();
213// std::cout << "Cluster score: " << cluster_score << std::endl;
214 if (cluster_score >= min_cut_score_)
215 {
216 cut_support_indices.insert (cut_support_indices.end (), cluster_index.indices.begin (), cluster_index.indices.end ());
217 }
218 }
219 if (cut_support_indices.empty ())
220 {
221// std::cout << "Could not find planes which exceed required minimum score (threshold " << min_cut_score_ << "), not cutting" << std::endl;
222 continue;
223 }
224 }
225 else
226 {
227 double current_score = weight_sac.getBestScore ();
228 cut_support_indices = *support_indices;
229 // check if the score is below the threshold. If that is the case this segment should not be split
230 if (current_score < min_cut_score_)
231 {
232// std::cout << "Score too low, no cutting" << std::endl;
233 continue;
234 }
235 }
236
237 int number_connections_cut = 0;
238 for (const auto &point_index : cut_support_indices)
239 {
240 if (use_clean_cutting_)
241 {
242 // skip edges where both centroids are on one side of the cutting plane
243 std::uint32_t source_sv_label = sv_adjacency_list_[boost::source (seg_to_edgeIDs_map[seg_to_edge_points.first][point_index], sv_adjacency_list_)];
244 std::uint32_t target_sv_label = sv_adjacency_list_[boost::target (seg_to_edgeIDs_map[seg_to_edge_points.first][point_index], sv_adjacency_list_)];
245 // get centroids of vertices
246 const pcl::PointXYZRGBA source_centroid = sv_label_to_supervoxel_map_[source_sv_label]->centroid_;
247 const pcl::PointXYZRGBA target_centroid = sv_label_to_supervoxel_map_[target_sv_label]->centroid_;
248 // this makes a clean cut
249 if (pcl::pointToPlaneDistanceSigned (source_centroid, model_coefficients) * pcl::pointToPlaneDistanceSigned (target_centroid, model_coefficients) > 0)
250 {
251 continue;
252 }
253 }
254 sv_adjacency_list_[seg_to_edgeIDs_map[seg_to_edge_points.first][point_index]].used_for_cutting = true;
255 if (sv_adjacency_list_[seg_to_edgeIDs_map[seg_to_edge_points.first][point_index]].is_valid)
256 {
257 ++number_connections_cut;
258 sv_adjacency_list_[seg_to_edgeIDs_map[seg_to_edge_points.first][point_index]].is_valid = false;
259 }
260 }
261// std::cout << "We cut " << number_connections_cut << " connections" << std::endl;
262 if (number_connections_cut > 0)
263 cut_found = true;
264 }
265
266 // if not cut has been performed we can stop the recursion
267 if (cut_found)
268 {
269 doGrouping ();
270 --depth_levels_left;
271 applyCuttingPlane (depth_levels_left);
272 }
273 else
274 pcl::console::print_info ("Could not find any more cuts, stopping recursion\n");
275}
276
277/******************************************* Directional weighted RANSAC definitions ******************************************************************/
278
279
280template <typename PointT> bool
282{
283 // Warn and exit if no threshold was set
284 if (threshold_ == std::numeric_limits<double>::max ())
285 {
286 PCL_ERROR ("[pcl::CPCSegmentation<PointT>::WeightedRandomSampleConsensus::computeModel] No threshold set!\n");
287 return (false);
288 }
289
290 iterations_ = 0;
291 best_score_ = -std::numeric_limits<double>::max ();
292
293 pcl::Indices selection;
294 Eigen::VectorXf model_coefficients;
295
296 unsigned skipped_count = 0;
297 // suppress infinite loops by just allowing 10 x maximum allowed iterations for invalid model parameters!
298 const unsigned max_skip = max_iterations_ * 10;
299
300 // Iterate
301 while (iterations_ < max_iterations_ && skipped_count < max_skip)
302 {
303 // Get X samples which satisfy the model criteria and which have a weight > 0
304 sac_model_->setIndices (model_pt_indices_);
305 sac_model_->getSamples (iterations_, selection);
306
307 if (selection.empty ())
308 {
309 PCL_ERROR ("[pcl::CPCSegmentation<PointT>::WeightedRandomSampleConsensus::computeModel] No samples could be selected!\n");
310 break;
311 }
312
313 // Search for inliers in the point cloud for the current plane model M
314 if (!sac_model_->computeModelCoefficients (selection, model_coefficients))
315 {
316 //++iterations_;
317 ++skipped_count;
318 continue;
319 }
320 // weight distances to get the score (only using connected inliers)
321 sac_model_->setIndices (full_cloud_pt_indices_);
322
323 pcl::IndicesPtr current_inliers (new pcl::Indices);
324 sac_model_->selectWithinDistance (model_coefficients, threshold_, *current_inliers);
325 double current_score = 0;
326 Eigen::Vector3f plane_normal (model_coefficients[0], model_coefficients[1], model_coefficients[2]);
327 for (const auto &current_index : *current_inliers)
328 {
329 double index_score = weights_[current_index];
330 if (use_directed_weights_)
331 // the sqrt(2) factor was used in the paper and was meant for making the scores better comparable between directed and undirected weights
332 index_score *= 1.414 * (std::abs (plane_normal.dot (point_cloud_ptr_->at (current_index).getNormalVector3fMap ())));
333
334 current_score += index_score;
335 }
336 // normalize by the total number of inliers
337 current_score /= current_inliers->size ();
338
339 // Better match ?
340 if (current_score > best_score_)
341 {
342 best_score_ = current_score;
343 // Save the current model/inlier/coefficients selection as being the best so far
344 model_ = selection;
345 model_coefficients_ = model_coefficients;
346 }
347
348 ++iterations_;
349 PCL_DEBUG ("[pcl::CPCSegmentation<PointT>::WeightedRandomSampleConsensus::computeModel] Trial %d (max %d): score is %f (best is: %f so far).\n", iterations_, max_iterations_, current_score, best_score_);
350 if (iterations_ > max_iterations_)
351 {
352 PCL_DEBUG ("[pcl::CPCSegmentation<PointT>::WeightedRandomSampleConsensus::computeModel] RANSAC reached the maximum number of trials.\n");
353 break;
354 }
355 }
356// std::cout << "Took us " << iterations_ - 1 << " iterations" << std::endl;
357 PCL_DEBUG ("[pcl::CPCSegmentation<PointT>::WeightedRandomSampleConsensus::computeModel] Model: %lu size, %f score.\n", model_.size (), best_score_);
358
359 if (model_.empty ())
360 {
361 inliers_.clear ();
362 return (false);
363 }
364
365 // Get the set of inliers that correspond to the best model found so far
366 sac_model_->selectWithinDistance (model_coefficients_, threshold_, inliers_);
367 return (true);
368}
369
370#endif // PCL_SEGMENTATION_IMPL_CPC_SEGMENTATION_HPP_
A segmentation algorithm partitioning a supervoxel graph.
void segment()
Merge supervoxels using cuts through local convexities.
~CPCSegmentation() override
EuclideanClusterExtraction represents a segmentation class for cluster extraction in an Euclidean sen...
void extract(std::vector< PointIndices > &clusters)
Cluster extraction in a PointCloud given by <setInputCloud (), setIndices ()>
void setClusterTolerance(double tolerance)
Set the spatial cluster tolerance as a measure in the L2 Euclidean space.
void setSearchMethod(const KdTreePtr &tree)
Provide a pointer to the search object.
void setMaxClusterSize(pcl::uindex_t max_cluster_size)
Set the maximum number of points that a cluster needs to contain in order to be considered valid.
void setMinClusterSize(pcl::uindex_t min_cluster_size)
Set the minimum number of points that a cluster needs to contain in order to be considered valid.
virtual void setInputCloud(const PointCloudConstPtr &cloud)
Provide a pointer to the input dataset.
Definition pcl_base.hpp:65
virtual void setIndices(const IndicesPtr &indices)
Provide a pointer to the vector of indices that represents the input data.
Definition pcl_base.hpp:72
PointCloud represents the base class in PCL for storing collections of 3D points.
const PointT & at(int column, int row) const
Obtain the point given by the (column, row) coordinates.
shared_ptr< PointCloud< PointT > > Ptr
SampleConsensusModelPlane defines a model for 3D plane segmentation.
shared_ptr< SampleConsensusModelPlane< PointT > > Ptr
search::KdTree is a wrapper class which inherits the pcl::KdTree class for performing search function...
Definition kdtree.h:62
shared_ptr< KdTree< PointT, Tree > > Ptr
Definition kdtree.h:75
double pointToPlaneDistanceSigned(const Point &p, double a, double b, double c, double d)
Get the distance from a point to a plane (signed) defined by ax+by+cz+d=0.
PCL_EXPORTS void print_info(const char *format,...)
Print an info message on stream with colors.
int cp(int from, int to)
Returns field copy operation code.
Definition repacks.hpp:56
IndicesAllocator<> Indices
Type used for indices in PCL.
Definition types.h:133
shared_ptr< Indices > IndicesPtr
Definition pcl_base.h:58
A point structure representing Euclidean xyz coordinates, and the RGBA color.