跳转至

06 形态学运算#

一句话总结

形态学只有四个基础动作:膨胀(变胖)、腐蚀(变瘦)、开运算(先腐后膨 → 去掉小白点/毛刺)、闭运算(先膨后腐 → 填掉小黑点/裂缝)。它作用在 Region 上,必须搭配二值化使用

源文件: note/05图像预处理.hdevx2 / x4 / x5 / wood_knots 四组练习)

一、四个基础算子#

算子 签名 效果 一句话
dilation_circle (Region : RegionDilation : Radius : ) 区域外扩 Radius 像素 目标"长胖"
erosion_circle (Region : RegionErosion : Radius : ) 区域内缩 Radius 像素 目标"变瘦"
opening_circle (Region : RegionOpening : Radius : ) 先腐蚀 → 再膨胀 去小白点、去毛刺、断开细连接
closing_circle (Region : RegionClosing : Radius : ) 先膨胀 → 再腐蚀 填小黑点、补裂缝、连接断裂
dilation_circle (Region, RegionDilation, 3.5)    * 膨胀半径 3.5 像素
erosion_circle (Region, RegionErosion, 3.5)      * 腐蚀半径 3.5 像素
opening_circle (Region1, RegionOpening, 3.5)     * 开运算
closing_circle (Region2, RegionClosing, 6)       * 闭运算

Radius 的物理含义

Radius结构元(圆盘)的半径,单位是像素。它决定了"多小的东西会被消掉/填掉": - 开运算:凡是宽度小于 2×Radius 的白色结构都会被腐蚀掉,之后膨胀也回不来 → 这就是"去毛刺"的原理。 - 闭运算:凡是宽度小于 2×Radius 的黑色孔洞/缝隙都会被填上。

半径越大,去除/填补能力越强,但也越容易把有用特征一起干掉。

二、原理示意#

flowchart LR
    subgraph OPEN["开运算 opening"]
        direction LR
        O1["原图<br/>白点+白主体"] -->|1 腐蚀| O2["白点消失<br/>主体变瘦"]
        O2 -->|2 膨胀| O3["主体复位<br/>白点不再回来"]
    end
    subgraph CLOSE["闭运算 closing"]
        direction LR
        C1["原图<br/>黑点+白主体"] -->|1 膨胀| C2["黑点被填上<br/>主体变胖"]
        C2 -->|2 腐蚀| C3["主体复位<br/>黑点已是实体"]
    end

顺序绝对不能记反

开运算 = 先腐蚀后膨胀:先"瘦身"把细小的白东西挤掉,再"长回来",主体形状基本复原,但被挤掉的小白点再也长不回来闭运算 = 先膨胀后腐蚀:先"长胖"把小的黑孔糊上,再"瘦回来",主体形状复原,但被糊上的黑孔已经变成实体

记忆口诀:开 → 去掉"白"的小东西;闭 → 去掉"黑"的小东西。 或者记成"开运算让白的变干净,闭运算让白的变完整"。

三、源码逐段解读#

3.1 单独演示膨胀与腐蚀#

read_image (Image2, 'x5.bmp')
threshold (Image2, Region, 128, 255)          * 二值化拿到目标区域

dilation_circle (Region, RegionDilation, 3.5) * 膨胀区域向外扩 3.5 像素
erosion_circle (Region, RegionErosion, 3.5)   * 腐蚀区域向内缩 3.5 像素
stop ()
  • 膨胀后面积变大,细缝被填、相邻目标可能粘连;
  • 腐蚀后面积变小,细连接会断开、小目标可能直接消失。

3.2 开运算:去白毛刺#

read_image (Image3, 'x2.bmp')
threshold (Image3, Region1, 128, 255)
opening_circle (Region1, RegionOpening, 3.5)

x2.bmp 的目标主体边缘带有细白毛刺和孤立小白点,开运算后这些比 2×3.5 = 7 像素更细的结构全部消失,主体保持完好。

3.3 闭运算:填黑点#

read_image (Image4, 'x4.bmp')
threshold (Image4, Region2, 128, 255)
closing_circle (Region2, RegionClosing, 6)

x4.bmp 的白色主体里混着黑色小孔洞(椒盐噪点),闭运算半径取 6(比开运算的 3.5 更大),因为要填的孔洞尺寸更大。

3.4 形态学的真实用法:预处理 + 过滤#

* 形态学调整 一般都是搭配着二值化处理来使用
read_image (Image5, 'wood_knots')
threshold (Image5, Region3, 8, 100)              * 1 二值化木节是暗点 8~100
connection (Region3, ConnectedRegions)           * 2 分割成一个个独立候选
opening_circle (ConnectedRegions, RegionOpening1, 3.5)   * 3 开运算去掉细小毛刺干扰
select_shape (RegionOpening1, SelectedRegions, 'area', 'and', 50, 500)  * 4 按面积筛选
dev_clear_window ()
dev_display (Image5)
dev_display (SelectedRegions)

这是标准工业写法,顺序值得背下来:

flowchart LR
    A["threshold<br/>粗分割"] --> B["connection<br/>打散"]
    B --> C["opening/closing<br/>修形去噪"]
    C --> D["select_shape<br/>按特征精筛"]
    D --> E["测量/输出"]

为什么开运算放在 connection 之后?

从原理上说,开运算放在 connection 之前也可以(结果等价,都是逐区域处理)。放在之后的好处是:中间可以用 stop ()dev_display 观察"开了多少东西",也方便在开运算后立刻接 select_shape。实际项目中两种顺序都常见,关键是理解每一步在做什么。

四、同类算子家族#

除了圆形结构元,HALCON 还提供矩形与任意结构元版本:

家族 圆(对称性最好) 正矩形 带角度矩形
生成结构元 gen_circle gen_rectangle1 gen_rectangle2
膨胀 dilation_circle dilation_rectangle1 dilation2
腐蚀 erosion_circle erosion_rectangle1 erosion2
开运算 opening_circle opening_rectangle1 opening
闭运算 closing_circle closing_rectangle1 closing

其他常用变形:

算子 作用
opening / closing 任意结构元(由 gen_region_* 生成)做开/闭运算
boundary (Region, RegionBorder, 'inner') 取区域边界(常配合 select_shape'area' 做细线提取)
skeleton (Region, Skeleton) 提取骨架(中轴线),用于细长物体
fill_up (Region, RegionFillUp) 填充区域内的所有孔洞(比闭运算更彻底,不看孔的大小)
fill_up_shape 按孔的形状特征填充
shape_trans (Region, Out, 'convex') 形状变换:'convex' 凸包、'ellipse' 等效椭圆、'rectangle1/2' 外接矩形

fill_upclosing_circle 的区别

closing_circle 只能填小于结构元尺寸的孔;fill_up 无视孔的大小,把所有被包围的孔全填上。判断"零件内部应该实心"时,fill_up 往往更省事。

五、参数速查与经验值#

场景 建议
去除孤立小噪点 opening_circle,Radius 略大于噪点半径(如 1.5 ~ 3.5)
去除细毛刺 opening_circle,Radius 取毛刺宽度的一半以上
填补小孔洞 closing_circle,Radius 取孔洞半径的一半以上
把断开的物体连起来 closing_circle,Radius 取断裂间隙的一半以上
需要精确保留尺寸 慎用形态学,它会改变面积;改用 fill_up 或直接筛选

上表的半径值只是\"起点\",可以标定得更准

圆盘结构元下 erode / dilate 等价于距离阈值,所以缺陷的内切圆半径就是 Radius 的理论下限。 本篇第 3.2 / 3.3 节用的 3.5x2.bmp)与 6x4.bmp)属"宁大勿小"的经验值 —— 实测这两张图分别只需 14.5,半径取大会白白多削主体、还会蚕食字本身的环孔。 完整标定流程与实测数据见 [[13 形态学调整 结构元半径标定]]。

形态学会改变面积

膨胀/腐蚀/开/闭都会改变区域的形状与面积。如果后续要测面积、直径、圆度,务必: 1. 形态学只用于"去噪/修形",测量在修形后的区域上做(此时测的是修形后的量); 2. 若必须测原图尺寸,则不要在测量链路上用形态学,改用 select_shape 直接剔除噪点。

六、自检#

Question

  1. 为什么开运算能去毛刺,腐蚀却不能?—— 腐蚀能去掉毛刺,但会让主体整体变瘦;开运算在腐蚀后再膨胀,主体恢复原尺寸,毛刺却回不来。
  2. 想要"忽略小孔、只保留大孔"该怎么写?—— 闭运算用小半径填掉小孔,剩下的大孔保留;或者用 select_shape'holes_num' / 'area_holes' 筛选。
  3. dilation_circle (Region, Out, 10) 之后再 erosion_circle (Out, Out2, 10),结果和 closing_circle (Region, Out2, 10) 一样吗?—— 等价。这就是闭运算的定义。

上一步:图像预处理与滤波 · 下一步:区域测量与结果可视化 · 深入:形态学调整 结构元半径标定