4. 图像采集与预处理:触发模式、行频、增益与去噪
图像采集这一步,说白了就是让相机“听话”地拍照。线扫相机不像面阵相机那样“咔嚓”一下完事,它是一行一行地扫。怎么让它扫得准、扫得清楚,全靠触发模式、行频、增益这些参数配合。我做了这么多年标定,见过太多因为这一步没调好,后面算法怎么跑都跑不对的案例。
4.1 触发模式:外触发 vs 内触发
触发模式决定了相机什么时候开始采集。两种模式各有各的用场,我分别说说。
4.1.1 内触发(Free Run)
内触发模式下,相机按照内部时钟自己跑,不需要外部信号。适合做静态测试、实验室调试。但用在产线上?我劝你别这么干。因为传送带速度一波动,图像就拉伸或压缩了。
4.1.2 外触发(External Trigger)
外触发依赖外部编码器或PLC给的脉冲信号。每来一个脉冲,相机扫一行。这样图像的行间距就和物理运动严格对应,不会变形。
我曾经在一个项目里,客户坚持用内触发,结果图像里的产品尺寸怎么量都不对。后来换成外触发,问题一秒解决。你想想看,传送带速度有波动,内触发根本扛不住。
| 对比项 | 内触发 | 外触发 |
|---|---|---|
| 信号来源 | 相机内部时钟 | 外部编码器/PLC |
| 适用场景 | 实验室、静态调试 | 产线、运动物体 |
| 图像一致性 | 受速度波动影响 | 严格对应物理位置 |
| 标定推荐 | 不推荐 | 强烈推荐 |
4.2 行频计算:让图像不变形
行频,就是相机每秒扫多少行。单位是 kHz(千行/秒)。行频没算对,图像要么拉长,要么压扁。我见过有人直接随便填一个数,结果标定板上的圆都变成椭圆了。
行频的计算公式其实很简单:
行频 (Hz) = 运动速度 (mm/s) / 像素精度 (mm/pixel)
举个例子:传送带速度 100 mm/s,像素精度要 0.05 mm/pixel。那么行频 = 100 / 0.05 = 2000 Hz,也就是 2 kHz。
实际项目中,我一般会留 10%~20% 的余量。因为编码器信号可能有抖动,留点余量能保证每行都完整采集。
4.3 增益与曝光时间调节
这两个参数直接影响图像亮度。但很多人搞混它们的作用。
4.3.1 曝光时间
曝光时间决定了传感器收集光的时间。线扫相机里,曝光时间不能太长,否则一行还没扫完,下一行信号就来了。我一般把曝光时间控制在行周期的 80% 以内。
4.3.2 增益
增益是放大电信号。说白了,就是图像不够亮时,硬拉亮度。但增益会放大噪声。我个人的原则是:先调曝光时间,曝光时间调到头还不够亮,再动增益。
| 参数 | 作用 | 副作用 | 调节优先级 |
|---|---|---|---|
| 曝光时间 | 增加进光量 | 可能拖影 | 第一优先 |
| 增益 | 放大信号 | 放大噪声 | 最后手段 |
4.4 图像去噪:让数据干净点
图像噪声是标定的大敌。噪声会让特征点提取不准,标定结果自然就飘了。常用的去噪方法有几种:
- 均值滤波: 简单,但会模糊边缘。标定板上的圆点边缘一模糊,圆心就偏了。
- 高斯滤波: 比均值好一点,但同样有边缘模糊问题。
- 中值滤波: 对椒盐噪声效果好,而且能保边缘。我个人比较喜欢。
- 双边滤波: 保边去噪,但速度慢。离线标定可以用,在线就别想了。
4.5 本章知识体系
下面这张图把本章的核心逻辑串起来了。从触发模式开始,到行频计算,再到增益曝光调节,最后去噪。每一步都影响最终图像质量。
4.6 实战脚本片段
下面是一个简单的 Python 脚本片段,演示如何设置外触发、计算行频、调节曝光和增益,并做中值滤波。你可以直接拿去改。
import cv2
import numpy as np
# 相机参数设置(以某品牌线扫相机为例)
camera = Camera() # 假设已实例化
# 1. 设置外触发模式
camera.set_trigger_mode('external') # 'external' 或 'internal'
# 2. 计算行频
speed = 100.0 # mm/s
pixel_resolution = 0.05 # mm/pixel
line_rate = speed / pixel_resolution # 2000 Hz
camera.set_line_rate(int(line_rate * 1.1)) # 留10%余量
# 3. 调节曝光和增益
camera.set_exposure_time(400) # 微秒
camera.set_gain(0) # 尽量用0,不够亮加光源
# 4. 采集一行图像并去噪
raw_line = camera.capture_line()
denoised_line = cv2.medianBlur(raw_line, 3) # 中值滤波,核大小3
print(f"行频设置为: {int(line_rate * 1.1)} Hz")
print("图像采集与预处理完成")
嗯,到这里图像采集与预处理的核心内容就讲完了。记住一句话:前面调好了,后面标定就顺了。别在这一步偷懒,否则后面全是坑。