Point Cloud Library (PCL) 1.15.1-dev
Loading...
Searching...
No Matches
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
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
46namespace pcl
47{
48
49template <>
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
56namespace segmentation
57{
58
59template <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
67template <typename PointT>
68grabcut::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
79template <typename PointT> void
81{
82 input_ = cloud;
83}
84
85template <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,
119 SegmentationBackground);
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
145template <typename PointT> void
146GrabCut<PointT>::addEdge (vertex_descriptor v1, vertex_descriptor v2, float capacity, float rev_capacity)
147{
148 graph_.addEdge (v1, v2, capacity, rev_capacity);
149}
150
151template <typename PointT> void
152GrabCut<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
158template <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
180template <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
190template <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
207template <typename PointT> void
209{
210 std::size_t changed = indices_->size ();
211
212 while (changed)
213 changed = refineOnce ();
214}
215
216template <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
247template <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
264template <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
329template <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
355template <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
382template <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
421template <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
491template <typename PointT> void
493{
494 L_ = 8*lambda_ + 1;
495}
496
497template <typename PointT> void
498GrabCut<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.
typename PCLBase< PointT >::PointCloudConstPtr PointCloudConstPtr
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
PointIndices::ConstPtr PointIndicesConstPtr
Definition pcl_base.h:77
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.
SegmentationValue
Grabcut derived hard segmentation values.
pcl::PointCloud< Color > Image
An Image is a point cloud of Color.
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:56
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.