第三章 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 校正方法

瑞芯微平台通常提供两种方式:

  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 校正流程

  1. 标定:拍摄均匀光照下的灰卡,记录每个像素的增益系数
  2. 存储:将增益系数表存入ISP的LUT(查找表)
  3. 实时校正:每个像素乘以对应的增益系数
// 瑞芯微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域预处理的完整流程和核心逻辑。你可以把它当作调试时的「检查清单」。

RAW域预处理流程框架图 RAW域预处理流程框架 传感器RAW数据 步骤1:黑电平校正 去除暗电流偏移,恢复真实黑电平 步骤2:镜头阴影校正 补偿镜头边缘亮度衰减和色偏 步骤3:坏点校正 检测并修复静态/动态坏点 步骤4:RAW域去噪 双边滤波/非局部均值/BM3D 干净的RAW数据 → HDR合成 调试要点 • 黑电平:逐通道统计 • LSC:用积分球标定 • DPC:HDR模式调低阈值 • 去噪:长/短曝光帧差异化 注:以上步骤在瑞芯微ISP流水线中按顺序执行,部分步骤可硬件加速

这张图把四个步骤串起来了。你调试的时候,可以按这个顺序逐个检查。我个人习惯是先确认黑电平,再做LSC,然后处理坏点,最后去噪。顺序乱了,效果会打折扣。