From ffee1a4126e58237888e234cacf21d4babeaf4c8 Mon Sep 17 00:00:00 2001 From: Suleyman TURKMEN Date: Tue, 8 Feb 2022 08:37:04 +0300 Subject: [PATCH] fix cv::floodfill() for calling it with an empty mask --- modules/imgproc/include/opencv2/imgproc.hpp | 7 ++-- modules/imgproc/src/floodfill.cpp | 42 +++++++++------------ modules/imgproc/test/test_floodfill.cpp | 4 +- 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index bd788a7a01..9425ba88d5 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -3660,10 +3660,11 @@ a mask and then extract the contour, or copy the region to another image, and so function unless the #FLOODFILL_MASK_ONLY flag is set in the second variant of the function. See the details below. @param mask Operation mask that should be a single-channel 8-bit image, 2 pixels wider and 2 pixels -taller than image. Since this is both an input and output parameter, you must take responsibility -of initializing it. Flood-filling cannot go across non-zero pixels in the input mask. For example, +taller than image. If an empty Mat is passed it will be created automatically. Since this is both an +input and output parameter, you must take responsibility of initializing it. +Flood-filling cannot go across non-zero pixels in the input mask. For example, an edge detector output can be used as a mask to stop filling at edges. On output, pixels in the -mask corresponding to filled pixels in the image are set to 1 or to the a value specified in flags +mask corresponding to filled pixels in the image are set to 1 or to the specified value in flags as described below. Additionally, the function fills the border of the mask with ones to simplify internal processing. It is therefore possible to use the same mask in multiple calls to the function to make sure the filled areas do not overlap. diff --git a/modules/imgproc/src/floodfill.cpp b/modules/imgproc/src/floodfill.cpp index 2816795bc6..8595011d48 100644 --- a/modules/imgproc/src/floodfill.cpp +++ b/modules/imgproc/src/floodfill.cpp @@ -477,11 +477,10 @@ int cv::floodFill( InputOutputArray _image, InputOutputArray _mask, nv_buf._[0] = nv_buf._[1] = nv_buf._[2] = nv_buf._[3] = 0; struct { Vec3b b; Vec3i i; Vec3f f; } ld_buf, ud_buf; + Mat img = _image.getMat(), mask; - if( !_mask.empty() ) - mask = _mask.getMat(); - Size size = img.size(); + Size size = img.size(); int type = img.type(); int depth = img.depth(); int cn = img.channels(); @@ -495,6 +494,20 @@ int cv::floodFill( InputOutputArray _image, InputOutputArray _mask, if( connectivity != 0 && connectivity != 4 && connectivity != 8 ) CV_Error( CV_StsBadFlag, "Connectivity must be 4, 0(=4) or 8" ); + if( _mask.empty() ) + { + _mask.create( size.height + 2, size.width + 2, CV_8UC1 ); + _mask.setTo(0); + } + + mask = _mask.getMat(); + CV_CheckTypeEQ( mask.type(), CV_8U, "" ); + CV_CheckEQ( mask.rows, size.height + 2, "" ); + CV_CheckEQ( mask.cols, size.width + 2, "" ); + + Mat mask_inner = mask( Rect(1, 1, mask.cols - 2, mask.rows - 2) ); + copyMakeBorder( mask_inner, mask, 1, 1, 1, 1, BORDER_ISOLATED | BORDER_CONSTANT, Scalar(1) ); + bool is_simple = mask.empty() && (flags & FLOODFILL_MASK_ONLY) == 0; for( i = 0; i < cn; i++ ) @@ -544,26 +557,6 @@ int cv::floodFill( InputOutputArray _image, InputOutputArray _mask, } } - if( mask.empty() ) - { - Mat tempMask( size.height + 2, size.width + 2, CV_8UC1 ); - tempMask.setTo(Scalar::all(0)); - mask = tempMask; - } - else - { - CV_Assert( mask.rows == size.height+2 && mask.cols == size.width+2 ); - CV_Assert( mask.type() == CV_8U ); - } - - memset( mask.ptr(), 1, mask.cols ); - memset( mask.ptr(mask.rows-1), 1, mask.cols ); - - for( i = 1; i <= size.height; i++ ) - { - mask.at(i, 0) = mask.at(i, mask.cols-1) = (uchar)1; - } - if( depth == CV_8U ) for( i = 0; i < cn; i++ ) { @@ -632,7 +625,8 @@ int cv::floodFill( InputOutputArray _image, Point seedPoint, { CV_INSTRUMENT_REGION(); - return floodFill(_image, Mat(), seedPoint, newVal, rect, loDiff, upDiff, flags); + Mat mask; + return floodFill(_image, mask, seedPoint, newVal, rect, loDiff, upDiff, flags); } diff --git a/modules/imgproc/test/test_floodfill.cpp b/modules/imgproc/test/test_floodfill.cpp index b880c4ee37..934e421fba 100644 --- a/modules/imgproc/test/test_floodfill.cpp +++ b/modules/imgproc/test/test_floodfill.cpp @@ -531,11 +531,11 @@ TEST(Imgproc_FloodFill, maskValue) { const int n = 50; Mat img = Mat::zeros(n, n, CV_8U); - Mat mask = Mat::zeros(n + 2, n + 2, CV_8U); + Mat mask; circle(img, Point(n/2, n/2), 20, Scalar(100), 4); - int flags = 4 + CV_FLOODFILL_MASK_ONLY; + int flags = 4 + FLOODFILL_MASK_ONLY; floodFill(img, mask, Point(n/2 + 13, n/2), Scalar(100), NULL, Scalar(), Scalar(), flags); ASSERT_EQ(1, cvtest::norm(mask.rowRange(1, n-1).colRange(1, n-1), NORM_INF));