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("图像采集与预处理完成")
注意: 实际相机 SDK 接口各不相同,上面的代码是伪代码。你需要根据自己用的相机品牌(如 Basler、Dalsa、Teledyne)替换对应 API。

嗯,到这里图像采集与预处理的核心内容就讲完了。记住一句话:前面调好了,后面标定就顺了。别在这一步偷懒,否则后面全是坑。