2. 图像预处理实战

图像预处理,说白了就是给原始图像“洗个澡”。

在工业现场,相机拍回来的图很少是完美的。光照不均、传感器噪声、工件抖动,这些问题不处理,后面的检测算法根本跑不动。我刚开始做视觉项目时,就吃过这个亏——算法在实验室跑得好好的,上了产线全崩。后来才发现,是预处理没做扎实。

核心观点:预处理不是可选项,是必选项。它直接决定了后续检测的稳定性和精度。

2.1 图像滤波:去噪是第一步

滤波的目的很简单:保留有用信息,干掉噪声。工业场景中常见的噪声有高斯噪声(传感器热噪声)和椒盐噪声(电磁干扰)。

2.1.1 高斯滤波

高斯滤波用加权平均的方式平滑图像。离中心越近的像素权重越大,符合自然分布。它对付高斯噪声效果很好。

import cv2
import numpy as np

# 读取图像
img = cv2.imread('defect_sample.jpg', cv2.IMREAD_GRAYSCALE)

# 高斯滤波,核大小5x5,sigma=1.5
gaussian_blur = cv2.GaussianBlur(img, (5, 5), 1.5)

cv2.imshow('Gaussian Blur', gaussian_blur)
cv2.waitKey(0)

我的经验:核大小选奇数,3、5、7常用。sigma值一般设1.0~2.0。太大图像会糊,太小去噪不干净。我在检测手机屏幕划痕时,sigma设1.2效果最好。

2.1.2 中值滤波

中值滤波用邻域像素的中位数代替中心像素。它对椒盐噪声有奇效,而且能保留边缘细节。

# 中值滤波,核大小5
median_blur = cv2.medianBlur(img, 5)

cv2.imshow('Median Blur', median_blur)
cv2.waitKey(0)

注意:中值滤波的核大小也是奇数。核越大,去噪越强,但图像细节损失也越大。我曾经在检测PCB焊点时,核设到7,结果把微小裂纹也给滤掉了,漏检率飙升。后来改回3,问题解决。

2.1.3 双边滤波

双边滤波是“保边去噪”的利器。它同时考虑空间距离和像素值差异,边缘处权重降低,所以边缘能保留下来。

# 双边滤波,d=9,sigmaColor=75,sigmaSpace=75
bilateral_blur = cv2.bilateralFilter(img, 9, 75, 75)

cv2.imshow('Bilateral Filter', bilateral_blur)
cv2.waitKey(0)

适用场景:需要保留边缘的缺陷检测,比如划痕、裂纹、毛刺。双边滤波计算量大,实时性要求高的产线慎用。

2.2 图像增强:让缺陷“现形”

增强的目的,是把人眼或算法难以分辨的特征凸显出来。说白了,就是让该亮的地方更亮,该暗的地方更暗。

2.2.1 直方图均衡化

直方图均衡化把图像的灰度分布拉伸到整个范围。对比度低的图像,处理后细节会清晰很多。

# 全局直方图均衡化
equalized = cv2.equalizeHist(img)

cv2.imshow('Equalized', equalized)
cv2.waitKey(0)

避坑指南:全局均衡化有时会过度增强噪声。我建议用CLAHE(自适应直方图均衡化),它分块处理,效果更自然。

# CLAHE
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
clahe_img = clahe.apply(img)

cv2.imshow('CLAHE', clahe_img)
cv2.waitKey(0)

2.2.2 伽马校正

伽马校正通过非线性变换调整图像亮度。gamma值小于1,暗部变亮;大于1,亮部变暗。

# 伽马校正,gamma=0.5
gamma = 0.5
gamma_corrected = np.power(img / 255.0, gamma) * 255.0
gamma_corrected = gamma_corrected.astype(np.uint8)

cv2.imshow('Gamma Corrected', gamma_corrected)
cv2.waitKey(0)

实战经验:检测金属表面反光缺陷时,我常用gamma=0.6~0.8压低高光,让划痕更明显。检测暗色塑料件时,gamma=1.2~1.5提亮暗部。

2.3 图像几何变换:对齐与校正

产线上工件位置不可能完全一致。几何变换的作用,就是把图像统一到标准位置,方便后续处理。

2.3.1 仿射变换

仿射变换保持直线和平行关系。平移、旋转、缩放、剪切都属于仿射变换。它需要3个对应点来计算变换矩阵。

# 仿射变换:旋转+平移
rows, cols = img.shape
M = cv2.getRotationMatrix2D((cols/2, rows/2), 45, 1.0)  # 旋转45度
affine_img = cv2.warpAffine(img, M, (cols, rows))

cv2.imshow('Affine Transform', affine_img)
cv2.waitKey(0)

我的习惯:做仿射变换前,先用模板匹配或特征点定位找到工件的基准点。这样变换后的图像才能和模板对齐。

2.3.2 透视变换

透视变换可以改变视角,把倾斜的物体“拉正”。它需要4个对应点,变换后直线仍然是直线,但平行关系不一定保持。

# 透视变换:矫正倾斜的工件
pts1 = np.float32([[50,50],[200,50],[50,200],[200,200]])
pts2 = np.float32([[0,0],[300,0],[0,300],[300,300]])
M = cv2.getPerspectiveTransform(pts1, pts2)
perspective_img = cv2.warpPerspective(img, M, (300, 300))

cv2.imshow('Perspective Transform', perspective_img)
cv2.waitKey(0)

注意:透视变换计算量大,实时性要求高的场景要优化。我曾在一条每小时检测6000个零件的产线上,把透视变换换成仿射变换,速度提升了30%,精度没降。

2.4 本章知识体系

下面这张图总结了本章的核心内容,方便你快速回顾。

图像预处理实战知识体系 图像滤波 图像增强 几何变换 高斯滤波 中值滤波 双边滤波 直方图均衡化 CLAHE 伽马校正 仿射变换 透视变换 核心原则 1. 滤波去噪,保留边缘 2. 增强对比,凸显缺陷 3. 几何对齐,统一标准 4. 先滤波后增强,最后几何变换

2.5 三种滤波对比

滤波类型 适用噪声 边缘保留 计算速度 典型场景
高斯滤波 高斯噪声 一般 快 传感器热噪声、光照均匀场景
中值滤波 椒盐噪声 好 中等 电磁干扰、焊点检测
双边滤波 高斯噪声 优秀 慢 划痕、裂纹、毛刺检测

总结:预处理没有银弹。不同场景要选不同方法。我的建议是——先试中值滤波,速度快、效果好。如果边缘保留不够,再换双边滤波。高斯滤波适合噪声均匀的场景。

嗯,这一章的内容就到这里。预处理是视觉检测的基石,花时间把它调好,后面的工作会轻松很多。