Point Cloud Library (PCL)  1.15.1-dev
grabcut_segmentation.hpp
1 /*
2  * Software License Agreement (BSD License)
3  *
4  * Point Cloud Library (PCL) - www.pointclouds.org
5  * Copyright (c) 2012-, 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 Willow Garage, Inc. 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 #pragma once
39 
40 #include <pcl/common/distances.h>
41 #include <pcl/common/io.h> // for getFieldIndex
42 #include <pcl/common/point_tests.h> // for pcl::isFinite
43 #include <pcl/search/auto.h>
44 
45 
46 namespace pcl
47 {
48 
49 template <>
52 {
53  return ((c1.r-c2.r)*(c1.r-c2.r)+(c1.g-c2.g)*(c1.g-c2.g)+(c1.b-c2.b)*(c1.b-c2.b));
54 }
55 
56 namespace segmentation
57 {
58 
59 template <typename PointT>
61 {
62  r = static_cast<float> (p.r) / 255.0;
63  g = static_cast<float> (p.g) / 255.0;
64  b = static_cast<float> (p.b) / 255.0;
65 }
66 
67 template <typename PointT>
68 grabcut::Color::operator PointT () const
69 {
70  PointT p;
71  p.r = static_cast<std::uint32_t> (r * 255);
72  p.g = static_cast<std::uint32_t> (g * 255);
73  p.b = static_cast<std::uint32_t> (b * 255);
74  return (p);
75 }
76 
77 } // namespace segmentation
78 
79 template <typename PointT> void
81 {
82  input_ = cloud;
83 }
84 
85 template <typename PointT> bool
87 {
88  using namespace pcl::segmentation::grabcut;
90  {
91  PCL_ERROR ("[pcl::GrabCut::initCompute ()] Init failed!\n");
92  return (false);
93  }
94 
95  std::vector<pcl::PCLPointField> in_fields_;
96  if ((pcl::getFieldIndex<PointT> ("rgb", in_fields_) == -1) &&
97  (pcl::getFieldIndex<PointT> ("rgba", in_fields_) == -1))
98  {
99  PCL_ERROR ("[pcl::GrabCut::initCompute ()] No RGB data available, aborting!\n");
100  return (false);
101  }
102 
103  // Initialize the working image
104  image_.reset (new Image (input_->width, input_->height));
105  for (std::size_t i = 0; i < input_->size (); ++i)
106  {
107  (*image_) [i] = Color ((*input_)[i]);
108  }
109  width_ = image_->width;
110  height_ = image_->height;
111 
112  // Initialize the spatial locator
113  if (!tree_ && !input_->isOrganized ())
114  tree_.reset (pcl::search::autoSelectMethod<PointT> (input_, true, pcl::search::Purpose::many_knn_search));
115 
116  const std::size_t indices_size = indices_->size ();
117  trimap_ = std::vector<segmentation::grabcut::TrimapValue> (indices_size, TrimapUnknown);
118  hard_segmentation_ = std::vector<segmentation::grabcut::SegmentationValue> (indices_size,
120  GMM_component_.resize (indices_size);
121  n_links_.resize (indices_size);
122 
123  // soft_segmentation_ = 0; // Not yet implemented
124  foreground_GMM_.resize (K_);
125  background_GMM_.resize (K_);
126 
127  //set some constants
128  computeL ();
129 
130  if (image_->isOrganized ())
131  {
132  computeBetaOrganized ();
133  computeNLinksOrganized ();
134  }
135  else
136  {
137  computeBetaNonOrganized ();
138  computeNLinksNonOrganized ();
139  }
140 
141  initialized_ = false;
142  return (true);
143 }
144 
145 template <typename PointT> void
146 GrabCut<PointT>::addEdge (vertex_descriptor v1, vertex_descriptor v2, float capacity, float rev_capacity)
147 {
148  graph_.addEdge (v1, v2, capacity, rev_capacity);
149 }
150 
151 template <typename PointT> void
152 GrabCut<PointT>::setTerminalWeights (vertex_descriptor v, float source_capacity, float sink_capacity)
153 {
154  graph_.addSourceEdge (v, source_capacity);
155  graph_.addTargetEdge (v, sink_capacity);
156 }
157 
158 template <typename PointT> void
160 {
161  using namespace pcl::segmentation::grabcut;
162  if (!initCompute ())
163  return;
164 
165  std::fill (trimap_.begin (), trimap_.end (), TrimapBackground);
166  std::fill (hard_segmentation_.begin (), hard_segmentation_.end (), SegmentationBackground);
167  for (const auto &index : indices->indices)
168  {
169  trimap_[index] = TrimapUnknown;
170  hard_segmentation_[index] = SegmentationForeground;
171  }
172 
173  if (!initialized_)
174  {
175  fitGMMs ();
176  initialized_ = true;
177  }
178 }
179 
180 template <typename PointT> void
182 {
183  // Step 3: Build GMMs using Orchard-Bouman clustering algorithm
184  buildGMMs (*image_, *indices_, hard_segmentation_, GMM_component_, background_GMM_, foreground_GMM_);
185 
186  // Initialize the graph for graphcut (do this here so that the T-Link debugging image will be initialized)
187  initGraph ();
188 }
189 
190 template <typename PointT> int
192 {
193  // Steps 4 and 5: Learn new GMMs from current segmentation
194  learnGMMs (*image_, *indices_, hard_segmentation_, GMM_component_, background_GMM_, foreground_GMM_);
195 
196  // Step 6: Run GraphCut and update segmentation
197  initGraph ();
198 
199  float flow = graph_.solve ();
200 
201  int changed = updateHardSegmentation ();
202  PCL_INFO ("%d pixels changed segmentation (max flow = %f)\n", changed, flow);
203 
204  return (changed);
205 }
206 
207 template <typename PointT> void
209 {
210  std::size_t changed = indices_->size ();
211 
212  while (changed)
213  changed = refineOnce ();
214 }
215 
216 template <typename PointT> int
218 {
219  using namespace pcl::segmentation::grabcut;
220 
221  int changed = 0;
222 
223  const int number_of_indices = static_cast<int> (indices_->size ());
224  for (int i_point = 0; i_point < number_of_indices; ++i_point)
225  {
226  SegmentationValue old_value = hard_segmentation_ [i_point];
227 
228  if (trimap_ [i_point] == TrimapBackground)
229  hard_segmentation_ [i_point] = SegmentationBackground;
230  else
231  if (trimap_ [i_point] == TrimapForeground)
232  hard_segmentation_ [i_point] = SegmentationForeground;
233  else // TrimapUnknown
234  {
235  if (isSource (graph_nodes_[i_point]))
236  hard_segmentation_ [i_point] = SegmentationForeground;
237  else
238  hard_segmentation_ [i_point] = SegmentationBackground;
239  }
240 
241  if (old_value != hard_segmentation_ [i_point])
242  ++changed;
243  }
244  return (changed);
245 }
246 
247 template <typename PointT> void
249 {
250  using namespace pcl::segmentation::grabcut;
251  for (const auto &index : indices->indices)
252  trimap_[index] = t;
253 
254  // Immediately set the hard segmentation as well so that the display will update.
255  if (t == TrimapForeground)
256  for (const auto &index : indices->indices)
257  hard_segmentation_[index] = SegmentationForeground;
258  else
259  if (t == TrimapBackground)
260  for (const auto &index : indices->indices)
261  hard_segmentation_[index] = SegmentationBackground;
262 }
263 
264 template <typename PointT> void
266 {
267  using namespace pcl::segmentation::grabcut;
268  const int number_of_indices = static_cast<int> (indices_->size ());
269  // Set up the graph (it can only be used once, so we have to recreate it each time the graph is updated)
270  graph_.clear ();
271  graph_nodes_.clear ();
272  graph_nodes_.resize (indices_->size ());
273  int start = graph_.addNodes (indices_->size ());
274  for (std::size_t idx = 0; idx < indices_->size (); ++idx)
275  {
276  graph_nodes_[idx] = start;
277  ++start;
278  }
279 
280  // Set T-Link weights
281  for (int i_point = 0; i_point < number_of_indices; ++i_point)
282  {
283  int point_index = (*indices_) [i_point];
284  float back, fore;
285 
286  switch (trimap_[point_index])
287  {
288  case TrimapUnknown :
289  {
290  fore = static_cast<float> (-std::log (background_GMM_.probabilityDensity ((*image_)[point_index])));
291  back = static_cast<float> (-std::log (foreground_GMM_.probabilityDensity ((*image_)[point_index])));
292  break;
293  }
294  case TrimapBackground :
295  {
296  fore = 0;
297  back = L_;
298  break;
299  }
300  default :
301  {
302  fore = L_;
303  back = 0;
304  }
305  }
306 
307  setTerminalWeights (graph_nodes_[i_point], fore, back);
308  }
309 
310  // Set N-Link weights from precomputed values
311  for (int i_point = 0; i_point < number_of_indices; ++i_point)
312  {
313  const NLinks &n_link = n_links_[i_point];
314  if (n_link.nb_links > 0)
315  {
316  const auto point_index = (*indices_) [i_point];
317  auto weights_it = n_link.weights.begin ();
318  for (auto indices_it = n_link.indices.cbegin (); indices_it != n_link.indices.cend (); ++indices_it, ++weights_it)
319  {
320  if ((*indices_it != point_index) && (*indices_it != UNAVAILABLE))
321  {
322  addEdge (graph_nodes_[i_point], graph_nodes_[*indices_it], *weights_it, *weights_it);
323  }
324  }
325  }
326  }
327 }
328 
329 template <typename PointT> void
331 {
332  const int number_of_indices = static_cast<int> (indices_->size ());
333  for (int i_point = 0; i_point < number_of_indices; ++i_point)
334  {
335  NLinks &n_link = n_links_[i_point];
336  if (n_link.nb_links > 0)
337  {
338  const auto point_index = (*indices_) [i_point];
339  auto dists_it = n_link.dists.cbegin ();
340  auto weights_it = n_link.weights.begin ();
341  for (auto indices_it = n_link.indices.cbegin (); indices_it != n_link.indices.cend (); ++indices_it, ++dists_it, ++weights_it)
342  {
343  if (*indices_it != point_index)
344  {
345  // We saved the color distance previously at the computeBeta stage for optimization purpose
346  float color_distance = *weights_it;
347  // Set the real weight
348  *weights_it = static_cast<float> (lambda_ * std::exp (-beta_ * color_distance) / sqrt (*dists_it));
349  }
350  }
351  }
352  }
353 }
354 
355 template <typename PointT> void
357 {
358  for( unsigned int y = 0; y < image_->height; ++y )
359  {
360  for( unsigned int x = 0; x < image_->width; ++x )
361  {
362  // We saved the color and euclidean distance previously at the computeBeta stage for
363  // optimization purpose but here we compute the real weight
364  std::size_t point_index = y * input_->width + x;
365  NLinks &links = n_links_[point_index];
366 
367  if( x > 0 && y < image_->height-1 )
368  links.weights[0] = lambda_ * std::exp (-beta_ * links.weights[0]) / links.dists[0];
369 
370  if( y < image_->height-1 )
371  links.weights[1] = lambda_ * std::exp (-beta_ * links.weights[1]) / links.dists[1];
372 
373  if( x < image_->width-1 && y < image_->height-1 )
374  links.weights[2] = lambda_ * std::exp (-beta_ * links.weights[2]) / links.dists[2];
375 
376  if( x < image_->width-1 )
377  links.weights[3] = lambda_ * std::exp (-beta_ * links.weights[3]) / links.dists[3];
378  }
379  }
380 }
381 
382 template <typename PointT> void
384 {
385  float result = 0;
386  std::size_t edges = 0;
387 
388  const int number_of_indices = static_cast<int> (indices_->size ());
389 
390  for (int i_point = 0; i_point < number_of_indices; i_point++)
391  {
392  const auto point_index = (*indices_)[i_point];
393  const PointT& point = input_->points [point_index];
394  if (pcl::isFinite (point))
395  {
396  NLinks &links = n_links_[i_point];
397  int found = tree_->nearestKSearch (point, nb_neighbours_, links.indices, links.dists);
398  if (found > 1)
399  {
400  links.nb_links = found - 1;
401  links.weights.reserve (links.nb_links);
402  for (const auto& nn_index : links.indices)
403  {
404  if (nn_index != point_index)
405  {
406  float color_distance = squaredEuclideanDistance ((*image_)[point_index], (*image_)[nn_index]);
407  links.weights.push_back (color_distance);
408  result+= color_distance;
409  ++edges;
410  }
411  else
412  links.weights.push_back (0.f);
413  }
414  }
415  }
416  }
417 
418  beta_ = 1e5 / (2*result / edges);
419 }
420 
421 template <typename PointT> void
423 {
424  float result = 0;
425  std::size_t edges = 0;
426 
427  for (unsigned int y = 0; y < input_->height; ++y)
428  {
429  for (unsigned int x = 0; x < input_->width; ++x)
430  {
431  std::size_t point_index = y * input_->width + x;
432  NLinks &links = n_links_[point_index];
433  links.nb_links = 4;
434  links.weights.resize (links.nb_links, 0);
435  links.dists.resize (links.nb_links, 0);
436  links.indices.resize (links.nb_links, -1);
437 
438  if (x > 0 && y < input_->height-1)
439  {
440  std::size_t upleft = (y+1) * input_->width + x - 1;
441  links.indices[0] = upleft;
442  links.dists[0] = std::sqrt (2.f);
443  float color_dist = squaredEuclideanDistance ((*image_)[point_index],
444  (*image_)[upleft]);
445  links.weights[0] = color_dist;
446  result+= color_dist;
447  edges++;
448  }
449 
450  if (y < input_->height-1)
451  {
452  std::size_t up = (y+1) * input_->width + x;
453  links.indices[1] = up;
454  links.dists[1] = 1;
455  float color_dist = squaredEuclideanDistance ((*image_)[point_index],
456  (*image_)[up]);
457  links.weights[1] = color_dist;
458  result+= color_dist;
459  edges++;
460  }
461 
462  if (x < input_->width-1 && y < input_->height-1)
463  {
464  std::size_t upright = (y+1) * input_->width + x + 1;
465  links.indices[2] = upright;
466  links.dists[2] = std::sqrt (2.f);
467  float color_dist = squaredEuclideanDistance ((*image_)[point_index],
468  image_->points [upright]);
469  links.weights[2] = color_dist;
470  result+= color_dist;
471  edges++;
472  }
473 
474  if (x < input_->width-1)
475  {
476  std::size_t right = y * input_->width + x + 1;
477  links.indices[3] = right;
478  links.dists[3] = 1;
479  float color_dist = squaredEuclideanDistance ((*image_)[point_index],
480  (*image_)[right]);
481  links.weights[3] = color_dist;
482  result+= color_dist;
483  edges++;
484  }
485  }
486  }
487 
488  beta_ = 1e5 / (2*result / edges);
489 }
490 
491 template <typename PointT> void
493 {
494  L_ = 8*lambda_ + 1;
495 }
496 
497 template <typename PointT> void
498 GrabCut<PointT>::extract (std::vector<pcl::PointIndices>& clusters)
499 {
500  using namespace pcl::segmentation::grabcut;
501  clusters.clear ();
502  clusters.resize (2);
503  clusters[0].indices.reserve (indices_->size ());
504  clusters[1].indices.reserve (indices_->size ());
505  refine ();
506  assert (hard_segmentation_.size () == indices_->size ());
507  const int indices_size = static_cast<int> (indices_->size ());
508  for (int i = 0; i < indices_size; ++i)
509  if (hard_segmentation_[i] == SegmentationForeground)
510  clusters[1].indices.push_back (i);
511  else
512  clusters[0].indices.push_back (i);
513 }
514 
515 } // namespace pcl
516 
void computeL()
Compute L parameter from given lambda.
void addEdge(vertex_descriptor v1, vertex_descriptor v2, float capacity, float rev_capacity)
Add an edge to the graph, graph must be oriented so we add the edge and its reverse.
virtual void fitGMMs()
Fit Gaussian Multi Models.
void computeNLinksNonOrganized()
Compute NLinks from cloud.
virtual void refine()
Run Grabcut refinement on the hard segmentation.
void setTrimap(const PointIndicesConstPtr &indices, segmentation::grabcut::TrimapValue t)
Edit Trimap.
pcl::segmentation::grabcut::BoykovKolmogorov::vertex_descriptor vertex_descriptor
void extract(std::vector< pcl::PointIndices > &clusters)
This method launches the segmentation algorithm and returns the clusters that were obtained during th...
void initGraph()
Build the graph for GraphCut.
void computeBetaOrganized()
Compute beta from image.
void computeNLinksOrganized()
Compute NLinks from image.
void setTerminalWeights(vertex_descriptor v, float source_capacity, float sink_capacity)
Set the weights of SOURCE --> v and v --> SINK.
virtual int refineOnce()
void setBackgroundPointsIndices(int x1, int y1, int x2, int y2)
Set background indices, foreground indices = indices \ background indices.
void setInputCloud(const PointCloudConstPtr &cloud) override
Provide a pointer to the input dataset.
void computeBetaNonOrganized()
Compute beta from cloud.
PCL base class.
Definition: pcl_base.h:70
typename PointCloud::ConstPtr PointCloudConstPtr
Definition: pcl_base.h:74
PointIndices::ConstPtr PointIndicesConstPtr
Definition: pcl_base.h:77
PointCloud represents the base class in PCL for storing collections of 3D points.
Definition: point_cloud.h:174
Define standard C methods to do distance calculations.
@ many_knn_search
The search method will mainly be used for nearestKSearch where k is larger than 1.
TrimapValue
User supplied Trimap values.
PCL_EXPORTS void buildGMMs(const Image &image, const Indices &indices, const std::vector< SegmentationValue > &hardSegmentation, std::vector< std::size_t > &components, GMM &background_GMM, GMM &foreground_GMM)
Build the initial GMMs using the Orchard and Bouman color clustering algorithm.
SegmentationValue
Grabcut derived hard segmentation values.
PCL_EXPORTS void learnGMMs(const Image &image, const Indices &indices, const std::vector< SegmentationValue > &hard_segmentation, std::vector< std::size_t > &components, GMM &background_GMM, GMM &foreground_GMM)
Iteratively learn GMMs using GrabCut updating algorithm.
float squaredEuclideanDistance(const PointType1 &p1, const PointType2 &p2)
Calculate the squared euclidean distance between the two given points.
Definition: distances.h:182
bool isFinite(const PointT &pt)
Tests if the 3D components of a point are all finite param[in] pt point to be tested return true if f...
Definition: point_tests.h:55
static constexpr index_t UNAVAILABLE
Definition: pcl_base.h:62
A point structure representing Euclidean xyz coordinates, and the RGB color.
Structure to save RGB colors into floats.