漫水填充算法
漫水填充算法
一、技术背景
在二值图像处理中,目标区域内部可能存在孔洞(背景色的孤立区域)。这些孔洞会影响轮廓检测、面积计算等后续分析的准确性。漫水填充算法(Flood Fill)是一种区域填充算法,可以用于填充封闭区域或检测孔洞。
本文介绍 SEM 项目中imageUtils.fillHoles()的实现原理,包括四连通/八连通原理、边界填充、BitwiseNot 和 BitwiseOr 合并操作。
二、数学原理
2.1 漫水填充算法
漫水填充算法从指定种子点出发,将与种子点连通且满足一定条件的像素填充为目标颜色。
算法步骤:
- 从种子点( x 0 , y 0 ) (x_0, y_0)(x0,y0)开始
- 检查该点是否满足填充条件
- 如果满足,填充该点并加入队列
- 从队列取出点,检查其邻域
- 重复直到队列空
2.2 连通性
四连通:只考虑上下左右4个邻居
N 4 ( p ) = { ( x + 1 , y ) , ( x − 1 , y ) , ( x , y + 1 ) , ( x , y − 1 ) } N_4(p) = \{(x+1, y), (x-1, y), (x, y+1), (x, y-1)\}N4(p)={(x+1,y),(x−1,y),(x,y+1),(x,y−1)}
八连通:考虑周围8个邻居
N 8 ( p ) = N 4 ( p ) ∪ { ( x + 1 , y + 1 ) , ( x + 1 , y − 1 ) , ( x − 1 , y + 1 ) , ( x − 1 , y − 1 ) } N_8(p) = N_4(p) \cup \{(x+1, y+1), (x+1, y-1), (x-1, y+1), (x-1, y-1)\}N8(p)=N4(p)∪{(x+1,y+1),(x+1,y−1),(x−1,y+1),(x−1,y−1)}
2.3 孔洞填充原理
孔洞填充的核心思想是:
- 从图像边界开始漫水填充背景(将边界连通的背景填充为白色)
- 反转填充结果(背景变为黑色,孔洞变为白色)
- 将反转结果与原图进行或运算(合并孔洞和目标区域)
数学表达:
R e s u l t = O r i g i n a l ∪ ( B a c k g r o u n d f i l l e d ) c Result = Original \cup (Background_{filled})^cResult=Original∪(Backgroundfilled)c
其中,( B a c k g r o u n d f i l l e d ) c (Background_{filled})^c(Backgroundfilled)c表示填充后背景的补集。
三、代码实现
以下代码来自e:\SEM\Utils\imageUtils.cs:
//填充孔洞publicstaticvoidfillHoles(refMatsrc_img){Matfilled_img=newMat(src_img.Size(),MatType.CV_8UC1,Scalar.Black);for(inti=0;i<src_img.Rows;i++){for(intj=0;j<src_img.Cols;j++){if(src_img.At<Vec3b>(i,j)==newVec3b(0,0,0)){filled_img.At<byte>(i,j)=0;}else{filled_img.At<byte>(i,j)=255;}}}inth=src_img.Rows;intw=src_img.Cols;Matimg_floodfill=filled_img.Clone();MatMask=newMat();intpadding=2;Cv2.CopyMakeBorder(img_floodfill,Mask,padding,padding,padding,padding,BorderTypes.Constant,newScalar(0));Cv2.FloodFill(Mask,newPoint(0,0),newScalar(255));Matimage_floodfill_inv=newMat();Cv2.BitwiseNot(Mask,image_floodfill_inv);Matroi=newMat(image_floodfill_inv,newRect(padding,padding,src_img.Cols,src_img.Rows));Matresult_img=newMat();Cv2.BitwiseOr(filled_img,roi,result_img);src_img=result_img.Clone();}处理流程详解
- 彩色转灰度二值:将彩色图像转换为灰度二值图像(黑色为0,白色为255)
- 边界扩展:使用
CopyMakeBorder在图像周围添加边框,创建掩码 - 漫水填充:从(0,0)点开始填充背景区域为白色
- 反转:使用
BitwiseNot反转填充结果 - 提取ROI:去除添加的边框,恢复原始尺寸
- 合并:使用
BitwiseOr将孔洞区域与原图合并
OpenCV API
// 漫水填充intCv2.FloodFill(Matsrc,// 输入图像Matmask,// 掩码(比原图大2像素)PointseedPoint,// 种子点ScalarnewVal,// 填充颜色outRectrect,// 填充区域边界Scalar?loDiff,// 低阈值差(可选)Scalar?upDiff,// 高阈值差(可选)FloodFillFlags flags// 填充模式);// 按位取反Cv2.BitwiseNot(Matsrc,Matdst);// 按位或Cv2.BitwiseOr(Matsrc1,Matsrc2,Matdst);// 边界扩展Cv2.CopyMakeBorder(Matsrc,Matdst,inttop,intbottom,intleft,intright,BorderTypesborderType,Scalar?value);四、参数调优
4.1 连通性选择
| 连通性 | 特点 | 适用场景 |
|---|---|---|
| 四连通 | 严格,填充范围小 | 目标边界复杂 |
| 八连通 | 宽松,填充范围大 | 目标边界平滑 |
通过FloodFillFlags设置:
FloodFillFlags.FixedRange:使用固定范围FloodFillFlags.MaskOnly:只修改掩码
4.2 边界扩展参数
| 参数 | 推荐值 | 说明 |
|---|---|---|
| padding | 2 | 通常设为2(漫水填充需要掩码比原图大) |
| borderType | Constant | 常量填充 |
| value | 0 | 边界值为黑色 |
4.3 种子点选择
种子点通常选择图像边角位置:
(0, 0):左上角(0, height-1):左下角(width-1, 0):右上角(width-1, height-1):右下角
五、常见问题
Q1:填充后目标区域也被填充?
原因:种子点选择在目标区域内部。
解决方案:
- 确保种子点在背景区域(如边角位置)
- 检查目标是否完全封闭
Q2:孔洞未被完全填充?
原因:孔洞与边界连通,或连通性设置不当。
解决方案:
- 确保目标区域完全封闭
- 检查是否需要先进行形态学闭运算
Q3:为什么需要添加边界扩展?
原因:OpenCV 的漫水填充要求掩码比原图至少大2像素。
解决方案:
- 使用
CopyMakeBorder添加边框 - 填充完成后提取ROI恢复原始尺寸
Q4:彩色图像如何处理?
解决方案:
- 先转换为灰度或二值图像
- 示例代码中已包含彩色到灰度的转换逻辑
Q5:漫水填充与形态学闭运算的区别?
| 特性 | 漫水填充 | 形态学闭运算 |
|---|---|---|
| 孔洞类型 | 内部封闭孔洞 | 边缘小缺口 |
| 填充效果 | 完全填充 | 部分填充 |
| 边界影响 | 不影响边界 | 可能改变边界 |
| 计算速度 | 较慢 | 较快 |