第四章:图像形态学操作:腐蚀、膨胀、开运算、闭运算、形态学梯度、顶帽、黑帽,以及在缺陷检测中的应用场景
各位同学,欢迎来到第四章。这一章我们要聊的是形态学操作,说白了就是图像处理里的「形状学」。你想想看,工业缺陷检测里,很多缺陷其实就是形状上的异常——划痕、气泡、毛刺、空洞。形态学操作,就是专门用来处理这些形状问题的利器。
我个人习惯把形态学操作比作「图像的手术刀」。你可以用它来切除多余的噪点,也可以用它来填补断裂的缺陷。嗯,话不多说,我们直接开干。
4.1 腐蚀与膨胀:最基础的两个操作
腐蚀和膨胀,是所有形态学操作的根基。你理解了这两个,后面的开闭运算、梯度什么的,就是它们的排列组合。
4.1.1 腐蚀:让白色区域「瘦一圈」
腐蚀的原理很简单:用一个结构元素(比如3x3的方形)在图像上滑动。只有当结构元素覆盖的区域全是白色(前景)时,中心点才保留为白色。否则,就变成黑色(背景)。
结果就是:白色区域会缩小一圈。边缘的白色像素会被「腐蚀」掉。
在缺陷检测中,腐蚀有什么用?
- 去除噪点: 二值化后,图像上经常有一些孤立的小白点。腐蚀一下,这些小点就没了。
- 分离粘连的物体: 两个缺陷靠得太近,连在一起了?腐蚀一下,把它们分开。
- 细化边缘: 有时候我们需要精确测量缺陷的尺寸,腐蚀可以帮助我们找到更准确的边缘位置。
核心要点: 腐蚀操作会消除物体的边界点,使边界向内部收缩。可以用来消除小且无意义的物体。
4.1.2 膨胀:让白色区域「胖一圈」
膨胀和腐蚀正好相反。结构元素覆盖的区域里,只要有一个白色像素,中心点就变成白色。结果就是白色区域向外扩张一圈。
在缺陷检测中,膨胀有什么用?
- 填补空洞: 缺陷内部有断裂或小孔?膨胀一下,把它们填上。
- 连接断裂的缺陷: 一条划痕断成了几截?膨胀一下,让它们连起来。
- 增强特征: 有时候缺陷太细、太淡,膨胀可以增强它的可见性。
我的经验: 我在做PCB焊点检测时,经常遇到焊点边缘有微小的断裂。直接用膨胀,会把整个焊点都变大,导致误检。后来我改用「先膨胀后腐蚀」的开运算,效果就好多了。你想想看,这就是为什么我们要学组合操作。
4.2 开运算与闭运算:去噪与补洞的黄金搭档
开运算 = 先腐蚀,后膨胀。闭运算 = 先膨胀,后腐蚀。就这么简单,但效果天差地别。
4.2.1 开运算:先腐蚀,后膨胀
开运算的作用是:去除小的噪点,同时保持大物体的形状基本不变。
为什么会这样?因为腐蚀先去掉小噪点,然后膨胀又把大物体「恢复」回来。但那些被腐蚀掉的小噪点,膨胀是恢复不了的——因为它们已经没了。
应用场景:
- 去除背景噪点: 二值化后,背景上有很多小白点。开运算一下,干净多了。
- 分离粘连的缺陷: 两个缺陷之间只有很细的连接,开运算可以断开它们。
- 平滑物体轮廓: 缺陷边缘有毛刺?开运算可以磨平它们。
避坑指南: 我曾经在检测金属表面划痕时,直接用开运算去噪。结果发现,一些很细的划痕也被去掉了。后来我调整了结构元素的大小——用3x3的核去噪,用5x5的核保留划痕。记住:结构元素的大小,决定了你要保留的最小特征尺寸。
4.2.2 闭运算:先膨胀,后腐蚀
闭运算的作用是:填补物体内部的小空洞,同时保持大物体的形状基本不变。
原理和开运算类似,但顺序相反。膨胀先填上空洞,然后腐蚀把物体「恢复」回来。那些被填上的空洞,腐蚀是「挖」不出来的。
应用场景:
- 填补缺陷内部的空洞: 气泡、砂眼、裂纹内部的断裂。
- 连接断裂的缺陷: 一条划痕断成了几段,闭运算可以连起来。
- 平滑物体内部边缘: 缺陷内部有锯齿状边缘?闭运算可以磨平。
一句话总结: 开运算去「外噪」,闭运算补「内洞」。这两个操作,是我在缺陷检测项目中使用频率最高的形态学操作。
4.3 形态学梯度:提取边缘的利器
形态学梯度 = 膨胀图 - 腐蚀图。说白了,就是物体的「膨胀边界」减去「腐蚀边界」,得到的就是物体的轮廓。
这个操作在缺陷检测中非常有用:
- 提取缺陷边缘: 很多缺陷的检测,最终都要靠边缘信息来判断。
- 测量缺陷尺寸: 有了边缘,就可以计算缺陷的长、宽、面积。
- 检测边缘缺陷: 比如毛刺、缺口,这些本身就是边缘的异常。
我个人习惯用形态学梯度来替代Canny边缘检测,尤其是在光照不均匀的场景下。因为形态学梯度对光照变化不那么敏感,而且计算速度快。
4.4 顶帽与黑帽:提取亮/暗细节的绝招
这两个操作,很多人觉得抽象。其实说白了很简单:
- 顶帽运算 = 原图 - 开运算结果。开运算去掉了小亮点,所以顶帽提取的就是那些被去掉的小亮点。
- 黑帽运算 = 闭运算结果 - 原图。闭运算填上了小暗点,所以黑帽提取的就是那些被填上的小暗点。
应用场景:
- 顶帽: 提取亮色缺陷,比如金属表面的亮点、划痕、凸起。
- 黑帽: 提取暗色缺陷,比如凹坑、砂眼、裂纹。
我的经验: 我在做手机玻璃盖板检测时,经常用顶帽运算来提取划痕。因为划痕在玻璃上是亮色的,而背景是暗色的。顶帽运算可以很好地抑制背景,只保留划痕。嗯,这里要注意:结构元素的大小要略大于划痕的宽度,否则效果会打折扣。
4.5 实战案例:用形态学操作检测金属表面划痕
好了,理论讲完了。我们来看一个完整的实战案例。假设我们要检测金属表面的划痕。
步骤1:读取图像并灰度化
import cv2
import numpy as np
img = cv2.imread('metal_scratch.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
步骤2:顶帽运算提取亮色划痕
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 3))
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, kernel)
这里我用了一个15x3的矩形核。为什么是矩形?因为划痕是长条形的。为什么是15x3?因为划痕的宽度大约3个像素,长度15个像素。你想想看,结构元素的形状和大小,要跟你的缺陷特征匹配。
步骤3:二值化
_, binary = cv2.threshold(tophat, 30, 255, cv2.THRESH_BINARY)
步骤4:开运算去噪
kernel_clean = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
cleaned = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel_clean)
步骤5:闭运算连接断裂的划痕
kernel_connect = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 1))
connected = cv2.morphologyEx(cleaned, cv2.MORPH_CLOSE, kernel_connect)
步骤6:查找轮廓并标记缺陷
contours, _ = cv2.findContours(connected, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
area = cv2.contourArea(cnt)
if area > 50: # 过滤掉太小的噪点
x, y, w, h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 0, 255), 2)
cv2.imshow('Result', img)
cv2.waitKey(0)
关键点总结:
- 顶帽运算提取亮色划痕
- 开运算去除背景噪点
- 闭运算连接断裂的划痕
- 面积过滤去除小噪点
4.6 形态学操作在缺陷检测中的综合应用
最后,我整理了一个表格,方便你快速查阅不同形态学操作在缺陷检测中的应用场景:
| 形态学操作 | 主要作用 | 典型应用场景 |
|---|---|---|
| 腐蚀 | 去除小噪点,分离粘连物体 | 去除二值化后的孤立噪点 |
| 膨胀 | 填补空洞,连接断裂 | 连接断裂的划痕、裂纹 |
| 开运算 | 去外噪,平滑轮廓 | 去除背景噪点,分离粘连缺陷 |
| 闭运算 | 补内洞,连接断裂 | 填补气泡、砂眼内部空洞 |
| 形态学梯度 | 提取边缘 | 缺陷边缘检测、尺寸测量 |
| 顶帽运算 | 提取亮色细节 | 检测划痕、凸起、亮点 |
| 黑帽运算 | 提取暗色细节 | 检测凹坑、砂眼、裂纹 |
最后提醒一句: 形态学操作不是万能的。它处理的是「形状」问题,而不是「灰度」问题。如果你的缺陷和背景的灰度差异很小,形态学操作也救不了你。这时候,你需要先做对比度增强或者频域滤波。嗯,这些内容我们后面的章节会讲到。
好了,这一章就到这里。记住:形态学操作是缺陷检测的「基本功」,练好了,后面的路就好走了。下一章,我们会聊图像分割——如何把缺陷从背景中「切」出来。到时候见。