3. 图像采集与预处理:让相机“看清”缺陷的第一步

做光学检测这么多年,我最大的体会就是——图像预处理做得好,后面算法省一半的力气。你想想看,如果相机拍出来的图本身就乱七八糟的,你再牛的检测算法也白搭。今天我就把这块的核心操作掰开揉碎了讲给你听。

3.1 用OpenCV读取图像:别小看这一步

读取图像,听起来简单吧?但我见过太多新手在这栽跟头。OpenCV读取图像默认是BGR格式,不是我们熟悉的RGB。这坑我踩过不止一次。

⚠️ 注意:OpenCV的imread()默认返回BGR顺序,如果你用matplotlib显示,颜色会完全错乱。我曾经调试一个划痕检测算法,折腾了两小时才发现是颜色通道顺序搞反了。
import cv2
import numpy as np

# 读取图像——我最常用的方式
img = cv2.imread('defect_sample.jpg')

# 检查是否读取成功
if img is None:
    print("图像读取失败,检查路径或文件")
    exit()

# 显示原始图像
cv2.imshow('Original Image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

读取相机流呢?我建议用VideoCapture。记得设置分辨率,不然默认的640x480有时候不够用。

cap = cv2.VideoCapture(0)  # 0代表默认摄像头
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)

ret, frame = cap.read()
if ret:
    cv2.imwrite('captured_frame.jpg', frame)
cap.release()
💡 我的习惯:工业相机一般用GigE或USB3.0接口,读取时加个超时处理,防止相机掉线导致程序卡死。

3.2 灰度化:丢掉颜色,保留结构

为什么要灰度化?说白了,大部分光学缺陷(划痕、脏污、气泡)在灰度图上就能看得很清楚。颜色信息反而会干扰检测。我做过一个项目,检测手机玻璃盖板的划痕,彩色图跑算法要200ms,灰度图只要50ms,准确率还更高。

# 灰度化——三种方式,我常用第一种
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 或者直接以灰度模式读取
gray_img = cv2.imread('defect_sample.jpg', cv2.IMREAD_GRAYSCALE)

你可能会问:那什么时候保留彩色?嗯,当缺陷本身带有颜色特征时,比如PCB板上的氧化变色,这时候彩色信息就有用了。

3.3 直方图均衡化:让暗部细节“浮出水面”

直方图均衡化,说白了就是把图像的灰度分布“拉平”。我遇到过一批产品,因为打光角度问题,图像整体偏暗,缺陷几乎看不见。均衡化之后,那些细微的划痕立马显现出来。

# 全局直方图均衡化
equ = cv2.equalizeHist(gray)

# 对比显示
cv2.imshow('Original Gray', gray)
cv2.imshow('Equalized', equ)

但要注意——全局均衡化有时候会放大噪声。我建议你试试CLAHE(对比度受限自适应直方图均衡化),效果更细腻。

# CLAHE——我的首选
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
clahe_img = clahe.apply(gray)
🔑 关键点:clipLimit控制对比度增强的幅度,太大容易过曝,太小效果不明显。我一般从2.0开始调,根据实际图像微调。

3.4 高斯滤波去噪:平滑但不模糊边缘

高斯滤波,核心思想是用一个高斯核去卷积图像。每个像素的值由它周围像素加权平均得到,权重由高斯分布决定。这样做的好处是——去噪的同时尽量保留边缘

# 高斯滤波——核大小和标准差是关键
blurred = cv2.GaussianBlur(gray, (5, 5), 1.5)

# 参数说明:
# (5,5) 是核大小,必须是奇数
# 1.5 是标准差σ,控制平滑程度

核大小怎么选?我一般遵循这个原则:

核大小 适用场景 我的经验
(3, 3) 轻微噪声,需要保留精细缺陷 检测微小划痕时用
(5, 5) 中等噪声,通用场景 我最常用的尺寸
(7, 7) 强噪声,大缺陷检测 注意会模糊小缺陷
⚠️ 避坑指南:我曾经为了去噪把核设成(15,15),结果缺陷也被抹平了。高斯滤波不是越大越好,够用就行。

3.5 完整流程:从相机到预处理

好了,我们把上面这些串起来,形成一个完整的预处理流水线。这是我做项目时的标准流程:

def preprocess_image(image_path):
    # 1. 读取图像
    img = cv2.imread(image_path)
    if img is None:
        return None
    
    # 2. 灰度化
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # 3. CLAHE增强
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
    enhanced = clahe.apply(gray)
    
    # 4. 高斯滤波去噪
    denoised = cv2.GaussianBlur(enhanced, (5, 5), 1.5)
    
    return denoised

# 调用
result = preprocess_image('defect_sample.jpg')
cv2.imshow('Preprocessed', result)

这个流程我用了好几年,在玻璃、金属、塑料等多种材质上都验证过。当然,具体参数要根据你的实际图像微调。

3.6 知识体系总览

下面这张图,是我梳理的本章核心逻辑。你可以把它当作一个检查清单:

图像采集 相机/图片读取 灰度化 BGR→GRAY 直方图均衡化 CLAHE增强 高斯滤波 去噪平滑 预处理流水线:原始图像 → 灰度 → 增强 → 去噪 → 输出 关键参数:核大小(5,5) | σ=1.5 | clipLimit=2.0

你看,整个流程其实就四个步骤。但每一步都有讲究——灰度化怎么选通道、均衡化用全局还是CLAHE、高斯滤波核多大合适。这些细节,决定了你最终检测算法的成败。

💡 最后说一句:预处理不是越复杂越好。我见过有人把预处理搞了七八步,结果检测速度慢得没法用。记住一个原则——够用就好,简单优先
专注资料整理