31

The documentation for OpenCV's floodfill function states:

The function uses and updates the mask, so you take responsibility of initializing the mask content. Flood-filling cannot go across non-zero pixels in the mask. For example, an edge detector output can be used as a mask to stop filling at edges. It is possible to use the same mask in multiple calls to the function to make sure the filled area does not overlap.

How does the function update the mask? Does it set all the pixels within the floodfill to some non-zero value?

1''
  • 26,823
  • 32
  • 143
  • 200

3 Answers3

34

All zero-valued pixels in the same connected component as the seed point of the mask are replaced by the value you specify. This value must be added to the flags parameter, left-shifted by 8 bits:

uchar fillValue = 128;
cv::floodFill(img, mask, seed, cv::Scalar(255) ,0, cv::Scalar(), cv::Scalar(), 4 | cv::FLOODFILL_MASK_ONLY | (fillValue << 8));

A simple, but perhaps enlightening example follows. Creating an image like so:

//Create simple input image
cv::Point seed(4,4);
cv::Mat img = cv::Mat::zeros(100,100,CV_8UC1);
cv::circle(img, seed, 20, cv::Scalar(128),3);

Results in this image:

Original image

Then, creating a mask and flood-filling it:

//Create a mask from edges in the original image
cv::Mat mask;
cv::Canny(img, mask, 100, 200);
cv::copyMakeBorder(mask, mask, 1, 1, 1, 1, cv::BORDER_REPLICATE);

//Fill mask with value 128
uchar fillValue = 128;
cv::floodFill(img, mask, seed, cv::Scalar(255) ,0, cv::Scalar(), cv::Scalar(), 4 | cv::FLOODFILL_MASK_ONLY | (fillValue << 8));

Gives this result:

Flood-filled mask

The white pixels in the mask are the result of edge detection, while the grey pixels are the result of the flood-fill.

UPDATE: In response to the comment, flag value 4 specifies the pixel neighborhood with which to compare the color value difference. From the documentation:

Lower bits contain a connectivity value, 4 (default) or 8, used within the function. Connectivity determines which neighbors of a pixel are considered.

When the cv::FLOODFILL_MASK_ONLY flag is not passed, both the image and the mask are updated, but the flood filling will stop at at any nonzero mask values.

Aurelius
  • 11,111
  • 3
  • 52
  • 69
  • Would someone rewrite this peace of code in the answer to java, please? – Jürgen K. Sep 01 '15 at 15:37
  • 2
    @Aurelius Thanks for pointing out that the pixels of the mask have to be zero-valued in order to get filled. This hint saved my day.... – tisch Jul 07 '16 at 14:15
  • Hint for python users: This works exactly the same way, here's the syntax: `flood_value = 128 cv.floodFill(img, mask, seed, 255, 5, 5, flags=cv.FLOODFILL_MASK_ONLY | (flood_value << 8))` – vatbub Mar 19 '20 at 17:44
13

And a python version

im = cv2.imread("seagull.jpg")
h,w,chn = im.shape
seed = (w/2,h/2)

mask = np.zeros((h+2,w+2),np.uint8)

floodflags = 4
floodflags |= cv2.FLOODFILL_MASK_ONLY
floodflags |= (255 << 8)

num,im,mask,rect = cv2.floodFill(im, mask, seed, (255,0,0), (10,)*3, (10,)*3, floodflags)

cv2.imwrite("seagull_flood.png", mask)

(Seagull image from Wikimedia: https://commons.wikimedia.org/wiki/Commons:Quality_images#/media/File:Gull_portrait_ca_usa.jpg)

Result: result

Roy Shilkrot
  • 3,079
  • 29
  • 25
  • 4
    One particularly important thing to notice is that when specifying a point `x` and `y` axis are switched compared to numpy indexing. – Íhor Mé Mar 24 '18 at 02:03
  • 2
    With the same image and code, I get only the edges - https://drive.google.com/open?id=17DhliW4OIy2iNPJkXglbNPdgaEgpGACf Can you please advise why this might be the case? – a_jelly_fish Nov 14 '19 at 14:07
  • @aneeshaasc try (7,)*3, (7,)*3 instead of (10,)*3, (10,)*3 in cv2.floodFill(im, mask, seed, (255,0,0), (10,)*3, (10,)*3, floodflags) – Tran Anh Minh Apr 02 '20 at 20:16
  • What's the meaning of the flag 255<<8 ? I refer inparticular to the <<8 part as I kknow that 255 means that the mask will be filled with taht value in the region indentified – Aelius Feb 08 '21 at 15:44
  • @Aelius see the docs for a full description: https://docs.opencv.org/4.5.1/d7/d1b/group__imgproc__misc.html#ga366aae45a6c1289b341d140839f18717 – Roy Shilkrot Feb 09 '21 at 09:15
0

Per Aurelius' answer, the mask needs to be zeroed.

Checking comment in the source, it stated that

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.

The mask will impact the result so need to be zeroed before use:

cv::Mat mask;
mask = cv::Mat::zeros(img.rows + 2, img.cols + 2, CV_8UC1);
MK Yung
  • 4,344
  • 6
  • 30
  • 35