3. 测温数据采集:红外热像仪原理、温度数据读取与预处理
各位同学,咱们今天聊点实在的。测温系统里最核心的一环,就是数据采集。你算法再牛,模型再准,原始数据要是脏的、乱的,那后面全是白搭。我这些年调试测温设备,踩过的坑有一半都出在数据采集这一步。所以这一节,咱们把红外热像仪的原理、数据怎么读、怎么洗,一次性讲透。
3.1 红外热像仪的工作原理
说白了,红外热像仪就是个「温度相机」。普通相机拍可见光,它拍的是红外辐射。任何温度高于绝对零度的物体,都会向外辐射红外线。温度越高,辐射越强。热像仪的核心器件是焦平面阵列探测器,它把接收到的红外辐射转换成电信号,再映射成温度值。
我刚开始接触时有个误区:以为热像仪直接输出温度。其实它输出的是原始数字信号(DN值),需要经过标定曲线换算才能得到温度。不同厂家的标定算法不一样,但原理大同小异。
T = f(DN) —— 原始数字信号 → 温度值
实际工程中,通常用多项式拟合或查表法实现。
3.2 模拟温度数据生成
做开发时,你不可能每次都扛着热像仪调试。所以我习惯先模拟数据。这样算法迭代快,不受硬件限制。下面是我常用的模拟生成方法:
import numpy as np
import random
def generate_temperature_frame(height=24, width=32, base_temp=36.5):
"""
模拟生成一帧测温数据
height, width: 热像仪分辨率(常见低分辨率)
base_temp: 基准温度
"""
# 生成基础温度场
frame = np.ones((height, width)) * base_temp
# 模拟人体区域(中心区域升温)
center_h, center_w = height//2, width//2
frame[center_h-3:center_h+3, center_w-3:center_w+3] += random.uniform(0.5, 1.5)
# 模拟噪声(传感器固有噪声)
noise = np.random.normal(0, 0.1, (height, width))
frame += noise
return frame
# 生成一帧数据
temp_data = generate_temperature_frame()
print(f"温度范围:{temp_data.min():.2f}°C ~ {temp_data.max():.2f}°C")
嗯,这里要注意。模拟数据虽然方便,但噪声模型不能太理想。我曾在项目中直接用高斯噪声模拟,结果算法在真实设备上完全失效。真实热像仪的噪声是空间相关的,还有固定模式噪声。所以模拟时最好加一点「坏像素」和「漂移」。比如:
# 模拟坏像素(死点)
dead_pixels = [(5, 10), (12, 20), (18, 8)]
for h, w in dead_pixels:
frame[h, w] = -999 # 无效值标记
# 模拟温度漂移(随时间缓慢变化)
drift = np.sin(np.linspace(0, 2*np.pi, width)) * 0.2
frame += drift
3.3 温度数据预处理
拿到原始数据后,不能直接用。为什么?因为热像仪天生有三大问题:噪声、非均匀性、坏像素。预处理就是解决这些问题的。我一般分三步走:
3.3.1 去噪处理
热像仪的噪声主要是高斯噪声和椒盐噪声。高斯噪声用滤波,椒盐噪声用中值滤波。我个人习惯先做中值滤波,再做高斯滤波。顺序不能反,否则椒盐噪声会扩散。
from scipy.ndimage import median_filter, gaussian_filter
def denoise_temperature(frame):
# 第一步:中值滤波(去除坏点)
frame_clean = median_filter(frame, size=3)
# 第二步:高斯滤波(平滑噪声)
frame_smooth = gaussian_filter(frame_clean, sigma=1.0)
return frame_smooth
3.3.2 坏像素修复
热像仪用久了,总会有几个像素坏掉。表现为温度值异常(过高或过低)。修复方法很简单:用周围像素的平均值替代。
def fix_bad_pixels(frame, threshold=5.0):
"""
修复坏像素
threshold: 与邻域均值差异超过此值视为坏点
"""
from scipy.ndimage import uniform_filter
# 计算局部均值
local_mean = uniform_filter(frame, size=3)
# 检测坏点
diff = np.abs(frame - local_mean)
bad_mask = diff > threshold
# 修复
frame_fixed = frame.copy()
frame_fixed[bad_mask] = local_mean[bad_mask]
return frame_fixed
3.3.3 归一化处理
归一化是为了让数据落在统一的尺度内,方便后续算法处理。测温场景下,我通常做两种归一化:
| 归一化类型 | 公式 | 适用场景 |
|---|---|---|
| Min-Max归一化 | (x - min) / (max - min) | 温度范围已知且稳定 |
| Z-score标准化 | (x - mean) / std | 温度分布未知,有异常值 |
def normalize_temperature(frame, method='minmax'):
if method == 'minmax':
# 去掉无效值后再归一化
valid = frame > -100
min_val = frame[valid].min()
max_val = frame[valid].max()
normalized = (frame - min_val) / (max_val - min_val + 1e-8)
elif method == 'zscore':
valid = frame > -100
mean = frame[valid].mean()
std = frame[valid].std()
normalized = (frame - mean) / (std + 1e-8)
# 无效值置为0
normalized[~valid] = 0
return normalized
3.4 知识体系总览
下面这张图,是我梳理的本章核心逻辑。你把它存脑子里,后面几章都会用到。
你看,整个流程其实不复杂。但每一步都有细节。我见过很多同学直接拿原始数据跑算法,结果精度一塌糊涂。预处理花的时间,会在后面十倍百倍地省回来。
- 热像仪输出的是DN值,不是温度,需要标定换算
- 模拟数据要包含噪声、坏像素、漂移,不能太理想
- 预处理三步走:去噪 → 修复坏像素 → 归一化
- 滤波核宁小勿大,归一化前先剔除无效值
公众号:蓝海资料掘金营,微信deep3321