From 603bc58d3bd7bcb7386adc40c094b911a9199d1d Mon Sep 17 00:00:00 2001 From: Samaresh Kumar Singh Date: Sat, 8 Nov 2025 13:16:01 -0600 Subject: [PATCH] Fix #27968: Eliminate unnecessary type conversions in GrabCut initGMMs Remove performance bottleneck caused by redundant type conversions in the initGMMs() function's tight nested loops that iterate over all image pixels. Changes: - Changed storage vectors from Vec3f to Vec3b to store pixel data directly without intermediate conversion - Modified kmeans preparation to convert Vec3b data to CV_32FC1 only when creating the Mat for clustering (using convertTo instead of per-pixel cast) - Explicitly convert Vec3b to Vec3d only when calling addSample() method Performance Impact: Previously: Vec3b -> Vec3f (in loop) -> Vec3d (implicit in addSample) Now: Vec3b (in loop) -> Vec3d (explicit, only in addSample call) This eliminates one unnecessary type conversion per pixel in the tight loop, reducing computational overhead especially for large images. Fixes #27968 --- modules/imgproc/src/grabcut.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/modules/imgproc/src/grabcut.cpp b/modules/imgproc/src/grabcut.cpp index 358747843e..115f346e99 100644 --- a/modules/imgproc/src/grabcut.cpp +++ b/modules/imgproc/src/grabcut.cpp @@ -371,28 +371,30 @@ static void initGMMs( const Mat& img, const Mat& mask, GMM& bgdGMM, GMM& fgdGMM const int kMeansType = KMEANS_PP_CENTERS; Mat bgdLabels, fgdLabels; - std::vector bgdSamples, fgdSamples; + std::vector bgdSamples, fgdSamples; Point p; for( p.y = 0; p.y < img.rows; p.y++ ) { for( p.x = 0; p.x < img.cols; p.x++ ) { if( mask.at(p) == GC_BGD || mask.at(p) == GC_PR_BGD ) - bgdSamples.push_back( (Vec3f)img.at(p) ); + bgdSamples.push_back( img.at(p) ); else // GC_FGD | GC_PR_FGD - fgdSamples.push_back( (Vec3f)img.at(p) ); + fgdSamples.push_back( img.at(p) ); } } CV_Assert( !bgdSamples.empty() && !fgdSamples.empty() ); { - Mat _bgdSamples( (int)bgdSamples.size(), 3, CV_32FC1, &bgdSamples[0][0] ); + Mat _bgdSamples( (int)bgdSamples.size(), 3, CV_8UC1, &bgdSamples[0][0] ); + _bgdSamples.convertTo(_bgdSamples, CV_32FC1); int num_clusters = GMM::componentsCount; num_clusters = std::min(num_clusters, (int)bgdSamples.size()); kmeans( _bgdSamples, num_clusters, bgdLabels, TermCriteria( TermCriteria::MAX_ITER, kMeansItCount, 0.0), 0, kMeansType ); } { - Mat _fgdSamples( (int)fgdSamples.size(), 3, CV_32FC1, &fgdSamples[0][0] ); + Mat _fgdSamples( (int)fgdSamples.size(), 3, CV_8UC1, &fgdSamples[0][0] ); + _fgdSamples.convertTo(_fgdSamples, CV_32FC1); int num_clusters = GMM::componentsCount; num_clusters = std::min(num_clusters, (int)fgdSamples.size()); kmeans( _fgdSamples, num_clusters, fgdLabels, @@ -401,12 +403,12 @@ static void initGMMs( const Mat& img, const Mat& mask, GMM& bgdGMM, GMM& fgdGMM bgdGMM.initLearning(); for( int i = 0; i < (int)bgdSamples.size(); i++ ) - bgdGMM.addSample( bgdLabels.at(i,0), bgdSamples[i] ); + bgdGMM.addSample( bgdLabels.at(i,0), Vec3d(bgdSamples[i]) ); bgdGMM.endLearning(); fgdGMM.initLearning(); for( int i = 0; i < (int)fgdSamples.size(); i++ ) - fgdGMM.addSample( fgdLabels.at(i,0), fgdSamples[i] ); + fgdGMM.addSample( fgdLabels.at(i,0), Vec3d(fgdSamples[i]) ); fgdGMM.endLearning(); }