第三章 RAW域预处理:从传感器原始数据到可用图像
各位工程师朋友,欢迎来到第三章。上一章我们聊了HDR合成的基本框架,今天要深入的是RAW域预处理——说白了,就是传感器吐出来的原始数据,怎么变成能用的干净信号。
我个人习惯把RAW域预处理比作「地基工程」。你想想看,后面所有的HDR合成、去噪、锐化,都建立在这几步之上。地基没打好,后面再牛的算法也救不回来。
核心观点:RAW域预处理的质量,直接决定HDR合成的天花板。我在瑞芯微平台上调试过不下20个传感器型号,可以负责任地说:预处理做扎实了,HDR效果至少提升30%。
3.1 黑电平校正(Black Level Correction)
先讲黑电平。什么是黑电平?传感器在全黑环境下,理论上输出应该是0,但实际上由于暗电流、电路噪声等因素,会有一个固定的偏移量。这个偏移量就是黑电平。
我记得第一次调RK3588的HDR时,发现暗部偏绿。查了半天,原来是黑电平没对准——不同通道的黑电平不一样,导致暗部色偏。
3.1.1 黑电平的来源
- 暗电流:传感器本身的热噪声,温度越高越明显
- 电路偏移:ADC转换、模拟前端电路的固定偏置
- OB像素:光学黑区(Optical Black)像素的平均值
3.1.2 校正方法
瑞芯微平台通常提供两种方式:
- 全局减固定值:最简单,但精度一般
- 逐行/逐列校正:针对不同行、列的黑电平差异做补偿
// 瑞芯微RKISP驱动中的黑电平校正示例
// 全局减固定值模式
uint16_t black_level_r = 64; // R通道黑电平
uint16_t black_level_gr = 64; // Gr通道
uint16_t black_level_gb = 64; // Gb通道
uint16_t black_level_b = 64; // B通道
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// 根据Bayer模式判断当前像素通道
switch (get_bayer_channel(x, y)) {
case CH_R: pixel -= black_level_r; break;
case CH_GR: pixel -= black_level_gr; break;
case CH_GB: pixel -= black_level_gb; break;
case CH_B: pixel -= black_level_b; break;
}
// 钳位到有效范围
pixel = CLAMP(pixel, 0, 4095);
}
}
实战技巧:我建议在调试阶段,先抓一帧全黑图像,统计每个通道的均值。如果发现某个通道的均值明显偏离预期,大概率是黑电平没对准。瑞芯微的ISP调试工具里有个「Black Level Analysis」功能,很好用。
3.2 镜头阴影校正(Lens Shading Correction, LSC)
镜头阴影,也叫亮度衰减。说白了,就是镜头中心亮、边缘暗。这是光学系统的物理特性,任何镜头都有,只是程度不同。
你想想看,如果HDR合成时不做LSC,边缘区域的亮度信息会被低估,合成出来的图像就会出现「中心正常、边缘偏暗」的问题。我在一个宽动态场景的项目中遇到过,当时客户反馈边缘细节丢失,查到最后就是LSC参数没调好。
3.2.1 阴影类型
| 类型 | 原因 | 表现 |
|---|---|---|
| 亮度阴影 | 镜头边缘进光量减少 | 边缘亮度下降 |
| 颜色阴影 | 不同波长光线折射角度不同 | 边缘出现色偏 |
3.2.2 校正流程
- 标定:拍摄均匀光照下的灰卡,记录每个像素的增益系数
- 存储:将增益系数表存入ISP的LUT(查找表)
- 实时校正:每个像素乘以对应的增益系数
// 瑞芯微LSC增益表结构(简化版)
typedef struct {
uint16_t grid_x; // 网格X方向点数
uint16_t grid_y; // 网格Y方向点数
uint16_t gain_r[17][13]; // R通道增益表(17x13网格)
uint16_t gain_gr[17][13]; // Gr通道
uint16_t gain_gb[17][13]; // Gb通道
uint16_t gain_b[17][13]; // B通道
} rk_lsc_gain_table_t;
// 双线性插值计算当前像素的增益
float get_gain(float x, float y, uint16_t gain_table[17][13]) {
int xi = (int)(x * 16 / width);
int yi = (int)(y * 12 / height);
float fx = x * 16 / width - xi;
float fy = y * 12 / height - yi;
// 双线性插值
return (1-fx)*(1-fy)*gain_table[yi][xi] +
fx*(1-fy)*gain_table[yi][xi+1] +
(1-fx)*fy*gain_table[yi+1][xi] +
fx*fy*gain_table[yi+1][xi+1];
}
避坑指南:我曾经在RK3588上调试一个广角镜头,发现边缘增益过大导致噪声被放大。后来发现是标定时灰卡没放平,导致增益系数偏大。建议标定时用专业积分球,或者至少保证灰卡完全垂直于光轴。
3.3 坏点校正(Defect Pixel Correction, DPC)
坏点,就是传感器上那些「不听话」的像素。有的永远亮着(亮点),有的永远黑着(暗点),有的随机闪烁(闪烁点)。
HDR合成对坏点特别敏感。为什么?因为HDR需要融合多帧图像,如果某个像素在长曝光帧是亮点,在短曝光帧是正常值,合成后就会出现一个「忽明忽暗」的鬼影。
3.3.1 坏点类型
- 静态坏点:固定位置,永远异常
- 动态坏点:随温度、增益变化出现
- 闪烁坏点:随机出现,难以预测
3.3.2 校正策略
| 策略 | 适用场景 | 优缺点 |
|---|---|---|
| 静态坏点表 | 出厂标定 | 准确,但无法处理动态坏点 |
| 动态检测+中值滤波 | 实时处理 | 灵活,但可能误判 |
| 自适应阈值 | 高动态范围场景 | 效果好,计算量大 |
// 动态坏点检测与校正(简化版)
// 基于邻域像素的梯度检测
int detect_bad_pixel(uint16_t center, uint16_t neighbors[8]) {
// 计算邻域中值
uint16_t median = get_median(neighbors, 8);
// 计算差
int diff = abs(center - median);
// 自适应阈值:基于邻域标准差
int threshold = get_adaptive_threshold(neighbors, 8);
return (diff > threshold) ? 1 : 0;
}
// 校正:用邻域中值替换
uint16_t correct_bad_pixel(uint16_t neighbors[8]) {
return get_median(neighbors, 8);
}
个人经验:瑞芯微的ISP硬件自带DPC模块,但默认参数偏保守。我建议在HDR模式下,把检测阈值调低20%左右。因为HDR的噪声水平更高,坏点更容易被噪声掩盖。调低阈值可以更早发现坏点,避免合成后出现「坏点鬼影」。
3.4 RAW域去噪(RAW Domain Denoising)
最后一步,也是争议最大的一步。为什么要在RAW域去噪?因为噪声在RAW域是加性高斯分布,到了RGB域就变成了复杂的颜色相关噪声,更难处理。
我记得有个项目,客户坚持在RGB域做去噪,结果HDR合成后暗部出现大量彩色噪点。后来改到RAW域去噪,同样的算法,效果天差地别。
3.4.1 噪声模型
RAW域的噪声主要由两部分组成:
- 散粒噪声:与信号强度平方根成正比,高亮区域明显
- 读出噪声:与信号强度无关,暗部区域明显
3.4.2 常用去噪方法
| 方法 | 原理 | 瑞芯微平台支持 |
|---|---|---|
| 双边滤波 | 空间+强度权重 | 硬件加速 |
| 非局部均值 | 搜索相似块 | 软件实现 |
| BM3D | 块匹配+3D变换 | 软件实现(性能要求高) |
| 小波去噪 | 频域阈值 | 部分硬件支持 |
// 瑞芯微平台RAW域双边滤波示例
// 利用ISP硬件加速
void raw_bilateral_filter(uint16_t *raw, int width, int height,
float sigma_s, float sigma_r) {
// 配置ISP硬件参数
rkisp_bilateral_config_t config;
config.sigma_spatial = sigma_s; // 空间域标准差
config.sigma_range = sigma_r; // 强度域标准差
config.window_size = 5; // 5x5窗口
config.enable_hw_accel = 1; // 启用硬件加速
// 调用硬件处理
rkisp_process_raw(raw, width, height, &config);
}
重要提醒:RAW域去噪是一把双刃剑。去噪太强,会损失细节,HDR合成后边缘模糊;去噪太弱,噪声会传递到后续处理。我建议在HDR场景下,对长曝光帧做较强去噪,对短曝光帧做较弱去噪。这样既能压制暗部噪声,又能保留高亮细节。
3.5 本章知识体系
下面这张图总结了RAW域预处理的完整流程和核心逻辑。你可以把它当作调试时的「检查清单」。
这张图把四个步骤串起来了。你调试的时候,可以按这个顺序逐个检查。我个人习惯是先确认黑电平,再做LSC,然后处理坏点,最后去噪。顺序乱了,效果会打折扣。