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