How does cv2.floodfill work?

2020-06-06 08:18发布

问题:

here is a example code showing usage of cv2.floodfill function

import cv2
import numpy as np
import os

def imshow(img):
    cv2.imshow('img', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


img = cv2.imread('test4.png')

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

thresh = cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY_INV,3,1)

_, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_NONE)

mask = np.zeros(img.shape[:-1],np.uint8)

cv2.drawContours(mask,contours,-1,(255,255,255),-1)

height, width = img.shape[:-1]

mask1 = np.zeros((height+2, width+2), np.uint8)     # line 26
cv2.floodFill(mask,mask1,(0,0),255)     # line 27
mask_inv=cv2.bitwise_not(mask)

imshow(mask_inv)

i am using this function in one of me projects but i am not understanding the mask1 part of the code (line 26 and line 27 ) that is,

why do we create the mask1 with shape h+2, w+2 for a given image with height-'h' and width-'w'?(line 26)

why do we have to pass this mask1 to the cv2.floodfill function?(line 27)

here is the input and output of the example code.

input image

output image

please help

回答1:

The short answer for your current implementation is that you don't need to use that parameter. You can simply replace mask1 with None in the call because you're not using it. Check out my answers here, here, and here to see examples of floodFill() without the mask parameter.

The mask parameter simply fills in where floodFill() has been applied, or in case you just want to create a mask and not modify your image. You can see an example on one of my simple projects here using the mask parameter. In this case I was mimicking the Adobe Photoshop Magic Wand selection tool, and so I only needed the mask and didn't want to modify the image.

What floodFill() does is connects a pixel to it's neighbors if the neighbors are within some threshold difference of the pixel. Four-way connectivity checks the neighbors above and below, and to the left and right. Eight-way connectivity checks the diagonal pixels in addition. This means that at border pixels, you either need a bunch of if statements to not check the pixels outside the border, OR, you can simply pad the image with one pixel on each side so that you don't need special cases, which both reads better in code and is faster.

Normally, this would just be done inside the function and not exposed to the user. However, floodFill() is designed to be called multiple times on an image if need-be, and so creating a new padded Mat each time it's called would make the function slow. So instead, I believe, the padding is passed on to the user so that if they call the function multiple times, the padded mask is created only once.