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 * $Id$
37 *
38 */
39
40 #include <pcl/segmentation/grabcut_segmentation.h>
41
42 #include <cassert>
43 #include <vector>
44 #include <map>
45 #include <algorithm>
46
47 const int pcl::segmentation::grabcut::BoykovKolmogorov::TERMINAL = -1;
48
BoykovKolmogorov(std::size_t max_nodes)49 pcl::segmentation::grabcut::BoykovKolmogorov::BoykovKolmogorov (std::size_t max_nodes)
50 : flow_value_(0.0)
51 {
52 if (max_nodes > 0)
53 {
54 source_edges_.reserve (max_nodes);
55 target_edges_.reserve (max_nodes);
56 nodes_.reserve (max_nodes);
57 }
58 }
59
60 double
operator ()(int u,int v) const61 pcl::segmentation::grabcut::BoykovKolmogorov::operator() (int u, int v) const
62 {
63 if ((u < 0) && (v < 0)) return flow_value_;
64 if (u < 0) { return source_edges_[v]; }
65 if (v < 0) { return target_edges_[u]; }
66 capacitated_edge::const_iterator it = nodes_[u].find (v);
67 if (it == nodes_[u].end ()) return 0.0;
68 return it->second;
69 }
70
71 double
getSourceEdgeCapacity(int u) const72 pcl::segmentation::grabcut::BoykovKolmogorov::getSourceEdgeCapacity (int u) const
73 {
74 return (source_edges_[u]);
75 }
76
77 double
getTargetEdgeCapacity(int u) const78 pcl::segmentation::grabcut::BoykovKolmogorov::getTargetEdgeCapacity (int u) const
79 {
80 return (target_edges_[u]);
81 }
82
83 void
preAugmentPaths()84 pcl::segmentation::grabcut::BoykovKolmogorov::preAugmentPaths ()
85 {
86 for (int u = 0; u < (int)nodes_.size (); u++)
87 {
88 // augment s-u-t paths
89 if ((source_edges_[u] > 0.0) && (target_edges_[u] > 0.0))
90 {
91 const double cap = std::min (source_edges_[u], target_edges_[u]);
92 flow_value_ += cap;
93 source_edges_[u] -= cap;
94 target_edges_[u] -= cap;
95 }
96
97 if (source_edges_[u] == 0.0) continue;
98
99 // augment s-u-v-t paths
100 for (std::map<int, double>::iterator it = nodes_[u].begin (); it != nodes_[u].end (); ++it)
101 {
102 const int v = it->first;
103 if ((it->second == 0.0) || (target_edges_[v] == 0.0)) continue;
104 const double w = std::min (it->second, std::min (source_edges_[u], target_edges_[v]));
105 source_edges_[u] -= w;
106 target_edges_[v] -= w;
107 it->second -= w;
108 nodes_[v][u] += w;
109 flow_value_ += w;
110 if (source_edges_[u] == 0.0) break;
111 }
112 }
113 }
114
115 int
addNodes(std::size_t n)116 pcl::segmentation::grabcut::BoykovKolmogorov::addNodes (std::size_t n)
117 {
118 int node_id = (int)nodes_.size ();
119 nodes_.resize (nodes_.size () + n);
120 source_edges_.resize (nodes_.size (), 0.0);
121 target_edges_.resize (nodes_.size (), 0.0);
122 return (node_id);
123 }
124
125 // void
126 // pcl::segmentation::grabcut::BoykovKolmogorov::addSourceAndTargetEdges (int u, double source_cap, double sink_cap)
127 // {
128 // addSourceEdge (u, source_cap);
129 // addTargetEdge (u, sink_cap);
130
131 // }
132
133 void
addSourceEdge(int u,double cap)134 pcl::segmentation::grabcut::BoykovKolmogorov::addSourceEdge (int u, double cap)
135 {
136 assert ((u >= 0) && (u < (int)nodes_.size ()));
137 if (cap < 0.0)
138 {
139 flow_value_ += cap;
140 target_edges_[u] -= cap;
141 }
142 else
143 source_edges_[u] += cap;
144 }
145
146 void
addTargetEdge(int u,double cap)147 pcl::segmentation::grabcut::BoykovKolmogorov::addTargetEdge (int u, double cap)
148 {
149 assert ((u >= 0) && (u < (int)nodes_.size ()));
150 if (cap < 0.0)
151 {
152 flow_value_ += cap;
153 source_edges_[u] -= cap;
154 }
155 else
156 target_edges_[u] += cap;
157 }
158
159 void
addEdge(int u,int v,double cap_uv,double cap_vu)160 pcl::segmentation::grabcut::BoykovKolmogorov::addEdge (int u, int v, double cap_uv, double cap_vu)
161 {
162 assert ((u >= 0) && (u < (int)nodes_.size ()));
163 assert ((v >= 0) && (v < (int)nodes_.size ()));
164 assert (u != v);
165
166 capacitated_edge::iterator it = nodes_[u].find (v);
167 if (it == nodes_[u].end ())
168 {
169 assert (cap_uv + cap_vu >= 0.0);
170 if (cap_uv < 0.0)
171 {
172 nodes_[u].insert (std::make_pair (v, 0.0));
173 nodes_[v].insert (std::make_pair (u, cap_vu + cap_uv));
174 source_edges_[u] -= cap_uv;
175 target_edges_[v] -= cap_uv;
176 flow_value_ += cap_uv;
177 }
178 else
179 {
180 if (cap_vu < 0.0)
181 {
182 nodes_[u].insert (std::make_pair (v, cap_uv + cap_vu));
183 nodes_[v].insert (std::make_pair (u, 0.0));
184 source_edges_[v] -= cap_vu;
185 target_edges_[u] -= cap_vu;
186 flow_value_ += cap_vu;
187 }
188 else
189 {
190 nodes_[u].insert (std::make_pair (v, cap_uv));
191 nodes_[v].insert (std::make_pair (u, cap_vu));
192 }
193 }
194 }
195 else
196 {
197 capacitated_edge::iterator jt = nodes_[v].find (u);
198 it->second += cap_uv;
199 jt->second += cap_vu;
200 assert (it->second + jt->second >= 0.0);
201 if (it->second < 0.0)
202 {
203 jt->second += it->second;
204 source_edges_[u] -= it->second;
205 target_edges_[v] -= it->second;
206 flow_value_ += it->second;
207 it->second = 0.0;
208 }
209 else
210 {
211 if (jt->second < 0.0)
212 {
213 it->second += jt->second;
214 source_edges_[v] -= jt->second;
215 target_edges_[u] -= jt->second;
216 flow_value_ += jt->second;
217 jt->second = 0.0;
218 }
219 }
220 }
221 }
222
223 void
reset()224 pcl::segmentation::grabcut::BoykovKolmogorov::reset ()
225 {
226 flow_value_ = 0.0;
227 std::fill (source_edges_.begin (), source_edges_.end (), 0.0);
228 std::fill (target_edges_.begin (), target_edges_.end (), 0.0);
229 for (auto &node : nodes_)
230 {
231 for (auto &edge : node)
232 {
233 edge.second = 0.0;
234 }
235 }
236 std::fill (cut_.begin (), cut_.end (), FREE);
237 parents_.clear ();
238 clearActive ();
239 }
240
241 void
clear()242 pcl::segmentation::grabcut::BoykovKolmogorov::clear ()
243 {
244 flow_value_ = 0.0;
245 source_edges_.clear ();
246 target_edges_.clear ();
247 nodes_.clear ();
248 cut_.clear ();
249 parents_.clear ();
250 clearActive ();
251 }
252
253 double
solve()254 pcl::segmentation::grabcut::BoykovKolmogorov::solve ()
255 {
256 // initialize search tree and active set
257 cut_.resize (nodes_.size ());
258 std::fill (cut_.begin (), cut_.end (), FREE);
259 parents_.resize (nodes_.size ());
260
261 clearActive ();
262
263 // pre-augment paths
264 preAugmentPaths ();
265
266 // initialize search trees
267 initializeTrees ();
268
269 std::deque<int> orphans;
270 while (!isActiveSetEmpty ())
271 {
272 const std::pair<int, int> path = expandTrees ();
273 augmentPath (path, orphans);
274 if (!orphans.empty ())
275 {
276 adoptOrphans (orphans);
277 }
278 }
279 return (flow_value_);
280 }
281
282 void
initializeTrees()283 pcl::segmentation::grabcut::BoykovKolmogorov::initializeTrees ()
284 {
285 // initialize search tree
286 for (int u = 0; u < (int)nodes_.size (); u++)
287 {
288 if (source_edges_[u] > 0.0)
289 {
290 cut_[u] = SOURCE;
291 parents_[u].first = TERMINAL;
292 markActive (u);
293 }
294 else
295 {
296 if (target_edges_[u] > 0.0)
297 {
298 cut_[u] = TARGET;
299 parents_[u].first = TERMINAL;
300 markActive (u);
301 }
302 }
303 }
304 }
305
306 std::pair<int, int>
expandTrees()307 pcl::segmentation::grabcut::BoykovKolmogorov::expandTrees ()
308 {
309 // expand trees looking for augmenting paths
310 while (!isActiveSetEmpty ())
311 {
312 const int u = active_head_;
313
314 if (cut_[u] == SOURCE) {
315 for (capacitated_edge::iterator it = nodes_[u].begin (); it != nodes_[u].end (); ++it)
316 {
317 if (it->second > 0.0)
318 {
319 if (cut_[it->first] == FREE)
320 {
321 cut_[it->first] = SOURCE;
322 parents_[it->first] = std::make_pair (u, std::make_pair (it, nodes_[it->first].find (u)));
323 markActive (it->first);
324 }
325 else
326 {
327 if (cut_[it->first] == TARGET)
328 {
329 // found augmenting path
330 return (std::make_pair (u, it->first));
331 }
332 }
333 }
334 }
335 }
336 else
337 {
338 for (capacitated_edge::iterator it = nodes_[u].begin (); it != nodes_[u].end (); ++it)
339 {
340 if (cut_[it->first] == TARGET) continue;
341 if (nodes_[it->first][u] > 0.0)
342 {
343 if (cut_[it->first] == FREE)
344 {
345 cut_[it->first] = TARGET;
346 parents_[it->first] = std::make_pair (u, std::make_pair (nodes_[it->first].find (u), it));
347 markActive (it->first);
348 }
349 else
350 {
351 if (cut_[it->first] == SOURCE)
352 {
353 // found augmenting path
354 return (std::make_pair (it->first, u));
355 }
356 }
357 }
358 }
359 }
360
361 // remove node from active set
362 markInactive (u);
363 }
364
365 return (std::make_pair (TERMINAL, TERMINAL));
366 }
367
368 void
augmentPath(const std::pair<int,int> & path,std::deque<int> & orphans)369 pcl::segmentation::grabcut::BoykovKolmogorov::augmentPath (const std::pair<int, int>& path, std::deque<int>& orphans)
370 {
371 if ((path.first == TERMINAL) && (path.second == TERMINAL))
372 return;
373
374 // find path capacity
375
376 // backtrack
377 const edge_pair e = std::make_pair (nodes_[path.first].find (path.second),
378 nodes_[path.second].find (path.first));
379 double c = e.first->second;
380 int u = path.first;
381 while (parents_[u].first != TERMINAL)
382 {
383 c = std::min (c, parents_[u].second.first->second);
384 u = parents_[u].first;
385 //assert (cut_[u] == SOURCE);
386 }
387 c = std::min (c, source_edges_[u]);
388
389 // forward track
390 u = path.second;
391 while (parents_[u].first != TERMINAL)
392 {
393 c = std::min (c, parents_[u].second.first->second);
394 u = parents_[u].first;
395 //assert (cut_[u] == TARGET);
396 }
397 c = std::min (c, target_edges_[u]);
398
399 // augment path
400 flow_value_ += c;
401 //DRWN_LOG_DEBUG ("path capacity: " << c);
402
403 // backtrack
404 u = path.first;
405 while (parents_[u].first != TERMINAL)
406 {
407 //nodes_[u][parents_[u].first] += c;
408 parents_[u].second.second->second += c;
409 parents_[u].second.first->second -= c;
410 if (parents_[u].second.first->second == 0.0)
411 {
412 orphans.push_front (u);
413 }
414 u = parents_[u].first;
415 }
416 source_edges_[u] -= c;
417 if (source_edges_[u] == 0.0)
418 {
419 orphans.push_front (u);
420 }
421
422 // link
423 e.first->second -= c;
424 e.second->second += c;
425
426 // forward track
427 u = path.second;
428 while (parents_[u].first != TERMINAL) {
429 //nodes_[parents_[u].first][u] += c;
430 parents_[u].second.second->second += c;
431 parents_[u].second.first->second -= c;
432 if (parents_[u].second.first->second == 0.0) {
433 orphans.push_back (u);
434 }
435 u = parents_[u].first;
436 }
437 target_edges_[u] -= c;
438 if (target_edges_[u] == 0.0) {
439 orphans.push_back (u);
440 }
441 }
442
443 void
adoptOrphans(std::deque<int> & orphans)444 pcl::segmentation::grabcut::BoykovKolmogorov::adoptOrphans (std::deque<int>& orphans)
445 {
446 // find new parent for orphaned subtree or free it
447 while (!orphans.empty ())
448 {
449 const int u = orphans.front ();
450 const char tree_label = cut_[u];
451 orphans.pop_front ();
452
453 // can occur if same node is inserted into orphans multiple times
454 if (tree_label == FREE) continue;
455 //assert (tree_label != FREE);
456
457 // look for new parent
458 bool b_free_orphan = true;
459 for (capacitated_edge::iterator jt = nodes_[u].begin (); jt != nodes_[u].end (); ++jt) {
460 // skip if different trees
461 if (cut_[jt->first] != tree_label) continue;
462
463 // check edge capacity
464 const capacitated_edge::iterator kt = nodes_[jt->first].find (u);
465 if (((tree_label == TARGET) && (jt->second <= 0.0)) ||
466 ((tree_label == SOURCE) && (kt->second <= 0.0)))
467 continue;
468
469 // check that u is not an ancestor of jt->first
470 int v = jt->first;
471 while ((v != u) && (v != TERMINAL))
472 {
473 v = parents_[v].first;
474 }
475 if (v != TERMINAL) continue;
476
477 // add as parent
478 const edge_pair e = (tree_label == SOURCE) ? std::make_pair (kt, jt) : std::make_pair (jt, kt);
479 parents_[u] = std::make_pair (jt->first, e);
480 b_free_orphan = false;
481 break;
482 }
483
484 // free the orphan subtree and remove it from the active set
485 if (b_free_orphan)
486 {
487 for (capacitated_edge::const_iterator jt = nodes_[u].begin (); jt != nodes_[u].end (); ++jt)
488 {
489 if ((cut_[jt->first] == tree_label) && (parents_[jt->first].first == u))
490 {
491 orphans.push_front (jt->first);
492 markActive (jt->first);
493 }
494 else if (cut_[jt->first] != FREE)
495 {
496 markActive (jt->first);
497 }
498 }
499
500 // mark inactive and free
501 markInactive (u);
502 cut_[u] = FREE;
503 }
504 }
505 }
506
507 void
clearActive()508 pcl::segmentation::grabcut::BoykovKolmogorov::clearActive ()
509 {
510 active_head_ = active_tail_ = TERMINAL;
511 active_list_.resize (nodes_.size ());
512 std::fill (active_list_.begin (), active_list_.end (), std::make_pair (TERMINAL, TERMINAL));
513 }
514
515 void
markActive(int u)516 pcl::segmentation::grabcut::BoykovKolmogorov::markActive (int u)
517 {
518 if (isActive (u)) return;
519
520 active_list_[u].first = active_tail_;
521 active_list_[u].second = TERMINAL;
522 if (active_tail_ == TERMINAL)
523 active_head_ = u;
524 else
525 active_list_[active_tail_].second = u;
526 active_tail_ = u;
527 }
528
529 void
markInactive(int u)530 pcl::segmentation::grabcut::BoykovKolmogorov::markInactive (int u)
531 {
532 //if (!isActive (u)) return;
533
534 if (u == active_head_)
535 {
536 active_head_ = active_list_[u].second;
537 if (u != active_tail_)
538 {
539 active_list_[active_list_[u].second].first = TERMINAL;
540 }
541 }
542 else
543 if (u == active_tail_)
544 {
545 active_tail_ = active_list_[u].first;
546 active_list_[active_list_[u].first].second = TERMINAL;
547 }
548 else
549 if (active_list_[u].first != TERMINAL)
550 {
551 active_list_[active_list_[u].first].second = active_list_[u].second;
552 active_list_[active_list_[u].second].first = active_list_[u].first;
553 }
554 //active_list_[u] = std::make_pair (TERMINAL, TERMINAL);
555 active_list_[u].first = TERMINAL;
556 }
557
558 void
add(const Color & c)559 pcl::segmentation::grabcut::GaussianFitter::add (const Color &c)
560 {
561 sum_[0] += c.r; sum_[1] += c.g; sum_[2] += c.b;
562 accumulator_ (0,0) += c.r*c.r; accumulator_ (0,1) += c.r*c.g; accumulator_ (0,2) += c.r*c.b;
563 accumulator_ (1,0) += c.g*c.r; accumulator_ (1,1) += c.g*c.g; accumulator_ (1,2) += c.g*c.b;
564 accumulator_ (2,0) += c.b*c.r; accumulator_ (2,1) += c.b*c.g; accumulator_ (2,2) += c.b*c.b;
565
566 ++count_;
567 }
568
569 // Build the gaussian out of all the added colors
570 void
fit(Gaussian & g,std::size_t total_count,bool compute_eigens) const571 pcl::segmentation::grabcut::GaussianFitter::fit (Gaussian& g, std::size_t total_count, bool compute_eigens) const
572 {
573 if (count_==0)
574 {
575 g.pi = 0;
576 }
577 else
578 {
579 const float count_f = static_cast<float> (count_);
580
581 // Compute mean of gaussian
582 g.mu.r = sum_[0]/count_f;
583 g.mu.g = sum_[1]/count_f;
584 g.mu.b = sum_[2]/count_f;
585
586 // Compute covariance matrix
587 g.covariance (0,0) = accumulator_ (0,0)/count_f - g.mu.r*g.mu.r + epsilon_;
588 g.covariance (0,1) = accumulator_ (0,1)/count_f - g.mu.r*g.mu.g;
589 g.covariance (0,2) = accumulator_ (0,2)/count_f - g.mu.r*g.mu.b;
590 g.covariance (1,0) = accumulator_ (1,0)/count_f - g.mu.g*g.mu.r;
591 g.covariance (1,1) = accumulator_ (1,1)/count_f - g.mu.g*g.mu.g + epsilon_;
592 g.covariance (1,2) = accumulator_ (1,2)/count_f - g.mu.g*g.mu.b;
593 g.covariance (2,0) = accumulator_ (2,0)/count_f - g.mu.b*g.mu.r;
594 g.covariance (2,1) = accumulator_ (2,1)/count_f - g.mu.b*g.mu.g;
595 g.covariance (2,2) = accumulator_ (2,2)/count_f - g.mu.b*g.mu.b + epsilon_;
596
597 // Compute determinant of covariance matrix
598 g.determinant = g.covariance (0,0)*(g.covariance (1,1)*g.covariance (2,2) - g.covariance (1,2)*g.covariance (2,1))
599 - g.covariance (0,1)*(g.covariance (1,0)*g.covariance (2,2) - g.covariance (1,2)*g.covariance (2,0))
600 + g.covariance (0,2)*(g.covariance (1,0)*g.covariance (2,1) - g.covariance (1,1)*g.covariance (2,0));
601
602 // Compute inverse (cofactor matrix divided by determinant)
603 g.inverse (0,0) = (g.covariance (1,1)*g.covariance (2,2) - g.covariance (1,2)*g.covariance (2,1)) / g.determinant;
604 g.inverse (1,0) = -(g.covariance (1,0)*g.covariance (2,2) - g.covariance (1,2)*g.covariance (2,0)) / g.determinant;
605 g.inverse (2,0) = (g.covariance (1,0)*g.covariance (2,1) - g.covariance (1,1)*g.covariance (2,0)) / g.determinant;
606 g.inverse (0,1) = -(g.covariance (0,1)*g.covariance (2,2) - g.covariance (0,2)*g.covariance (2,1)) / g.determinant;
607 g.inverse (1,1) = (g.covariance (0,0)*g.covariance (2,2) - g.covariance (0,2)*g.covariance (2,0)) / g.determinant;
608 g.inverse (2,1) = -(g.covariance (0,0)*g.covariance (2,1) - g.covariance (0,1)*g.covariance (2,0)) / g.determinant;
609 g.inverse (0,2) = (g.covariance (0,1)*g.covariance (1,2) - g.covariance (0,2)*g.covariance (1,1)) / g.determinant;
610 g.inverse (1,2) = -(g.covariance (0,0)*g.covariance (1,2) - g.covariance (0,2)*g.covariance (1,0)) / g.determinant;
611 g.inverse (2,2) = (g.covariance (0,0)*g.covariance (1,1) - g.covariance (0,1)*g.covariance (1,0)) / g.determinant;
612
613 // The weight of the gaussian is the fraction of the number of pixels in this Gaussian to the number
614 // of pixels in all the gaussians of this GMM.
615 g.pi = count_f / static_cast<float> (total_count);
616
617 if (compute_eigens)
618 {
619 // Compute eigenvalues and vectors using SVD
620 Eigen::JacobiSVD<Eigen::Matrix3f> svd (g.covariance, Eigen::ComputeFullU);
621 // Store highest eigenvalue
622 g.eigenvalue = svd.singularValues ()[0];
623 // Store corresponding eigenvector
624 g.eigenvector = svd.matrixU ().col (0);
625 }
626 }
627 }
628
629 float
probabilityDensity(const Color & c)630 pcl::segmentation::grabcut::GMM::probabilityDensity (const Color &c)
631 {
632 float result = 0;
633
634 for (std::size_t i=0; i < gaussians_.size (); ++i)
635 result += gaussians_[i].pi * probabilityDensity (i, c);
636
637 return (result);
638 }
639
640 float
probabilityDensity(std::size_t i,const Color & c)641 pcl::segmentation::grabcut::GMM::probabilityDensity (std::size_t i, const Color &c)
642 {
643 float result = 0;
644 const pcl::segmentation::grabcut::Gaussian &G = gaussians_[i];
645 if (G.pi > 0 )
646 {
647 if (G.determinant > 0)
648 {
649 float r = c.r - G.mu.r;
650 float g = c.g - G.mu.g;
651 float b = c.b - G.mu.b;
652
653 float d = r * (r*G.inverse (0,0) + g*G.inverse (1,0) + b*G.inverse (2,0)) +
654 g * (r*G.inverse (0,1) + g*G.inverse (1,1) + b*G.inverse (2,1)) +
655 b * (r*G.inverse (0,2) + g*G.inverse (1,2) + b*G.inverse (2,2));
656
657 result = static_cast<float> (1.0/(::sqrt (G.determinant)) * std::exp (-0.5*d));
658 }
659 }
660
661 return (result);
662 }
663
664 void
buildGMMs(const Image & image,const Indices & indices,const std::vector<SegmentationValue> & hard_segmentation,std::vector<std::size_t> & components,GMM & background_GMM,GMM & foreground_GMM)665 pcl::segmentation::grabcut::buildGMMs (const Image& image,
666 const Indices& indices,
667 const std::vector<SegmentationValue>& hard_segmentation,
668 std::vector<std::size_t>& components,
669 GMM& background_GMM, GMM& foreground_GMM)
670 {
671 // Step 3: Build GMMs using Orchard-Bouman clustering algorithm
672
673 // Set up Gaussian Fitters
674 std::vector<GaussianFitter> back_fitters (background_GMM.getK ());
675 std::vector<GaussianFitter> fore_fitters (foreground_GMM.getK ());
676
677 std::size_t fore_count = 0, back_count = 0;
678 const int indices_size = static_cast<int> (indices.size ());
679 // Initialize the first foreground and background clusters
680 for (int idx = 0; idx < indices_size; ++idx)
681 {
682 components [idx] = 0;
683
684 if (hard_segmentation [idx] == SegmentationForeground)
685 {
686 fore_fitters[0].add (image[indices[idx]]);
687 fore_count++;
688 }
689 else
690 {
691 back_fitters[0].add (image[indices[idx]]);
692 back_count++;
693 }
694 }
695
696 back_fitters[0].fit (background_GMM[0], back_count, true);
697 fore_fitters[0].fit (foreground_GMM[0], fore_count, true);
698
699 std::size_t n_back = 0, n_fore = 0; // Which cluster will be split
700 std::size_t max_K = (background_GMM.getK () > foreground_GMM.getK ()) ? background_GMM.getK () : foreground_GMM.getK ();
701
702 // Compute clusters
703 for (std::size_t i = 1; i < max_K; ++i)
704 {
705 // Reset the fitters for the splitting clusters
706 back_fitters[n_back] = GaussianFitter ();
707 fore_fitters[n_fore] = GaussianFitter ();
708
709 // For brevity, get references to the splitting Gaussians
710 Gaussian& bg = background_GMM[n_back];
711 Gaussian& fg = foreground_GMM[n_fore];
712
713 // Compute splitting points
714 float split_background = bg.eigenvector[0] * bg.mu.r + bg.eigenvector[1] * bg.mu.g + bg.eigenvector[2] * bg.mu.b;
715 float split_foreground = fg.eigenvector[0] * fg.mu.r + fg.eigenvector[1] * fg.mu.g + fg.eigenvector[2] * fg.mu.b;
716
717 // Split clusters nBack and nFore, place split portion into cluster i
718 for (int idx = 0; idx < indices_size; ++idx)
719 {
720 const Color &c = image[indices[idx]];
721
722 // For each pixel
723 if (i < foreground_GMM.getK () &&
724 hard_segmentation[idx] == SegmentationForeground &&
725 components[idx] == n_fore)
726 {
727 if (fg.eigenvector[0] * c.r + fg.eigenvector[1] * c.g + fg.eigenvector[2] * c.b > split_foreground)
728 {
729 components[idx] = i;
730 fore_fitters[i].add (c);
731 }
732 else
733 {
734 fore_fitters[n_fore].add (c);
735 }
736 }
737 else if (i < background_GMM.getK () &&
738 hard_segmentation[idx] == SegmentationBackground &&
739 components[idx] == n_back)
740 {
741 if (bg.eigenvector[0] * c.r + bg.eigenvector[1] * c.g + bg.eigenvector[2] * c.b > split_background)
742 {
743 components[idx] = i;
744 back_fitters[i].add (c);
745 }
746 else
747 {
748 back_fitters[n_back].add (c);
749 }
750 }
751 }
752
753 // Compute new split Gaussians
754 back_fitters[n_back].fit (background_GMM[n_back], back_count, true);
755 fore_fitters[n_fore].fit (foreground_GMM[n_fore], fore_count, true);
756
757 if (i < background_GMM.getK ())
758 back_fitters[i].fit (background_GMM[i], back_count, true);
759 if (i < foreground_GMM.getK ())
760 fore_fitters[i].fit (foreground_GMM[i], fore_count, true);
761
762 // Find clusters with highest eigenvalue
763 n_back = 0;
764 n_fore = 0;
765
766 for (std::size_t j = 0; j <= i; ++j)
767 {
768 if (j < background_GMM.getK () && background_GMM[j].eigenvalue > background_GMM[n_back].eigenvalue)
769 n_back = j;
770
771 if (j < foreground_GMM.getK () && foreground_GMM[j].eigenvalue > foreground_GMM[n_fore].eigenvalue)
772 n_fore = j;
773 }
774 }
775
776 back_fitters.clear ();
777 fore_fitters.clear ();
778 }
779
780 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)781 pcl::segmentation::grabcut::learnGMMs (const Image& image,
782 const Indices& indices,
783 const std::vector<SegmentationValue>& hard_segmentation,
784 std::vector<std::size_t>& components,
785 GMM& background_GMM, GMM& foreground_GMM)
786 {
787 const std::size_t indices_size = static_cast<std::size_t> (indices.size ());
788 // Step 4: Assign each pixel to the component which maximizes its probability
789 for (std::size_t idx = 0; idx < indices_size; ++idx)
790 {
791 const Color &c = image[indices[idx]];
792
793 if (hard_segmentation[idx] == SegmentationForeground)
794 {
795 std::size_t k = 0;
796 float max = 0;
797
798 for (std::size_t i = 0; i < foreground_GMM.getK (); i++)
799 {
800 float p = foreground_GMM.probabilityDensity (i, c);
801 if (p > max)
802 {
803 k = i;
804 max = p;
805 }
806 }
807 components[idx] = k;
808 }
809 else
810 {
811 std::size_t k = 0;
812 float max = 0;
813
814 for (std::size_t i = 0; i < background_GMM.getK (); i++)
815 {
816 float p = background_GMM.probabilityDensity (i, c);
817 if (p > max)
818 {
819 k = i;
820 max = p;
821 }
822 }
823 components[idx] = k;
824 }
825 }
826
827 // Step 5: Relearn GMMs from new component assignments
828
829 // Set up Gaussian Fitters
830 std::vector<GaussianFitter> back_fitters (background_GMM.getK ());
831 std::vector<GaussianFitter> fore_fitters (foreground_GMM.getK ());
832
833 std::size_t fore_counter = 0, back_counter = 0;
834 for (std::size_t idx = 0; idx < indices_size; ++idx)
835 {
836 const Color &c = image [indices [idx]];
837
838 if (hard_segmentation[idx] == SegmentationForeground)
839 {
840 fore_fitters[components[idx]].add (c);
841 fore_counter++;
842 }
843 else
844 {
845 back_fitters[components[idx]].add (c);
846 back_counter++;
847 }
848 }
849
850 for (std::size_t i = 0; i < background_GMM.getK (); ++i)
851 back_fitters[i].fit (background_GMM[i], back_counter, false);
852
853 for (std::size_t i = 0; i < foreground_GMM.getK (); ++i)
854 fore_fitters[i].fit (foreground_GMM[i], fore_counter, false);
855
856 back_fitters.clear ();
857 fore_fitters.clear ();
858 }
859