4、热成像数据预处理:非均匀性校正、坏点替换、温度标定、增益与偏移调整
各位同学,咱们今天聊点硬核的。热成像数据预处理,说白了就是让原始数据变得「能用」。你想想看,从探测器里直接读出来的数据,那叫一个惨不忍睹——有的地方亮、有的地方暗,还有一堆死像素在捣乱。我当年第一次拿到原始数据时,差点以为传感器坏了。后来才知道,嗯,这就是常态。
预处理这步做不好,后面所有算法都是白搭。我个人习惯把这四个步骤串成一条流水线:先做非均匀性校正,再替换坏点,然后做温度标定,最后调增益和偏移。顺序别搞反了,否则你会后悔的。
4.1 非均匀性校正(NUC)
非均匀性校正,简称 NUC。为什么需要它?因为热成像探测器里每个像素的响应曲线都不一样。同一个温度,有的像素输出高,有的输出低。你想想看,如果直接显示,画面就会像一块花布。
我在项目中遇到过最夸张的情况:同一块黑体,左边像素输出 1200,右边输出 800,差了整整 400 个码值。不校正?那画面根本没法看。
核心原理:每个像素的响应可以近似为线性模型:Output = Gain × Radiance + Offset。校正就是求出每个像素的 Gain 和 Offset,然后统一映射到标准曲线上。
常用的方法有两种:
- 两点校正法:采集高温和低温两个黑体图像,计算每个像素的增益和偏移。速度快,适合实时系统。
- 多点校正法:采集多个温度点,用分段线性或多项式拟合。精度高,但计算量大。
我个人习惯在产线校准阶段用多点校正,把系数存到 Flash 里。运行时只用两点校正做微调,这样既保证精度又不拖速度。
小技巧:校正时别忘了做「快门校正」。很多热成像模组内部有个机械快门,用它做参考源,可以自动补偿环境温度变化带来的漂移。我一般每 5 分钟触发一次快门校正,效果不错。
4.2 坏点替换
坏点,就是那些「死掉」的像素。有的永远输出最大值(亮斑),有的永远输出最小值(暗斑),还有的随机跳变(闪烁点)。
我曾经调试过一批探测器,坏点率高达 0.5%。你算算,640×480 的分辨率,差不多 1500 个坏点。不处理的话,画面就像撒了一把芝麻。
坏点替换的流程分两步:
- 坏点检测:在均匀背景下,统计每个像素的均值和方差。偏离均值超过 3σ 的,标记为坏点。
- 坏点替换:用周围正常像素的均值或中值替换。我推荐用 3×3 邻域的中值滤波,对孤立坏点效果很好。
// 坏点替换示例(C语言伪代码)
for (int y = 1; y < height-1; y++) {
for (int x = 1; x < width-1; x++) {
if (bad_pixel_map[y][x] == 1) {
// 取3x3邻域内正常像素的中值
int values[8];
int count = 0;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
if (dx == 0 && dy == 0) continue;
if (bad_pixel_map[y+dy][x+dx] == 0) {
values[count++] = raw_data[y+dy][x+dx];
}
}
}
// 排序取中值
quick_sort(values, count);
raw_data[y][x] = values[count / 2];
}
}
}
注意:坏点替换不能过度。如果坏点聚集在一起(比如 3×3 区域内有 4 个以上坏点),建议用更大范围的插值,或者直接标记为无效区域。我曾经因为没处理好坏点簇,导致画面出现「马赛克」——那叫一个难看。
4.3 温度标定
温度标定,就是把像素的灰度值转换成实际温度。说白了,就是建立「码值 → 温度」的映射关系。
为什么需要标定?因为探测器输出的是辐射强度,不是温度。中间隔着发射率、大气透过率、环境反射等一系列因素。不标定的话,你看到的只是「相对温度」,不是「绝对温度」。
标定的标准流程是这样的:
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 准备黑体 | 高精度黑体,温度范围覆盖 -20°C ~ 150°C |
| 2 | 采集数据 | 每隔 5°C 采集一帧图像,记录每个像素的码值 |
| 3 | 拟合曲线 | 用多项式或分段线性拟合码值-温度关系 |
| 4 | 生成查找表 | 将拟合结果存入 LUT,运行时查表转换 |
我个人习惯用 5 阶多项式拟合,精度可以做到 ±0.1°C。不过要注意,多项式在端点处容易震荡,所以我会在两端各留 5°C 的余量,超出范围的就用线性外推。
关键点:标定时的环境温度要和实际使用环境一致。如果标定在 25°C 的实验室做,拿到 -10°C 的户外用,误差会很大。我建议做「多温区标定」——在 0°C、25°C、50°C 三个环境温度下分别标定,然后根据当前环境温度插值使用。
4.4 增益与偏移调整
最后一步,增益和偏移调整。这一步的目的是把温度数据映射到显示范围内,让画面看起来舒服。
你想想看,如果场景温度范围是 20°C ~ 40°C,但显示器的灰度范围是 0~255。直接映射的话,20°C 对应 0,40°C 对应 255,中间 1°C 对应 12.75 个灰度级——对比度足够了。但如果场景温度范围是 -10°C ~ 100°C,1°C 只对应 2.3 个灰度级,画面就会灰蒙蒙的。
增益和偏移的调整公式很简单:
Display_Value = (Temperature - T_min) / (T_max - T_min) * 255
其中 T_min 和 T_max 是显示窗口的上下限。增益就是 255 / (T_max - T_min),偏移就是 -T_min / (T_max - T_min) * 255。
我在项目中遇到过一个问题:场景温度变化剧烈时,固定窗口会导致画面过曝或过暗。后来我用了「自适应窗口」——根据当前帧的温度直方图,自动调整 T_min 和 T_max。比如取直方图的 2% 和 98% 分位点作为窗口边界,这样不管场景怎么变,画面都能保持良好对比度。
实战经验:自适应窗口虽然好用,但要注意「抖动」问题。如果每帧都重新计算窗口,画面会闪烁。我一般用滑动平均——当前窗口 = 0.9 × 上一帧窗口 + 0.1 × 当前帧计算窗口。这样既响应快,又不会闪。
好了,这四个步骤讲完了。非均匀性校正解决「花屏」,坏点替换解决「麻点」,温度标定解决「准不准」,增益偏移解决「好不好看」。每一步都有坑,但踩过之后你就知道怎么绕过去了。
嗯,今天就到这里。代码和公式都在上面了,你们自己跑一遍试试。有问题随时找我。
公众号:蓝海资料掘金营,微信deep3321