39 #ifndef PCL_SEGMENTATION_MIN_CUT_SEGMENTATION_HPP_
40 #define PCL_SEGMENTATION_MIN_CUT_SEGMENTATION_HPP_
42 #include <boost/graph/boykov_kolmogorov_max_flow.hpp>
43 #include <pcl/segmentation/min_cut_segmentation.h>
44 #include <pcl/search/search.h>
45 #include <pcl/search/auto.h>
49 template <
typename Po
intT>
53 template <
typename Po
intT>
56 foreground_points_.clear ();
57 background_points_.clear ();
60 edge_marker_.clear ();
64 template <
typename Po
intT>
void
68 graph_is_valid_ =
false;
69 unary_potentials_are_valid_ =
false;
70 binary_potentials_are_valid_ =
false;
74 template <
typename Po
intT>
double
77 return (pow (1.0 / inverse_sigma_, 0.5));
81 template <
typename Po
intT>
void
86 inverse_sigma_ = 1.0 / (sigma * sigma);
87 binary_potentials_are_valid_ =
false;
92 template <
typename Po
intT>
double
95 return (pow (radius_, 0.5));
99 template <
typename Po
intT>
void
102 if (radius > epsilon_)
104 radius_ = radius * radius;
105 unary_potentials_are_valid_ =
false;
110 template <
typename Po
intT>
double
113 return (source_weight_);
117 template <
typename Po
intT>
void
120 if (weight > epsilon_)
122 source_weight_ = weight;
123 unary_potentials_are_valid_ =
false;
135 template <
typename Po
intT>
void
142 template <
typename Po
intT>
unsigned int
145 return (number_of_neighbours_);
149 template <
typename Po
intT>
void
152 if (number_of_neighbours_ != neighbour_number && neighbour_number != 0)
154 number_of_neighbours_ = neighbour_number;
155 graph_is_valid_ =
false;
156 unary_potentials_are_valid_ =
false;
157 binary_potentials_are_valid_ =
false;
162 template <
typename Po
intT> std::vector<PointT, Eigen::aligned_allocator<PointT> >
165 return (foreground_points_);
169 template <
typename Po
intT>
void
172 foreground_points_.clear ();
173 foreground_points_.insert(
174 foreground_points_.end(), foreground_points->
cbegin(), foreground_points->
cend());
176 unary_potentials_are_valid_ =
false;
180 template <
typename Po
intT> std::vector<PointT, Eigen::aligned_allocator<PointT> >
183 return (background_points_);
187 template <
typename Po
intT>
void
190 background_points_.clear ();
191 background_points_.insert(
192 background_points_.end(), background_points->
cbegin(), background_points->
cend());
194 unary_potentials_are_valid_ =
false;
198 template <
typename Po
intT>
void
203 bool segmentation_is_possible = initCompute ();
204 if ( !segmentation_is_possible )
210 if ( graph_is_valid_ && unary_potentials_are_valid_ && binary_potentials_are_valid_ )
212 clusters.reserve (clusters_.size ());
213 std::copy (clusters_.cbegin (), clusters_.cend (), std::back_inserter (clusters));
220 if ( !graph_is_valid_ )
222 bool success = buildGraph ();
228 graph_is_valid_ =
true;
229 unary_potentials_are_valid_ =
true;
230 binary_potentials_are_valid_ =
true;
233 if ( !unary_potentials_are_valid_ )
235 bool success = recalculateUnaryPotentials ();
241 unary_potentials_are_valid_ =
true;
244 if ( !binary_potentials_are_valid_ )
246 bool success = recalculateBinaryPotentials ();
252 binary_potentials_are_valid_ =
true;
256 ResidualCapacityMap residual_capacity = boost::get (boost::edge_residual_capacity, *graph_);
258 max_flow_ = boost::boykov_kolmogorov_max_flow (*graph_, source_, sink_);
260 assembleLabels (residual_capacity);
262 clusters.reserve (clusters_.size ());
263 std::copy (clusters_.cbegin (), clusters_.cend (), std::back_inserter (clusters));
269 template <
typename Po
intT>
double
283 template <
typename Po
intT>
bool
286 const auto number_of_points = input_->size ();
287 const auto number_of_indices = indices_->size ();
289 if (input_->points.empty () || number_of_points == 0 || foreground_points_.empty () ==
true )
298 search_->setInputCloud (input_, indices_);
301 graph_.reset (
new mGraph);
304 *capacity_ = boost::get (boost::edge_capacity, *graph_);
307 *reverse_edges_ = boost::get (boost::edge_reverse, *graph_);
311 vertices_.resize (number_of_points + 2, vertex_descriptor);
313 std::set<int> out_edges_marker;
314 edge_marker_.clear ();
315 edge_marker_.resize (number_of_points + 2, out_edges_marker);
317 for (std::size_t i_point = 0; i_point < number_of_points + 2; i_point++)
318 vertices_[i_point] = boost::add_vertex (*graph_);
320 source_ = vertices_[number_of_points];
321 sink_ = vertices_[number_of_points + 1];
323 for (
const auto& point_index : (*indices_))
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);
333 std::vector<float> distances;
334 for (std::size_t i_point = 0; i_point < number_of_indices; i_point++)
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++)
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);
352 template <
typename Po
intT>
void
355 double min_dist_to_foreground = std::numeric_limits<double>::max ();
358 double initial_point[] = {0.0, 0.0};
360 initial_point[0] = (*input_)[point].x;
361 initial_point[1] = (*input_)[point].y;
363 for (
const auto& fg_point : foreground_points_)
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)
370 min_dist_to_foreground = dist;
374 sink_weight = pow (min_dist_to_foreground / radius_, 0.5);
376 source_weight = source_weight_;
408 template <
typename Po
intT>
bool
411 auto iter_out = edge_marker_[source].find (target);
412 if ( iter_out != edge_marker_[source].end () )
417 bool edge_was_added, reverse_edge_was_added;
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 )
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);
434 template <
typename Po
intT>
double
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);
449 template <
typename Po
intT>
bool
454 std::pair<EdgeDescriptor, bool> sink_edge;
456 for (boost::tie (src_edge_iter, src_edge_end) = boost::out_edges (source_, *graph_); src_edge_iter != src_edge_end; ++src_edge_iter)
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)
466 (*capacity_)[*src_edge_iter] = source_weight;
467 (*capacity_)[sink_edge.first] = sink_weight;
474 template <
typename Po
intT>
bool
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);
487 for (boost::tie (vertex_iter, vertex_end) = boost::vertices (*graph_); vertex_iter != vertex_end; ++vertex_iter)
490 if (source_vertex == source_ || source_vertex == sink_)
492 for (boost::tie (edge_iter, edge_end) = boost::out_edges (source_vertex, *graph_); edge_iter != edge_end; ++edge_iter)
496 if ((*capacity_)[reverse_edge] != 0.0)
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 () )
505 if (target_vertex != source_ && target_vertex != sink_)
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);
519 template <
typename Po
intT>
void
522 std::vector<int> labels;
523 labels.resize (input_->size (), 0);
524 for (
const auto& i_point : (*indices_))
530 clusters_.resize (2, segment);
533 for ( boost::tie (edge_iter, edge_end) = boost::out_edges (source_, *graph_); edge_iter != edge_end; ++edge_iter )
535 if (labels[edge_iter->m_target] == 1)
537 if (residual_capacity[*edge_iter] > epsilon_)
538 clusters_[1].
indices.push_back (
static_cast<int> (edge_iter->m_target));
540 clusters_[0].indices.push_back (
static_cast<int> (edge_iter->m_target));
551 if (!clusters_.empty ())
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;
561 for (
const auto& point_index : (clusters_[0].indices))
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);
572 for (
const auto& point_index : (clusters_[1].indices))
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);
584 return (colored_cloud);
587 #define PCL_INSTANTIATE_MinCutSegmentation(T) template class PCL_EXPORTS pcl::MinCutSegmentation<T>;
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.
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.
typename PointCloud::ConstPtr PointCloudConstPtr
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.
float distance(const PointT &p1, const PointT &p2)
@ 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.
IndicesAllocator<> Indices
Type used for indices in PCL.
A point structure representing Euclidean xyz coordinates, and the RGB color.