第四章:图像形态学操作:腐蚀、膨胀、开运算、闭运算、形态学梯度、顶帽、黑帽,以及在缺陷检测中的应用场景

各位同学,欢迎来到第四章。这一章我们要聊的是形态学操作,说白了就是图像处理里的「形状学」。你想想看,工业缺陷检测里,很多缺陷其实就是形状上的异常——划痕、气泡、毛刺、空洞。形态学操作,就是专门用来处理这些形状问题的利器。

我个人习惯把形态学操作比作「图像的手术刀」。你可以用它来切除多余的噪点,也可以用它来填补断裂的缺陷。嗯,话不多说,我们直接开干。

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 形态学操作在缺陷检测中的综合应用

最后,我整理了一个表格,方便你快速查阅不同形态学操作在缺陷检测中的应用场景:

形态学操作 主要作用 典型应用场景
腐蚀 去除小噪点,分离粘连物体 去除二值化后的孤立噪点
膨胀 填补空洞,连接断裂 连接断裂的划痕、裂纹
开运算 去外噪,平滑轮廓 去除背景噪点,分离粘连缺陷
闭运算 补内洞,连接断裂 填补气泡、砂眼内部空洞
形态学梯度 提取边缘 缺陷边缘检测、尺寸测量
顶帽运算 提取亮色细节 检测划痕、凸起、亮点
黑帽运算 提取暗色细节 检测凹坑、砂眼、裂纹

最后提醒一句: 形态学操作不是万能的。它处理的是「形状」问题,而不是「灰度」问题。如果你的缺陷和背景的灰度差异很小,形态学操作也救不了你。这时候,你需要先做对比度增强或者频域滤波。嗯,这些内容我们后面的章节会讲到。

好了,这一章就到这里。记住:形态学操作是缺陷检测的「基本功」,练好了,后面的路就好走了。下一章,我们会聊图像分割——如何把缺陷从背景中「切」出来。到时候见。