第四章:图像分割技术——阈值分割、边缘检测与形态学操作

各位同学,欢迎来到第四章。这一章的内容,说白了就是教你怎么把SEM图像里的缺陷“抠”出来。我做了这么多年半导体工艺,见过太多因为分割没做好导致误判的案例。今天咱们就把阈值分割、边缘检测和形态学操作这三个核心工具彻底讲透。

本章核心逻辑:图像分割的本质就是“找边界”和“分区域”。阈值分割靠灰度值硬切,边缘检测靠梯度变化软找,形态学操作则是修修补补的后期处理。三者配合,才能从复杂的SEM图像中稳定提取缺陷。

图像分割 三大支柱 阈值分割 Otsu / 自适应阈值 基于灰度直方图 边缘检测 Canny / Sobel 基于梯度变化 形态学操作 腐蚀/膨胀/开闭运算 后处理修形 灰度直方图 梯度计算 结构元素

4.1 阈值分割:最直接的分割方式

阈值分割,说白了就是设定一个灰度值门槛。比门槛亮的算一类,比门槛暗的算另一类。在SEM图像里,缺陷区域和背景往往有比较明显的灰度差异,这时候阈值分割就特别好使。

4.1.1 Otsu阈值分割

Otsu算法是我个人最常用的自动阈值方法。它不需要你手动调参,算法自己会算出一个最优阈值。原理很简单:遍历所有可能的阈值,找到那个能让前景和背景的类间方差最大的值。

我的经验:Otsu对直方图呈双峰的图像效果最好。如果SEM图像的灰度直方图只有一个峰(比如背景太亮或太暗),Otsu可能会翻车。我遇到过几次,后来就养成了先看直方图的习惯。

import cv2
import numpy as np

# 读取SEM图像
img = cv2.imread('sem_defect.png', 0)

# Otsu阈值分割
ret, thresh_otsu = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

print(f"Otsu自动计算的阈值: {ret}")
# 输出类似:Otsu自动计算的阈值: 127

4.1.2 自适应阈值

为什么需要自适应阈值?因为SEM图像经常存在光照不均匀的问题。比如晶圆边缘比中心暗,或者扫描电子束的强度有波动。这时候全局阈值就不够用了。

自适应阈值的思想是:对每个像素,只考虑它周围一个小邻域的灰度分布,算出局部阈值。这样就能应对光照不均的情况。

# 自适应阈值
thresh_adaptive = cv2.adaptiveThreshold(
    img, 255, 
    cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 
    cv2.THRESH_BINARY, 
    blockSize=11,  # 邻域大小,奇数
    C=2            # 从均值减去的常数
)

避坑指南:blockSize参数很关键。设太小了容易把噪声当成缺陷,设太大了又失去了自适应的意义。我曾经在一个金属线缺陷检测项目里,把blockSize从11调到21,误检率直接降了一半。建议从11开始试,根据图像分辨率调整。

4.2 边缘检测:找到缺陷的轮廓

阈值分割靠的是灰度值,边缘检测靠的是灰度变化率。说白了,就是找图像中灰度突然变化的地方。缺陷的边缘往往就是这种突变点。

4.2.1 Sobel算子

Sobel算子是最基础的边缘检测方法。它用两个3x3的卷积核,分别计算水平方向和垂直方向的梯度。然后合成得到边缘强度。

# Sobel边缘检测
sobel_x = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)  # 水平梯度
sobel_y = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)  # 垂直梯度

# 合成梯度幅值
sobel_magnitude = np.sqrt(sobel_x**2 + sobel_y**2)
sobel_magnitude = np.uint8(np.clip(sobel_magnitude, 0, 255))

嗯,这里要注意:Sobel对噪声比较敏感。如果SEM图像本身信噪比不高,建议先做一下高斯滤波再跑Sobel。

4.2.2 Canny边缘检测

Canny是业界公认的“黄金标准”。它比Sobel复杂得多,但效果也稳定得多。Canny的流程是:先高斯滤波降噪,再算梯度,然后做非极大值抑制(只保留梯度方向上的局部最大值),最后用双阈值检测和连接边缘。

# Canny边缘检测
edges = cv2.Canny(img, threshold1=50, threshold2=150)

# 双阈值参数说明:
# threshold1: 低阈值,低于此值的像素被丢弃
# threshold2: 高阈值,高于此值的像素被保留为强边缘
# 介于两者之间的,如果与强边缘相连则保留,否则丢弃

实战建议:Canny的两个阈值怎么调?我一般用2:1到3:1的比例。比如低阈值50,高阈值150。如果图像对比度低,可以适当降低两个阈值。我在检测氧化层裂纹时,就经常把阈值降到30和90,才能把细小的裂纹边缘抓出来。

4.3 形态学操作:修修补补的后处理

分割完的图像往往不完美。可能有毛刺、有空洞、有孤立噪点。这时候就需要形态学操作来“整形”。

4.3.1 腐蚀与膨胀

腐蚀:让白色区域“瘦一圈”。可以消除小的白色噪点,但也会让缺陷变小。

膨胀:让白色区域“胖一圈”。可以填补小的空洞,但也会让缺陷变大。

# 定义结构元素(核)
kernel = np.ones((3,3), np.uint8)

# 腐蚀
eroded = cv2.erode(binary_img, kernel, iterations=1)

# 膨胀
dilated = cv2.dilate(binary_img, kernel, iterations=1)

4.3.2 开运算与闭运算

开运算 = 先腐蚀后膨胀。作用是:消除小的噪点,断开细小的连接。说白了就是“去毛刺”。

闭运算 = 先膨胀后腐蚀。作用是:填补小的空洞,连接断开的轮廓。说白了就是“补窟窿”。

# 开运算:先腐蚀后膨胀
opening = cv2.morphologyEx(binary_img, cv2.MORPH_OPEN, kernel)

# 闭运算:先膨胀后腐蚀
closing = cv2.morphologyEx(binary_img, cv2.MORPH_CLOSE, kernel)

我的习惯:在缺陷检测流程里,我通常先做开运算去掉噪声,再做闭运算填补缺陷内部的空洞。顺序不能反,否则噪声会被膨胀放大。有一次我搞反了顺序,结果把一堆噪声点连成了“伪缺陷”,排查了半天才发现问题。

4.4 实战组合拳:一个完整的缺陷分割流程

光讲理论没意思,咱们来一个完整的流程。假设我们要从一张SEM图像中检测出划痕缺陷:

  1. 预处理:高斯滤波去噪,核大小5x5
  2. 边缘检测:Canny算子,阈值50/150
  3. 形态学处理:先开运算(3x3核,1次),再闭运算(5x5核,2次)
  4. 轮廓提取:用cv2.findContours找到所有连通区域
  5. 筛选:根据面积、长宽比等特征过滤掉非缺陷区域
def detect_scratches(img_path):
    # 1. 读取并预处理
    img = cv2.imread(img_path, 0)
    blurred = cv2.GaussianBlur(img, (5,5), 0)
    
    # 2. Canny边缘检测
    edges = cv2.Canny(blurred, 50, 150)
    
    # 3. 形态学操作
    kernel_open = np.ones((3,3), np.uint8)
    kernel_close = np.ones((5,5), np.uint8)
    
    opened = cv2.morphologyEx(edges, cv2.MORPH_OPEN, kernel_open)
    closed = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, kernel_close)
    
    # 4. 找轮廓
    contours, _ = cv2.findContours(closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    # 5. 筛选缺陷(面积大于100像素的才保留)
    defects = []
    for cnt in contours:
        area = cv2.contourArea(cnt)
        if area > 100:
            defects.append(cnt)
    
    return defects

我曾经踩过的坑:在步骤3里,开运算的核大小不能太大。有一次我用7x7的核对划痕做开运算,结果把细划痕直接腐蚀没了。后来我改成3x3,效果就好多了。记住:形态学操作是“手术刀”,不是“大砍刀”,下手要轻。

4.5 本章小结

阈值分割、边缘检测、形态学操作,这三者是SEM图像缺陷识别的基石。阈值分割适合背景均匀的情况,边缘检测能抓出精细的轮廓,形态学操作则是最后的“美容师”。

实际项目中,很少只用一种方法。我通常的做法是:先用Otsu或自适应阈值做粗分割,再用Canny精修边缘,最后用形态学操作清理结果。这套组合拳,我在几十个缺陷检测项目里都用过,效果稳定。

好了,这一章的内容就到这里。记住:多动手试参数,多观察中间结果,才是掌握图像分割的不二法门。


专注资料整理