2.3 形态学操作


2.3 形态学操作

本节摘要:形态学操作在二值图上"按形状修正形状":腐蚀收缩白区、膨胀扩张白区,两者组合出开运算(去小白点)、闭运算(填小洞、连断裂)、梯度(描边)、礼帽黑帽(提取亮暗细节)。本节先跑最小例子再讲结构元素的作用,核心是"结构元素形状决定操作的方向性",最后给出噪声点、断裂、空洞三类问题的标准组合拳。

学习目标

阅读完本节,你应当能够:

  1. 用 erode、dilate 各一行代码完成腐蚀与膨胀并解释白区变化
  2. 用 morphologyEx 的五个标志完成开、闭、梯度、礼帽、黑帽
  3. 解释结构元素的形状与尺寸如何影响结果
  4. 针对"噪点、断裂、空洞"三类问题写出对应的形态学组合方案
  5. 说清形态学对灰度图的扩展用法及其边界

先跑起来:腐蚀与膨胀的直观对比

造一张有白色方块的二值图,先腐蚀再膨胀,各看一眼:

import cv2 import numpy as np img = np.zeros((100, 100), dtype=np.uint8) cv2.rectangle(img, (20, 20), (79, 79), 255, -1) # 实心白色方块 kernel = np.ones((5, 5), np.uint8) # 5x5 方形结构元素 eroded = cv2.erode(img, kernel) # 腐蚀:白区四周各缩2像素 dilated = cv2.dilate(img, kernel) # 膨胀:白区四周各扩2像素

运行后一目了然:腐蚀把 60×60 的方块削成约 50×50,膨胀把它扩成约 70×70。方向记法很朴素:腐蚀是"白区被啃,边界收缩",膨胀是"白区长胖,边界外扩"。这个方向性是相对于"前景=白色"的约定,而 OpenCV 的默认约定正是亮区为前景——如果你把目标画成黑底白字,一切叙述都成立;若你的目标是暗区,先反相再操作,否则方向全反。

再跑组合操作,看一次就能记住它们各干什么:

opened = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel) # 开:先腐蚀后膨胀 closed = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) # 闭:先膨胀后腐蚀 grad = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel) # 梯度:膨胀减腐蚀

在方块图上,开和闭的结果看起来都跟原图差不多——因为没有噪声点和空洞,它们的"修正作用"无事可做。往图上撒几个孤立白点、在方块里戳几个黑洞,差异立刻显现:开运算后白点消失、方块原样;闭运算后黑洞被填、方块原样。"先损后补"的设计哲学就在这里:腐蚀那一步本来会把边界啃掉一圈,但随后的膨胀又把边界推回去——只有那些尺寸小于结构元素的干扰物(腐蚀一步就整根拔掉的噪点、膨胀一步就能合拢的窄缝)回不来,于是被精确清除。

核心原理:结构元素与四则组合

2.1 结构元素:形态学的"笔刷"

所有形态学操作都围绕一个形状模板展开——结构元素,它决定"以什么形状的邻域为单位做与/或运算"。3×3 全一是方形笔刷,cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9)) 是圆笔刷,MORPH_RECT 与 MORPH_CROSS 分别给出矩形与十字。

笔刷的形状会"印"在结果上:方形结构元素腐蚀出的边界带直角,圆形结构元素处理出的边界圆润。处理圆形目标(细胞、气泡、硬币)优先椭圆笔刷;处理方形目标(集成电路、建筑平面)用方笔刷;十字笔刷用于只想在横竖方向起作用的场景,比如修复水平细线。

尺寸是力度旋钮:结构元素越大,腐蚀膨胀越猛,开运算能清掉的干扰物尺寸上限也越大。经验起点是 3×3 或 5×5,按"要清除的目标最大有多大"来定——想擦掉直径 8 像素的噪声团,结构元素就得比 8 大,否则擦不干净。

2.2 从两块砖到五面墙

腐蚀和膨胀是两块基本砖,砌墙的规则:

操作 定义 效果 典型用途
开运算 open 先腐蚀后膨胀 去掉小的白色孤立点与毛刺,白区整体不变 清除背景噪声、平滑目标轮廓
闭运算 close 先膨胀后腐蚀 填充小黑洞、连接窄缝断裂 补全目标内部空洞、粘连断裂字符
梯度 gradient 膨胀减腐蚀 得到白色区域边界线 快速提取轮廓骨架
礼帽 tophat 原图减开运算 提取被开运算去掉的亮细节 分离明亮小目标、光照校正
黑帽 blackhat 闭运算减原图 提取闭运算填掉的暗细节 提取暗孔洞、阴影结构

礼帽和黑帽不那么直观,值得多说一句:开运算会把"比结构元素小的亮斑"整体抹掉,那么原图减去开运算的结果,剩下的恰好就是这些亮斑——礼帽等于一个"亮细节滤镜"。照度不均的图上做阈值分割前,礼帽常被用来拉平亮场,这比全局阈值稳得多(与下一节的自适应阈值殊途同归)。

2.3 灰度图上的形态学

腐蚀与膨胀在灰度图上依然有定义:腐蚀取邻域最小值(整体变暗),膨胀取最大值(整体变亮)。开运算于是变成"抹掉亮的小突起",闭运算变成"填掉暗的小坑"。这个扩展让形态学可以不二值化就直接压灰度噪声,但也让效果更难预料——我建议只在二值图上用形态学做形状修正,灰度图上的去噪优先交给第 2 章第 1 节的滤波,各司其职。

工程实践要点

3.1 三类问题的标准配方

直接给结论,配方可以背下来:

  • 噪声点(背景白斑):开运算,结构元素略大于噪声直径。顽固噪声可迭代两次,但注意大核会同时削掉目标的小突起。
  • 断裂(字符笔画断开、目标碎成几块):闭运算,结构元素尺寸要"跨得过断口"。断口 10 像素,核至少 11。
  • 空洞(目标内部黑斑):闭运算;洞特别大时改用轮廓填充(findContours 加 drawContours 的填充模式)更可靠。

实际项目里两者常常连用:先开运算清背景,再闭运算补目标,顺序不要反——先闭会把噪声点也"补"得更结实,给后面添堵。

3.2 迭代次数与等效大核

morphologyEx 的 iterations 参数可以重复执行,开运算迭代 2 次的效果接近用更大结构元素开 1 次,但不完全等价(细节上有差异)。需要强力度时,我更倾向直接换大结构元素,语义清晰;迭代次数留给"同一核稍微多修一遍"的微调场景。

3.3 别在错误的颜色约定里干活

如果你的前景是暗的(白底黑字),腐蚀与膨胀的效果整体反过来:腐蚀会"加粗"黑字。两个选择:先 bitwise_not 反相成白前景,操作完再反回去;或者接受反方向语义并在注释里写明。团队里统一"亮前景"约定,能省掉无数沟通成本。

⚠️ 常见坑:结构元素开得太大,把目标本体也啃掉。比如 5×5 的核开运算,会把所有宽度小于 5 的白色细线整条删除——细线检测场景用开运算提纯时,务必先量一下线宽。另一个坑是对彩色图直接做形态学,三通道各自运算后颜色交界处会串色,先转灰度或 HSV。

3.4 动手实验清单

  1. 在方块图上撒白点、戳黑洞,分别用开、闭运算处理,验证"点被清、洞被填、方块原样"。
  2. 用形态学梯度提取一幅图中所有目标的边界,与第 2 章第 5 节的 Canny 结果对比——形态学梯度只对二值图有意义,Canny 作用于灰度图。
  3. 生成 3×3、7×7、11×11 三种椭圆结构元素,对同一张含噪二值图做开运算,观察"清除上限"的变化。
  4. 找一张笔画断裂的验证码式图片,用闭运算把断笔连起来,记录最小可用核尺寸。

3.5 形态学家族全景图

四组组合操作的效果定位

四组组合操作的效果定位

3.6 常见疑问与解答

形态学梯度与 Canny 的边界有什么区别?

输入前提不同:形态学梯度只对二值图有意义(白区的边界线),Canny 作用于灰度图(灰度突变的边缘)。二值图的边界用形态学梯度一步到位且没有参数可调;灰度图的结构提取必须走 Canny 的四阶段(详见第 2 章第 5 节)。混用两者的输出做下游,是初学者常见的"图不对题"。

结构元素要不要旋转方向?

方与椭圆核是各向同性的(旋转不变),十字核有明确的方向性。想"只清理水平方向的细刺、不动竖直结构"这类需求,用横放的矩形核或十字核就能实现方向选择性。核的形状是"方向偏好"的开关,比想象中常用。

开闭连用时核要一样大吗?

不必。清背景的噪声直径与目标内空洞的尺寸通常不同,两步各用各的核更合理。连用的顺序(先开后闭)是共识,但两步的力度独立标定——用同一核"一档到底"是偷懒,效果上限受限。

3.7 实战配方:掩码净化的标准三步

第 2 章第 4 节产出的二值掩码,到第 4、5 章可用目标框之间,隔着一段几乎固定的净化流程,把它固化成配方。

第一步开运算清背景碎斑。结构元素按"最大噪声团直径加一"取奇数核,椭圆形状对无方向性的斑点最稳。清完检查目标本体有没有被啃(本节 3.3 的线宽警告):目标出现缺口说明核过大,回调。

第二步闭运算补目标空洞。核按"最大空洞口径"取,必要时迭代两次(小核两次比超大核一次温和)。这一步之后再轮廓提取,得到的边界连续性会好得多。

第三步轮廓级过滤兜底。findContours 找连通域,按面积与长宽比过滤:面积下限砍掉残余碎块,长宽比过滤掉细长的伪目标(划痕、反光条)。到这里,"可用的目标候选框"就交付了——第 4 章的传统检测与第 5 章的运动分析,吃的都是这个规格的输入。

三步的顺序与力度标定法(按清除目标的尺寸定核)都来自本节原理,配方本身没有新知识,新的是把"判断→选核→验证"的循环压缩成肌肉记忆。

本节要点回顾

  • 两块砖:腐蚀收缩白区、膨胀扩张白区,方向约定是亮区为前景,反相场景先统一颜色约定。
  • 五面墙:开去白点、闭填黑洞、梯度描边、礼帽取亮细节、黑帽取暗细节,全部由腐蚀膨胀组合而来。
  • 结构元素是尺子:形状(方、椭圆、十字)决定方向偏好,尺寸决定"清什么、留什么"的分界线。
  • 配方三连:噪点用开、断裂与空洞用闭、先开后闭的顺序别反。
  • 主场在二值图:灰度形态学存在但难控,灰度域去噪交给滤波,形状修正交给形态学。
  • 尺寸联动迭代:需要更强力度优先换大核,iterations 只做微调。

二值图是形态学的主场,那灰度图怎么变成二值图?下一节的阈值分割正是这座桥——固定、自适应、Otsu 三档怎么选,一次说清。

常见追问:结构元素必须是对称的吗

不必。椭圆形结构元素本身就是不对称的,用它做膨胀时朝长轴方向的扩张更明显,处理斜向条带状目标时比矩形核更好用。还能用 getStructuringElement 自定义任意形状,甚至定义一个"只有左边有像素"的偏置核,专门做特定方向的桥接。核的形状、尺寸两个维度都值得一试,很多"开闭运算不奏效"的场景,换个形状就通了。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U