1 /*M///////////////////////////////////////////////////////////////////////////////////////
2 //
3 // IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4 //
5 // By downloading, copying, installing or using the software you agree to this license.
6 // If you do not agree to this license, do not download, install,
7 // copy or use the software.
8 //
9 //
10 // Intel License Agreement
11 // For Open Source Computer Vision Library
12 //
13 // Copyright (C) 2000, Intel Corporation, all rights reserved.
14 // Third party copyrights are property of their respective owners.
15 //
16 // Redistribution and use in source and binary forms, with or without modification,
17 // are permitted provided that the following conditions are met:
18 //
19 // * Redistribution's of source code must retain the above copyright notice,
20 // this list of conditions and the following disclaimer.
21 //
22 // * Redistribution's in binary form must reproduce the above copyright notice,
23 // this list of conditions and the following disclaimer in the documentation
24 // and/or other materials provided with the distribution.
25 //
26 // * The name of Intel Corporation may not be used to endorse or promote products
27 // derived from this software without specific prior written permission.
28 //
29 // This software is provided by the copyright holders and contributors "as is" and
30 // any express or implied warranties, including, but not limited to, the implied
31 // warranties of merchantability and fitness for a particular purpose are disclaimed.
32 // In no event shall the Intel Corporation or contributors be liable for any direct,
33 // indirect, incidental, special, exemplary, or consequential damages
34 // (including, but not limited to, procurement of substitute goods or services;
35 // loss of use, data, or profits; or business interruption) however caused
36 // and on any theory of liability, whether in contract, strict liability,
37 // or tort (including negligence or otherwise) arising in any way out of
38 // the use of this software, even if advised of the possibility of such damage.
39 //
40 //M*/
41
42 #include "precomp.hpp"
43 #include "gcgraph.hpp"
44 #include <limits>
45
46 using namespace cv;
47
48 /*
49 This is implementation of image segmentation algorithm GrabCut described in
50 "GrabCut — Interactive Foreground Extraction using Iterated Graph Cuts".
51 Carsten Rother, Vladimir Kolmogorov, Andrew Blake.
52 */
53
54 /*
55 GMM - Gaussian Mixture Model
56 */
57 class GMM
58 {
59 public:
60 static const int componentsCount = 5;
61
62 GMM( Mat& _model );
63 double operator()( const Vec3d color ) const;
64 double operator()( int ci, const Vec3d color ) const;
65 int whichComponent( const Vec3d color ) const;
66
67 void initLearning();
68 void addSample( int ci, const Vec3d color );
69 void endLearning();
70
71 private:
72 void calcInverseCovAndDeterm( int ci );
73 Mat model;
74 double* coefs;
75 double* mean;
76 double* cov;
77
78 double inverseCovs[componentsCount][3][3];
79 double covDeterms[componentsCount];
80
81 double sums[componentsCount][3];
82 double prods[componentsCount][3][3];
83 int sampleCounts[componentsCount];
84 int totalSampleCount;
85 };
86
GMM(Mat & _model)87 GMM::GMM( Mat& _model )
88 {
89 const int modelSize = 3/*mean*/ + 9/*covariance*/ + 1/*component weight*/;
90 if( _model.empty() )
91 {
92 _model.create( 1, modelSize*componentsCount, CV_64FC1 );
93 _model.setTo(Scalar(0));
94 }
95 else if( (_model.type() != CV_64FC1) || (_model.rows != 1) || (_model.cols != modelSize*componentsCount) )
96 CV_Error( CV_StsBadArg, "_model must have CV_64FC1 type, rows == 1 and cols == 13*componentsCount" );
97
98 model = _model;
99
100 coefs = model.ptr<double>(0);
101 mean = coefs + componentsCount;
102 cov = mean + 3*componentsCount;
103
104 for( int ci = 0; ci < componentsCount; ci++ )
105 if( coefs[ci] > 0 )
106 calcInverseCovAndDeterm( ci );
107 }
108
operator ()(const Vec3d color) const109 double GMM::operator()( const Vec3d color ) const
110 {
111 double res = 0;
112 for( int ci = 0; ci < componentsCount; ci++ )
113 res += coefs[ci] * (*this)(ci, color );
114 return res;
115 }
116
operator ()(int ci,const Vec3d color) const117 double GMM::operator()( int ci, const Vec3d color ) const
118 {
119 double res = 0;
120 if( coefs[ci] > 0 )
121 {
122 CV_Assert( covDeterms[ci] > std::numeric_limits<double>::epsilon() );
123 Vec3d diff = color;
124 double* m = mean + 3*ci;
125 diff[0] -= m[0]; diff[1] -= m[1]; diff[2] -= m[2];
126 double mult = diff[0]*(diff[0]*inverseCovs[ci][0][0] + diff[1]*inverseCovs[ci][1][0] + diff[2]*inverseCovs[ci][2][0])
127 + diff[1]*(diff[0]*inverseCovs[ci][0][1] + diff[1]*inverseCovs[ci][1][1] + diff[2]*inverseCovs[ci][2][1])
128 + diff[2]*(diff[0]*inverseCovs[ci][0][2] + diff[1]*inverseCovs[ci][1][2] + diff[2]*inverseCovs[ci][2][2]);
129 res = 1.0f/sqrt(covDeterms[ci]) * exp(-0.5f*mult);
130 }
131 return res;
132 }
133
whichComponent(const Vec3d color) const134 int GMM::whichComponent( const Vec3d color ) const
135 {
136 int k = 0;
137 double max = 0;
138
139 for( int ci = 0; ci < componentsCount; ci++ )
140 {
141 double p = (*this)( ci, color );
142 if( p > max )
143 {
144 k = ci;
145 max = p;
146 }
147 }
148 return k;
149 }
150
initLearning()151 void GMM::initLearning()
152 {
153 for( int ci = 0; ci < componentsCount; ci++)
154 {
155 sums[ci][0] = sums[ci][1] = sums[ci][2] = 0;
156 prods[ci][0][0] = prods[ci][0][1] = prods[ci][0][2] = 0;
157 prods[ci][1][0] = prods[ci][1][1] = prods[ci][1][2] = 0;
158 prods[ci][2][0] = prods[ci][2][1] = prods[ci][2][2] = 0;
159 sampleCounts[ci] = 0;
160 }
161 totalSampleCount = 0;
162 }
163
addSample(int ci,const Vec3d color)164 void GMM::addSample( int ci, const Vec3d color )
165 {
166 sums[ci][0] += color[0]; sums[ci][1] += color[1]; sums[ci][2] += color[2];
167 prods[ci][0][0] += color[0]*color[0]; prods[ci][0][1] += color[0]*color[1]; prods[ci][0][2] += color[0]*color[2];
168 prods[ci][1][0] += color[1]*color[0]; prods[ci][1][1] += color[1]*color[1]; prods[ci][1][2] += color[1]*color[2];
169 prods[ci][2][0] += color[2]*color[0]; prods[ci][2][1] += color[2]*color[1]; prods[ci][2][2] += color[2]*color[2];
170 sampleCounts[ci]++;
171 totalSampleCount++;
172 }
173
endLearning()174 void GMM::endLearning()
175 {
176 const double variance = 0.01;
177 for( int ci = 0; ci < componentsCount; ci++ )
178 {
179 int n = sampleCounts[ci];
180 if( n == 0 )
181 coefs[ci] = 0;
182 else
183 {
184 coefs[ci] = (double)n/totalSampleCount;
185
186 double* m = mean + 3*ci;
187 m[0] = sums[ci][0]/n; m[1] = sums[ci][1]/n; m[2] = sums[ci][2]/n;
188
189 double* c = cov + 9*ci;
190 c[0] = prods[ci][0][0]/n - m[0]*m[0]; c[1] = prods[ci][0][1]/n - m[0]*m[1]; c[2] = prods[ci][0][2]/n - m[0]*m[2];
191 c[3] = prods[ci][1][0]/n - m[1]*m[0]; c[4] = prods[ci][1][1]/n - m[1]*m[1]; c[5] = prods[ci][1][2]/n - m[1]*m[2];
192 c[6] = prods[ci][2][0]/n - m[2]*m[0]; c[7] = prods[ci][2][1]/n - m[2]*m[1]; c[8] = prods[ci][2][2]/n - m[2]*m[2];
193
194 double dtrm = c[0]*(c[4]*c[8]-c[5]*c[7]) - c[1]*(c[3]*c[8]-c[5]*c[6]) + c[2]*(c[3]*c[7]-c[4]*c[6]);
195 if( dtrm <= std::numeric_limits<double>::epsilon() )
196 {
197 // Adds the white noise to avoid singular covariance matrix.
198 c[0] += variance;
199 c[4] += variance;
200 c[8] += variance;
201 }
202
203 calcInverseCovAndDeterm(ci);
204 }
205 }
206 }
207
calcInverseCovAndDeterm(int ci)208 void GMM::calcInverseCovAndDeterm( int ci )
209 {
210 if( coefs[ci] > 0 )
211 {
212 double *c = cov + 9*ci;
213 double dtrm =
214 covDeterms[ci] = c[0]*(c[4]*c[8]-c[5]*c[7]) - c[1]*(c[3]*c[8]-c[5]*c[6]) + c[2]*(c[3]*c[7]-c[4]*c[6]);
215
216 CV_Assert( dtrm > std::numeric_limits<double>::epsilon() );
217 inverseCovs[ci][0][0] = (c[4]*c[8] - c[5]*c[7]) / dtrm;
218 inverseCovs[ci][1][0] = -(c[3]*c[8] - c[5]*c[6]) / dtrm;
219 inverseCovs[ci][2][0] = (c[3]*c[7] - c[4]*c[6]) / dtrm;
220 inverseCovs[ci][0][1] = -(c[1]*c[8] - c[2]*c[7]) / dtrm;
221 inverseCovs[ci][1][1] = (c[0]*c[8] - c[2]*c[6]) / dtrm;
222 inverseCovs[ci][2][1] = -(c[0]*c[7] - c[1]*c[6]) / dtrm;
223 inverseCovs[ci][0][2] = (c[1]*c[5] - c[2]*c[4]) / dtrm;
224 inverseCovs[ci][1][2] = -(c[0]*c[5] - c[2]*c[3]) / dtrm;
225 inverseCovs[ci][2][2] = (c[0]*c[4] - c[1]*c[3]) / dtrm;
226 }
227 }
228
229 /*
230 Calculate beta - parameter of GrabCut algorithm.
231 beta = 1/(2*avg(sqr(||color[i] - color[j]||)))
232 */
calcBeta(const Mat & img)233 static double calcBeta( const Mat& img )
234 {
235 double beta = 0;
236 for( int y = 0; y < img.rows; y++ )
237 {
238 for( int x = 0; x < img.cols; x++ )
239 {
240 Vec3d color = img.at<Vec3b>(y,x);
241 if( x>0 ) // left
242 {
243 Vec3d diff = color - (Vec3d)img.at<Vec3b>(y,x-1);
244 beta += diff.dot(diff);
245 }
246 if( y>0 && x>0 ) // upleft
247 {
248 Vec3d diff = color - (Vec3d)img.at<Vec3b>(y-1,x-1);
249 beta += diff.dot(diff);
250 }
251 if( y>0 ) // up
252 {
253 Vec3d diff = color - (Vec3d)img.at<Vec3b>(y-1,x);
254 beta += diff.dot(diff);
255 }
256 if( y>0 && x<img.cols-1) // upright
257 {
258 Vec3d diff = color - (Vec3d)img.at<Vec3b>(y-1,x+1);
259 beta += diff.dot(diff);
260 }
261 }
262 }
263 if( beta <= std::numeric_limits<double>::epsilon() )
264 beta = 0;
265 else
266 beta = 1.f / (2 * beta/(4*img.cols*img.rows - 3*img.cols - 3*img.rows + 2) );
267
268 return beta;
269 }
270
271 /*
272 Calculate weights of noterminal vertices of graph.
273 beta and gamma - parameters of GrabCut algorithm.
274 */
calcNWeights(const Mat & img,Mat & leftW,Mat & upleftW,Mat & upW,Mat & uprightW,double beta,double gamma)275 static void calcNWeights( const Mat& img, Mat& leftW, Mat& upleftW, Mat& upW, Mat& uprightW, double beta, double gamma )
276 {
277 const double gammaDivSqrt2 = gamma / std::sqrt(2.0f);
278 leftW.create( img.rows, img.cols, CV_64FC1 );
279 upleftW.create( img.rows, img.cols, CV_64FC1 );
280 upW.create( img.rows, img.cols, CV_64FC1 );
281 uprightW.create( img.rows, img.cols, CV_64FC1 );
282 for( int y = 0; y < img.rows; y++ )
283 {
284 for( int x = 0; x < img.cols; x++ )
285 {
286 Vec3d color = img.at<Vec3b>(y,x);
287 if( x-1>=0 ) // left
288 {
289 Vec3d diff = color - (Vec3d)img.at<Vec3b>(y,x-1);
290 leftW.at<double>(y,x) = gamma * exp(-beta*diff.dot(diff));
291 }
292 else
293 leftW.at<double>(y,x) = 0;
294 if( x-1>=0 && y-1>=0 ) // upleft
295 {
296 Vec3d diff = color - (Vec3d)img.at<Vec3b>(y-1,x-1);
297 upleftW.at<double>(y,x) = gammaDivSqrt2 * exp(-beta*diff.dot(diff));
298 }
299 else
300 upleftW.at<double>(y,x) = 0;
301 if( y-1>=0 ) // up
302 {
303 Vec3d diff = color - (Vec3d)img.at<Vec3b>(y-1,x);
304 upW.at<double>(y,x) = gamma * exp(-beta*diff.dot(diff));
305 }
306 else
307 upW.at<double>(y,x) = 0;
308 if( x+1<img.cols && y-1>=0 ) // upright
309 {
310 Vec3d diff = color - (Vec3d)img.at<Vec3b>(y-1,x+1);
311 uprightW.at<double>(y,x) = gammaDivSqrt2 * exp(-beta*diff.dot(diff));
312 }
313 else
314 uprightW.at<double>(y,x) = 0;
315 }
316 }
317 }
318
319 /*
320 Check size, type and element values of mask matrix.
321 */
checkMask(const Mat & img,const Mat & mask)322 static void checkMask( const Mat& img, const Mat& mask )
323 {
324 if( mask.empty() )
325 CV_Error( CV_StsBadArg, "mask is empty" );
326 if( mask.type() != CV_8UC1 )
327 CV_Error( CV_StsBadArg, "mask must have CV_8UC1 type" );
328 if( mask.cols != img.cols || mask.rows != img.rows )
329 CV_Error( CV_StsBadArg, "mask must have as many rows and cols as img" );
330 for( int y = 0; y < mask.rows; y++ )
331 {
332 for( int x = 0; x < mask.cols; x++ )
333 {
334 uchar val = mask.at<uchar>(y,x);
335 if( val!=GC_BGD && val!=GC_FGD && val!=GC_PR_BGD && val!=GC_PR_FGD )
336 CV_Error( CV_StsBadArg, "mask element value must be equel"
337 "GC_BGD or GC_FGD or GC_PR_BGD or GC_PR_FGD" );
338 }
339 }
340 }
341
342 /*
343 Initialize mask using rectangular.
344 */
initMaskWithRect(Mat & mask,Size imgSize,Rect rect)345 static void initMaskWithRect( Mat& mask, Size imgSize, Rect rect )
346 {
347 mask.create( imgSize, CV_8UC1 );
348 mask.setTo( GC_BGD );
349
350 rect.x = std::max(0, rect.x);
351 rect.y = std::max(0, rect.y);
352 rect.width = std::min(rect.width, imgSize.width-rect.x);
353 rect.height = std::min(rect.height, imgSize.height-rect.y);
354
355 (mask(rect)).setTo( Scalar(GC_PR_FGD) );
356 }
357
358 /*
359 Initialize GMM background and foreground models using kmeans algorithm.
360 */
initGMMs(const Mat & img,const Mat & mask,GMM & bgdGMM,GMM & fgdGMM)361 static void initGMMs( const Mat& img, const Mat& mask, GMM& bgdGMM, GMM& fgdGMM )
362 {
363 const int kMeansItCount = 10;
364 const int kMeansType = KMEANS_PP_CENTERS;
365
366 Mat bgdLabels, fgdLabels;
367 std::vector<Vec3f> bgdSamples, fgdSamples;
368 Point p;
369 for( p.y = 0; p.y < img.rows; p.y++ )
370 {
371 for( p.x = 0; p.x < img.cols; p.x++ )
372 {
373 if( mask.at<uchar>(p) == GC_BGD || mask.at<uchar>(p) == GC_PR_BGD )
374 bgdSamples.push_back( (Vec3f)img.at<Vec3b>(p) );
375 else // GC_FGD | GC_PR_FGD
376 fgdSamples.push_back( (Vec3f)img.at<Vec3b>(p) );
377 }
378 }
379 CV_Assert( !bgdSamples.empty() && !fgdSamples.empty() );
380 Mat _bgdSamples( (int)bgdSamples.size(), 3, CV_32FC1, &bgdSamples[0][0] );
381 kmeans( _bgdSamples, GMM::componentsCount, bgdLabels,
382 TermCriteria( CV_TERMCRIT_ITER, kMeansItCount, 0.0), 0, kMeansType );
383 Mat _fgdSamples( (int)fgdSamples.size(), 3, CV_32FC1, &fgdSamples[0][0] );
384 kmeans( _fgdSamples, GMM::componentsCount, fgdLabels,
385 TermCriteria( CV_TERMCRIT_ITER, kMeansItCount, 0.0), 0, kMeansType );
386
387 bgdGMM.initLearning();
388 for( int i = 0; i < (int)bgdSamples.size(); i++ )
389 bgdGMM.addSample( bgdLabels.at<int>(i,0), bgdSamples[i] );
390 bgdGMM.endLearning();
391
392 fgdGMM.initLearning();
393 for( int i = 0; i < (int)fgdSamples.size(); i++ )
394 fgdGMM.addSample( fgdLabels.at<int>(i,0), fgdSamples[i] );
395 fgdGMM.endLearning();
396 }
397
398 /*
399 Assign GMMs components for each pixel.
400 */
assignGMMsComponents(const Mat & img,const Mat & mask,const GMM & bgdGMM,const GMM & fgdGMM,Mat & compIdxs)401 static void assignGMMsComponents( const Mat& img, const Mat& mask, const GMM& bgdGMM, const GMM& fgdGMM, Mat& compIdxs )
402 {
403 Point p;
404 for( p.y = 0; p.y < img.rows; p.y++ )
405 {
406 for( p.x = 0; p.x < img.cols; p.x++ )
407 {
408 Vec3d color = img.at<Vec3b>(p);
409 compIdxs.at<int>(p) = mask.at<uchar>(p) == GC_BGD || mask.at<uchar>(p) == GC_PR_BGD ?
410 bgdGMM.whichComponent(color) : fgdGMM.whichComponent(color);
411 }
412 }
413 }
414
415 /*
416 Learn GMMs parameters.
417 */
learnGMMs(const Mat & img,const Mat & mask,const Mat & compIdxs,GMM & bgdGMM,GMM & fgdGMM)418 static void learnGMMs( const Mat& img, const Mat& mask, const Mat& compIdxs, GMM& bgdGMM, GMM& fgdGMM )
419 {
420 bgdGMM.initLearning();
421 fgdGMM.initLearning();
422 Point p;
423 for( int ci = 0; ci < GMM::componentsCount; ci++ )
424 {
425 for( p.y = 0; p.y < img.rows; p.y++ )
426 {
427 for( p.x = 0; p.x < img.cols; p.x++ )
428 {
429 if( compIdxs.at<int>(p) == ci )
430 {
431 if( mask.at<uchar>(p) == GC_BGD || mask.at<uchar>(p) == GC_PR_BGD )
432 bgdGMM.addSample( ci, img.at<Vec3b>(p) );
433 else
434 fgdGMM.addSample( ci, img.at<Vec3b>(p) );
435 }
436 }
437 }
438 }
439 bgdGMM.endLearning();
440 fgdGMM.endLearning();
441 }
442
443 /*
444 Construct GCGraph
445 */
constructGCGraph(const Mat & img,const Mat & mask,const GMM & bgdGMM,const GMM & fgdGMM,double lambda,const Mat & leftW,const Mat & upleftW,const Mat & upW,const Mat & uprightW,GCGraph<double> & graph)446 static void constructGCGraph( const Mat& img, const Mat& mask, const GMM& bgdGMM, const GMM& fgdGMM, double lambda,
447 const Mat& leftW, const Mat& upleftW, const Mat& upW, const Mat& uprightW,
448 GCGraph<double>& graph )
449 {
450 int vtxCount = img.cols*img.rows,
451 edgeCount = 2*(4*img.cols*img.rows - 3*(img.cols + img.rows) + 2);
452 graph.create(vtxCount, edgeCount);
453 Point p;
454 for( p.y = 0; p.y < img.rows; p.y++ )
455 {
456 for( p.x = 0; p.x < img.cols; p.x++)
457 {
458 // add node
459 int vtxIdx = graph.addVtx();
460 Vec3b color = img.at<Vec3b>(p);
461
462 // set t-weights
463 double fromSource, toSink;
464 if( mask.at<uchar>(p) == GC_PR_BGD || mask.at<uchar>(p) == GC_PR_FGD )
465 {
466 fromSource = -log( bgdGMM(color) );
467 toSink = -log( fgdGMM(color) );
468 }
469 else if( mask.at<uchar>(p) == GC_BGD )
470 {
471 fromSource = 0;
472 toSink = lambda;
473 }
474 else // GC_FGD
475 {
476 fromSource = lambda;
477 toSink = 0;
478 }
479 graph.addTermWeights( vtxIdx, fromSource, toSink );
480
481 // set n-weights
482 if( p.x>0 )
483 {
484 double w = leftW.at<double>(p);
485 graph.addEdges( vtxIdx, vtxIdx-1, w, w );
486 }
487 if( p.x>0 && p.y>0 )
488 {
489 double w = upleftW.at<double>(p);
490 graph.addEdges( vtxIdx, vtxIdx-img.cols-1, w, w );
491 }
492 if( p.y>0 )
493 {
494 double w = upW.at<double>(p);
495 graph.addEdges( vtxIdx, vtxIdx-img.cols, w, w );
496 }
497 if( p.x<img.cols-1 && p.y>0 )
498 {
499 double w = uprightW.at<double>(p);
500 graph.addEdges( vtxIdx, vtxIdx-img.cols+1, w, w );
501 }
502 }
503 }
504 }
505
506 /*
507 Estimate segmentation using MaxFlow algorithm
508 */
estimateSegmentation(GCGraph<double> & graph,Mat & mask)509 static void estimateSegmentation( GCGraph<double>& graph, Mat& mask )
510 {
511 graph.maxFlow();
512 Point p;
513 for( p.y = 0; p.y < mask.rows; p.y++ )
514 {
515 for( p.x = 0; p.x < mask.cols; p.x++ )
516 {
517 if( mask.at<uchar>(p) == GC_PR_BGD || mask.at<uchar>(p) == GC_PR_FGD )
518 {
519 if( graph.inSourceSegment( p.y*mask.cols+p.x /*vertex index*/ ) )
520 mask.at<uchar>(p) = GC_PR_FGD;
521 else
522 mask.at<uchar>(p) = GC_PR_BGD;
523 }
524 }
525 }
526 }
527
grabCut(InputArray _img,InputOutputArray _mask,Rect rect,InputOutputArray _bgdModel,InputOutputArray _fgdModel,int iterCount,int mode)528 void cv::grabCut( InputArray _img, InputOutputArray _mask, Rect rect,
529 InputOutputArray _bgdModel, InputOutputArray _fgdModel,
530 int iterCount, int mode )
531 {
532 Mat img = _img.getMat();
533 Mat& mask = _mask.getMatRef();
534 Mat& bgdModel = _bgdModel.getMatRef();
535 Mat& fgdModel = _fgdModel.getMatRef();
536
537 if( img.empty() )
538 CV_Error( CV_StsBadArg, "image is empty" );
539 if( img.type() != CV_8UC3 )
540 CV_Error( CV_StsBadArg, "image mush have CV_8UC3 type" );
541
542 GMM bgdGMM( bgdModel ), fgdGMM( fgdModel );
543 Mat compIdxs( img.size(), CV_32SC1 );
544
545 if( mode == GC_INIT_WITH_RECT || mode == GC_INIT_WITH_MASK )
546 {
547 if( mode == GC_INIT_WITH_RECT )
548 initMaskWithRect( mask, img.size(), rect );
549 else // flag == GC_INIT_WITH_MASK
550 checkMask( img, mask );
551 initGMMs( img, mask, bgdGMM, fgdGMM );
552 }
553
554 if( iterCount <= 0)
555 return;
556
557 if( mode == GC_EVAL )
558 checkMask( img, mask );
559
560 const double gamma = 50;
561 const double lambda = 9*gamma;
562 const double beta = calcBeta( img );
563
564 Mat leftW, upleftW, upW, uprightW;
565 calcNWeights( img, leftW, upleftW, upW, uprightW, beta, gamma );
566
567 for( int i = 0; i < iterCount; i++ )
568 {
569 GCGraph<double> graph;
570 assignGMMsComponents( img, mask, bgdGMM, fgdGMM, compIdxs );
571 learnGMMs( img, mask, compIdxs, bgdGMM, fgdGMM );
572 constructGCGraph(img, mask, bgdGMM, fgdGMM, lambda, leftW, upleftW, upW, uprightW, graph );
573 estimateSegmentation( graph, mask );
574 }
575 }
576