第四章:图像采集与预处理——散斑图像采集技巧、图像去噪与增强
各位同学,大家好。我是你们的老朋友,一个在光学测量和硬件堆里摸爬滚打了十几年的工程师。今天咱们聊点实操的东西——散斑图像的采集与预处理。
说实话,散斑测量这活儿,七分靠采集,三分靠算法。你采集阶段要是搞砸了,后面算法再牛也白搭。我自己就吃过这个亏,所以今天我把压箱底的经验都掏出来,咱们一步步拆解。
4.1 散斑图像采集技巧:别让源头出问题
采集散斑图像,说白了就是“拍一张好照片”。但这里的“好”,跟咱们平时自拍可不一样。它要求高对比度、高信噪比,还得保证散斑颗粒大小合适。
我个人习惯,在搭建采集系统时,先问自己三个问题:
- 光源稳不稳? 激光器必须预热至少15分钟。我见过有人一开机就拍,结果前几张图亮度一直在漂,数据全废了。
- 相机曝光时间对不对? 散斑是干涉形成的,曝光时间太长,散斑会“糊”掉;太短,噪声又大。一般我建议先试几个曝光值,找到那个“刚好不过曝、背景又干净”的点。
- 被测表面有没有震动? 哪怕是一丝微小的震动,都会让散斑图出现“鬼影”。我曾经在实验室里因为空调风太大,导致数据重复性极差,后来加了个隔振台才搞定。
4.2 图像去噪:把脏东西洗掉
采集回来的散斑图,多少都有噪声。可能是传感器热噪声,也可能是环境光干扰。去噪这一步,我把它比作“洗脸”——洗得太轻,脏东西还在;洗得太狠,把五官都搓没了。
常用的三种滤波方法,我挨个说说我的使用心得。
4.2.1 均值滤波:简单但粗暴
均值滤波的原理,就是用一个窗口(比如3x3)扫过图像,把窗口内所有像素的灰度值取平均,代替中心像素的值。
// 均值滤波核心代码(C风格伪码)
for (int y = 1; y < height-1; y++) {
for (int x = 1; x < width-1; x++) {
int sum = 0;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
sum += img[y+dy][x+dx];
}
}
img_filtered[y][x] = sum / 9;
}
}
优点: 速度快,对高斯噪声有效。
缺点: 会模糊边缘。散斑的边界信息很重要,用均值滤波容易把散斑颗粒之间的边界“抹平”。
我的建议: 如果你只是做粗略的位移测量,可以用。但做高精度应变测量,慎用。
4.2.2 中值滤波:保边去噪的好手
中值滤波不是取平均,而是取窗口内所有像素的“中位数”。
// 中值滤波核心代码(C风格伪码)
for (int y = 1; y < height-1; y++) {
for (int x = 1; x < width-1; x++) {
// 收集窗口内9个像素值
int values[9];
int idx = 0;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
values[idx++] = img[y+dy][x+dx];
}
}
// 排序并取中位数
sort(values, 9);
img_filtered[y][x] = values[4]; // 第5个是中位数
}
}
这玩意儿对椒盐噪声(就是那种黑白点)特别有效。而且它不会模糊边缘,因为中位数不受极端值影响。
我遇到过的情况: 有一次在工厂现场采集,环境光有频闪,导致图像上出现周期性的亮暗条纹。用均值滤波根本去不掉,但中值滤波一上,干净多了。嗯,这里要注意,中值滤波计算量比均值大,实时性要求高的场合要权衡。
4.2.3 高斯滤波:更自然的平滑
高斯滤波跟均值滤波类似,但它的权重不是平均的,而是符合高斯分布——离中心越近的像素,权重越大。
// 高斯滤波(3x3核,sigma≈0.85)权重示例
// 核矩阵:
// 1 2 1
// 2 4 2
// 1 2 1
// 归一化因子 = 1/16
for (int y = 1; y < height-1; y++) {
for (int x = 1; x < width-1; x++) {
int sum = 0;
sum += img[y-1][x-1] * 1;
sum += img[y-1][x] * 2;
sum += img[y-1][x+1] * 1;
sum += img[y][x-1] * 2;
sum += img[y][x] * 4;
sum += img[y][x+1] * 2;
sum += img[y+1][x-1] * 1;
sum += img[y+1][x] * 2;
sum += img[y+1][x+1] * 1;
img_filtered[y][x] = sum / 16;
}
}
高斯滤波的效果比均值滤波更“自然”,因为它更尊重中心像素的值。在散斑处理中,我常用它来做轻微的平滑,保留大部分细节。
| 滤波类型 | 去噪能力 | 边缘保留 | 计算速度 | 适用场景 |
|---|---|---|---|---|
| 均值滤波 | 中等 | 差 | 快 | 粗略测量、高斯噪声 |
| 中值滤波 | 强(椒盐噪声) | 好 | 中等 | 有异常点、椒盐噪声 |
| 高斯滤波 | 中等偏上 | 较好 | 快 | 通用平滑、保留细节 |
4.3 图像增强:让特征更明显
去噪之后,图像干净了,但可能对比度不够。散斑图如果对比度低,后续相关计算精度会下降。这时候就需要图像增强。
最经典的方法就是直方图均衡化。
4.3.1 直方图均衡化:把灰度拉开
直方图均衡化的核心思想,就是把图像的灰度分布“拉伸”开,让原本集中在某个小范围的灰度值,均匀分布到整个0-255区间。
你想想看,如果一张散斑图所有像素的灰度值都在100-120之间,那看起来就是灰蒙蒙一片。均衡化之后,黑的更黑,白的更白,对比度一下就上来了。
// 直方图均衡化核心步骤(伪码)
// 1. 统计灰度直方图
int hist[256] = {0};
for (int i = 0; i < totalPixels; i++) {
hist[img[i]]++;
}
// 2. 计算累积分布函数(CDF)
int cdf[256];
int sum = 0;
for (int i = 0; i < 256; i++) {
sum += hist[i];
cdf[i] = sum;
}
// 3. 映射新灰度值
int cdf_min = cdf[0]; // 第一个非零的CDF值
for (int i = 0; i < totalPixels; i++) {
int old_gray = img[i];
int new_gray = (cdf[old_gray] - cdf_min) * 255 / (totalPixels - cdf_min);
img_enhanced[i] = new_gray;
}
注意: 直方图均衡化可能会放大噪声。所以我的流程是:先滤波去噪,再做均衡化。顺序别搞反了。
4.4 本章知识体系总览
下面这张图,是我自己画的整个预处理流程。你可以把它贴在工位上,每次做实验前扫一眼,保证不遗漏步骤。
这张图把整个流程串起来了。从采集开始,到去噪,再到增强,每一步都有对应的技术选型。我个人习惯在采集阶段多花点时间,因为源头好了,后面处理就轻松。
好了,这一章的内容就到这里。记住,预处理不是万能的,但没有预处理是万万不能的。下一章咱们会聊到散斑图像的相关计算,那才是真正出结果的地方。但如果你预处理没做好,再好的算法也救不了你。