Flood fill in OpenCV and EmguCV


The flood filling algorithm is to fill the connected area of ​​the seed point with a custom color according to the selected seed point, and achieve different filling effects by setting the upper and lower limits of the connectable pixels and the connection method.
Flood fills are often used to mark or separate parts of an image for further processing or analysis.
The so-called flood filling, in simple terms, is to automatically select the area connected to the seed point, and then replace the area with the specified color.
Flood fill can also be used to get a masked area from the input image, the mask will speed up the process, or only the pixels specified by the mask will be processed.
In OpenCV, flood filling is the most general method of filling algorithms. And in OpenCV 2.X, there are two versions of the FloodFill function rewritten in C++. A version without mask, and a version with mask. This mask is used to further control which areas will be filled with color (such as when filling the same image multiple times). Both versions of FloodFill must select a seed point in the image, and then fill all similar points in the adjacent area with the same color. The difference is that it is not necessary to dye all adjacent pixels with the same color. The result of a fill operation is always some contiguous region. The FloodFill function will color a point when a neighboring pixel is within a given range (from loDiff to upDiff) or within the original seedPoint pixel value range.

In OpenCV, the flood filling algorithm is implemented by the floodFill function, whose role is to fill a connected domain from the seed point with the color we specify. Connectivity is measured by how close the pixel values ​​are. OpenCV2.X has two C++ rewritten versions of floodFill.

The function prototype in OpenCV is as follows:

int floodFill(InputOutputArray image, Point seedPoint, Scalar newVal, Rect* rect=0, Scalar loDiff=Scalar(), Scalar upDiff=Scalar(), int flags=4 )
int floodFill(InputOutputArray image, Point seedPoint, Scalar newVal, Rect* rect=0, Scalar loDiff=Scalar(), Scalar upDiff=Scalar(), int flags=4 )

Comparing the two versions, the only difference is the second parameter.
  • The first parameter, the image of the InputOutputArray type, input/output 1-channel or 3-channel, 8-bit or floating-point image, the specific parameters are specified by the following parameters.
  • The second parameter, the mask of the InputOutputArray type, is an exclusive parameter of the second version of floodFill, indicating the operation mask. It should be a single-channel, 8-bit image that is two pixels larger in length and width than the input image image. The second version of floodFill needs to use and update the mask, so we must prepare and fill in this mask parameter here. The flood fill algorithm does not fill non-zero pixel regions of the mask. For example, the output of an edge detection operator can be used as a mask to prevent padding to edges. Similarly, the same mask can be used in multiple function calls to ensure that the filled areas do not overlap. It should also be noted that the mask mask will be 2 pixels larger than the image to be filled , so the coordinates of the point in the mask corresponding to the input image (x, y) pixel point are (x+1, y+1).
  • The third parameter, seedPoint of type Point, is the starting point of the flood filling algorithm.
  • The fourth parameter, newVal of Scalar type, is the value of the pixel to be colored, that is, the new value of the pixel in the redraw area.
  • The fifth parameter, a rect of type Rect*, has a default value of 0, an optional parameter used to set the minimum bounding rectangular area of ​​the area that the floodFill function will redraw.
  • The sixth parameter, loDiff of Scalar type, has a default value of Scalar( ), which represents the negative difference of brightness or color between the currently observed pixel value and its component neighbor pixel value or the seed pixel to be added to the component (lower brightness/color difference) maximum value. 
  • The seventh parameter, upDiff of Scalar type, has a default value of Scalar( ), which indicates the positive difference (lower brightness/color) between the currently observed pixel value and its component neighbor pixel value or the seed pixel to be added to the component. difference) maximum value.
  • 第八个参数,int类型的flags,操作标志符,此参数包含三个部分,比较复杂,我们一起详细看看。
                    a。低八位(第0~7位)用于控制算法的连通性,可取4 (4为缺省值) 或者 8。如果设为4,表示填充算法只考虑当前像素水平方向和垂直方向的相邻点;如果设为 8,除上述相邻点外,还会包含对角线方向的相邻点。
                    b。 中间八位 部分,上面关于高八位FLOODFILL_MASK_ONLY标识符中已经说的很明显,需要输入符合要求的掩码。Floodfill的flags参数的中间八位的值就是用于指定填充掩码图像的值的。但如果flags中间八位的值为0,则掩码会用1来填充。
                    c。 高八位 部分(16~23位)可以为0 或者如下两种选项标识符的组合:
                    FLOODFILL_FIXED_RANGE  - 如果设置为这个标识符的话,就会考虑当前像素与种子像素之间的差,否则就考虑当前像素与其相邻像素的差。也就是说,这个范围是浮动的。
                    FLOODFILL_MASK_ONLY - 如果设置为这个标识符的话,函数不会去填充改变原始图像 (也就是忽略第三个参数newVal), 而是去填充掩模图像(mask)。这个标识符只对第二个版本的floodFill有用,因第一个版本里面压根就没有mask参数。
而所有flags可以用or操作符连接起来,即“|”。例如,如果想用8邻域填充,并填充固定像素值范围,填充掩码而不是填充源图像,以及设填充值为47,那么输入的参数是这样:
flags=8 | FLOODFILL_MASK_ONLY | FLOODFILL_FIXED_RANGE | (47<<8)


eg。

Mat srcImage=imread("M:/image processing experiment/floodFill/test_.bmp");
Rect ccomp;
floodFill(srcImage,  Point(1, 1), CV_RGB(205, 205, 205), &ccomp, Scalar(15, 15, 15), Scalar(15, 15, 15), 8 | FLOODFILL_FIXED_RANGE );
imwrite("M:/image processing experiment/floodFill/test_dst.bmp", srcImage);
The seed point is (1,1). The following is a comparison of the original image and the result after flooding:

  

eg。

Mat srcImage=imread("M:/image processing experiment/floodFill/cloud.bmp");
Food mask;
mask.create ((srcImage) .rows + 2), (srcImage) .cols + 2), CV_8UC1);
mask = Scalar::all(0);
Mat ImageROI;
ImageROI = mask (Rect (1, 1, (srcImage) .cols, (srcImage. .Rows));
Mat dstImage;
Mat dstImage_canny;
srcImage.copyTo (dstImage_canny);
cvtColor(dstImage_canny, dstImage_canny, CV_RGB2GRAY);
medianBlur(dstImage_canny, dstImage_canny, 7);
Canny(dstImage_canny, dstImage, 3, 3 * 3, 3);
dstImage.copyTo(ImageROI);
Rect ccomp;
//选择了三个种子点,分别赋予了三种填充颜色。
//第一次调用floodFill时,未添加canny边缘检测后的掩模,所以云的边缘被腐蚀掉了一部分。结果就是部分云消失了。
//后两次调用floodFill时,添加了canny边缘检测后的掩模,云的边缘得到了很好的保留,但是部分颜色变化较大的区域,canny后,检验出了边缘,边缘的原来颜色得到了保留
floodFill(srcImage, Point(223, 184), CV_RGB(88,123,165), &ccomp, Scalar(35, 35, 35), Scalar(30, 30, 30), 8 | FLOODFILL_FIXED_RANGE );
floodFill(srcImage, mask,  Point(48, 507), CV_RGB(108,148,184), &ccomp, Scalar(25, 25, 25), Scalar(15, 15, 15), 8 | FLOODFILL_FIXED_RANGE );
floodFill(srcImage, mask,  Point(609, 582), CV_RGB(137,173,197), &ccomp, Scalar(25, 25, 25), Scalar(15, 15, 15), 8 | FLOODFILL_FIXED_RANGE );
imwrite("M:/图像处理实验/floodFill/云_dst.bmp", srcImage);

下面为原图与漫水填充后的输出结果:

 
canny边缘检测后的输出,也是floodFill函数中的掩模。黑色区域可以被填充,白色部分保留原来的颜色。


EmguCV中的函数原型如下:

Public Shared Function FloodFill(src As Emgu.CV.IInputOutputArray, mask As Emgu.CV.IInputOutputArray, seedPoint As System.Drawing.Point, newVal As Emgu.CV.Structure.MCvScalar, ByRef rect As System.Drawing.Rectangle, loDiff As Emgu.CV.Structure.MCvScalar, upDiff As Emgu.CV.Structure.MCvScalar, Optional connectivity As Emgu.CV.CvEnum.Connectivity = FourConnected, Optional flags As Emgu.CV.CvEnum.FloodFillType = Default) As Integer
  • 第一个参数,Emgu.CV.IInputOutputArray类型的image。
  • 第二个参数, Emgu.CV.IInputOutputArray类型的mask,表示操作掩模,。它应该为单通道、8位、长和宽上都比输入图像 image 大两个像素点的图像。与OpenCV不同,emguCV貌似没有第二个版本的FloodFill函数,emgu提供的FloodFill与OpenCV的第二个函数作用相似。漫水填充算法不会填充掩膜mask的非零像素区域。例如,一个边缘检测算子的输出可以用来作为掩膜,以防止填充到边缘。同样的,也可以在多次的函数调用中使用同一个掩膜,以保证填充的区域不会重叠。另外需要注意的是,掩膜mask会比需填充的图像大2个像素,所以 mask 中与输入图像(x,y)像素点相对应的点的坐标为(x+1,y+1)。
  • 第三个参数,Point类型的seedPoint,漫水填充算法的起始点。
  • 第四个参数,Emgu.CV.Structure.MCvScalar类型的newVal,像素点被染色的值,即在重绘区域像素的新值。
  • 第五个参数,Rect*类型的rect,用于设置floodFill函数将要重绘区域的最小边界矩形区域。与OpenCV不同,没有默认值。
  • 第六个参数,Emgu.CV.Structure.MCvScalar类型的loDiff,表示当前观察像素值与其部件邻域像素值或者待加入该部件的种子像素之间的亮度或颜色之负差(lower brightness/color difference)的最大值。 
  • The seventh parameter, upDiff of type Emgu.CV.Structure.MCvScalar, indicates the positive difference (lower brightness/color difference) between the currently observed pixel value and its component neighbor pixel value or the seed pixel to be added to the component ) maximum value.
  • The eighth parameter, the connectivity of type Emgu.CV.CvEnum.Connectivity . The connectivity of the control algorithm can be 4 (4 is the default value) or 8. If it is set to 4, it means that the filling algorithm only considers the adjacent points in the horizontal and vertical directions of the current pixel; if it is set to 8, in addition to the above adjacent points, the adjacent points in the diagonal direction will also be included.
  • The ninth parameter, the flags of Emgu.CV.CvEnum.FloodFillType type. Can be 0 (0 is the default) or a combination of the following two option identifiers:
                        FLOODFILL_FIXED_RANGE  - If set to this identifier, the difference between the current pixel and the seed pixel is considered, otherwise the difference between the current pixel and its neighbors is considered. That is, the range is floating.
                        FLOODFILL_MASK_ONLY  - If set to this flag, the function will not fill the original image (that is, ignore the third parameter newVal), but fill the mask image (mask).
eg。
Dim bkGrayWhite As New Gray(255)
Dim bkGrayBlack As New Gray(0)
Dim img As Image(Of Bgr, Byte) = New Image(Of Bgr, Byte)("M:\图像处理实验\FloodFill\云1.bmp")
Dim img_MedianBlur As Image(Of Bgr, Byte) = New Image(Of Bgr, Byte)(img.Width, img.Height)
img.CopyTo(img_MedianBlur)
CvInvoke.MedianBlur(img, img_MedianBlur, 7)
Dim mask As Image(Of Gray, Byte) = New Image(Of Gray, Byte)(img.Width + 2, img.Height + 2, bkGrayBlack)
'BGR
CvInvoke.FloodFill(img,
                   mask,
                   New System.Drawing.Point(2, 2),
                   New MCvScalar(165, 123, 88),
                   New System.Drawing.Rectangle(0, 0, 0, 0),
                   New MCvScalar(5, 5, 5),
                   New MCvScalar(5, 5, 5),
                   Emgu.CV.CvEnum.Connectivity.EightConnected,
                   Emgu.CV.CvEnum.FloodFillType.FixedRange
                   )
CvInvoke.cvSetImageROI(mask, New System.Drawing.Rectangle(1, 1, img.Width, img.Height))
Dim img_canny As Image(Of Gray, Byte) = New Image(Of Gray, Byte)(img.Width, img.Height, bkGrayBlack)
CvInvoke.Canny(img_MedianBlur, img_canny, 5, 5 * 3)
img_canny.CopyTo(mask)
CvInvoke.cvResetImageROI(mask)
CvInvoke.FloodFill(img,
                   mask,
                   New System.Drawing.Point(668, 570),
                   New MCvScalar(197, 173, 137),
                   New System.Drawing.Rectangle(0, 0, 0, 0),
                   New MCvScalar(25, 25, 25),
                   New MCvScalar(5, 5, 5),
                   Emgu.CV.CvEnum.Connectivity.EightConnected,
                   Emgu.CV.CvEnum.FloodFillType.FixedRange
                   )
img.Save("M:\图像处理实验\FloodFill\云1_result.bmp")


本文中的OpenCv与EmguCV均用的是3.0以上的版本。
参考文献:
Bradski & Kaebler ·《学习OpenCV(中文版)》· 清华大学出版社 · 2009

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325481170&siteId=291194637