3. 训练数据准备与合成
做AI降噪算法,最头疼的问题是什么?我个人觉得,不是模型结构选哪个,而是——训练数据从哪来。
你想想看,如果拿公开数据集训练出来的模型,放到自家ISP的RAW图上,效果往往惨不忍睹。为什么?因为每个传感器的噪声特性都不一样。所以这一章,咱们就聊聊怎么搞定训练数据。
3.1 真实噪声建模:Gaussian-Poisson模型
先说理论。传感器噪声到底长什么样?
我刚开始做ISP时,以为噪声就是简单的加性高斯白噪声。后来发现,根本不是这么回事。传感器噪声有两个主要来源:
- 光子散粒噪声(Shot Noise):光子到达传感器是随机的,服从泊松分布。信号越强,噪声方差越大。
- 读出噪声(Read Noise):电路读取电荷时引入的噪声,近似高斯分布,与信号强度无关。
所以,一个更准确的模型是:
y = Poisson(x) + Gaussian(0, σ²)
其中 x 是理想信号,y 是观测到的RAW值。这个模型叫 Gaussian-Poisson噪声模型,也叫 Poisson-Gaussian噪声模型。
核心公式:
噪声方差与信号强度的关系:σ²_total = a · x + b
其中 a 是泊松噪声增益(与ISO相关),b 是读出噪声方差。
说白了,就是噪声的方差会随着信号强度线性增加。暗区噪声小?不对,暗区信噪比低,看起来反而更明显。
3.2 RAW域噪声标定:从传感器拿到真实参数
理论模型有了,但参数怎么定?你得标定。
我在项目中遇到过最坑的事,就是拿了一颗新传感器,直接套用上一颗的噪声参数。结果模型在暗光下完全崩了。后来老老实实做标定,才发现两颗传感器的读出噪声差了将近一倍。
标定流程其实不复杂,但细节很多:
- 准备均匀光源:用积分球或均匀白板,保证传感器整个画幅照度一致。
- 拍摄多组RAW图:在不同ISO下,从暗到亮拍10-20个亮度等级。每个亮度拍50-100帧。
- 计算均值和方差:对每个像素位置,计算多帧的均值和方差。然后取空间平均。
- 拟合参数:用最小二乘法拟合 σ² = a · μ + b。
我的经验:标定时一定要关掉所有ISP处理(黑电平校正、LSC、去噪等),直接拿原始RAW数据。否则标出来的参数是"脏"的。
标定结果长什么样?给你看个例子:
| ISO | a(泊松增益) | b(读出噪声) |
|---|---|---|
| 100 | 0.0012 | 0.85 |
| 200 | 0.0025 | 0.92 |
| 400 | 0.0048 | 1.10 |
| 800 | 0.0095 | 1.45 |
| 1600 | 0.0190 | 2.10 |
你看,ISO越高,a和b都在涨。但b的涨幅没那么线性,因为读出噪声还受温度影响。
注意:温度对读出噪声影响很大。标定时要控制传感器温度,最好在常温(25°C)下进行。我曾经在夏天和冬天分别标定过同一颗传感器,b值差了30%。
3.3 合成数据对的生成流程
标定完参数,接下来就是生成训练数据了。我们的目标是:干净RAW → 加噪RAW,形成一对训练样本。
流程如下:
- 获取干净RAW:用低ISO(如ISO 100)拍摄多帧,然后平均。或者用高信噪比的合成图像。
- 生成噪声:根据标定参数,对每个像素生成泊松噪声和高斯噪声。
- 合成带噪RAW:干净RAW + 噪声 = 带噪RAW。
- 数据增强:随机裁剪、翻转、旋转、调整ISO参数等。
代码实现其实很直接:
def add_noise(clean_raw, a, b, gain=1.0):
"""
给干净RAW添加Gaussian-Poisson噪声
clean_raw: 归一化到[0,1]的RAW数据
a: 泊松噪声增益
b: 读出噪声方差
gain: ISO增益倍数
"""
# 1. 泊松噪声(光子散粒噪声)
shot_noise = np.random.poisson(clean_raw * gain) / gain - clean_raw
# 2. 高斯噪声(读出噪声)
read_noise = np.random.normal(0, np.sqrt(b), clean_raw.shape)
# 3. 合成
noisy_raw = clean_raw + a * shot_noise + read_noise
return np.clip(noisy_raw, 0, 1)
关键点:
- 泊松噪声的方差与信号强度成正比,所以要用
np.random.poisson而不是简单的高斯。 - 增益参数
gain对应ISO设置,ISO越高,泊松噪声越明显。 - 最后一定要
clip到[0,1],否则训练时会出NaN。
3.4 数据合成的进阶技巧
上面是最基础的版本。实际部署时,你会发现还有几个坑:
- Bayer模式:RAW数据是Bayer格式的,噪声在不同颜色通道上可能有差异。我建议每个通道单独标定参数。
- 行噪声(Row Noise):有些传感器有行噪声,表现为整行像素的偏移。这个需要额外建模。
- 固定模式噪声(FPN):暗场下拍多帧平均,可以提取FPN。合成时加上去。
我曾经在一个项目里,模型在实验室数据上PSNR很高,但一上手机就翻车。排查了三天,发现是行噪声没建模。加上行噪声后,模型立马收敛了。
避坑指南:合成数据时,噪声参数不要用固定值。每次训练时随机采样ISO和温度参数,让模型见过各种噪声强度。这样泛化能力会好很多。
3.5 知识结构总览
下面这张图,把整个数据准备流程串起来了:
3.6 总结一下
训练数据准备,说白了就三步:
- 建模:用Gaussian-Poisson模型描述传感器噪声。
- 标定:通过拍摄均匀RAW图,拟合出模型参数a和b。
- 合成:用标定参数生成大量干净-带噪数据对。
嗯,这里要注意:标定越细致,模型在真实场景下的表现就越好。别偷懒,多花两天时间把标定做扎实,后面训练和部署会省很多事。
我的建议:第一次做这个流程时,先拿一颗传感器完整跑一遍。从标定到合成到训练,全链路打通。后面换传感器,就只是换参数的事了。