3、RAW数据读取与解析:常见RAW格式(DNG、NEF、ARW)解析、Bayer阵列模式、黑电平与白平衡校正

做RAW域深度学习去噪,第一步就是跟RAW文件打交道。说实话,这步要是没搞对,后面模型再牛也是白搭。我见过不少同学,直接把RAW当普通图像读进来,结果训练出来的模型一塌糊涂。

今天我们就来聊聊,怎么把RAW文件里的数据,变成我们真正能用的数字信号。

3.1 常见RAW格式:DNG、NEF、ARW

市面上相机品牌那么多,RAW格式也五花八门。尼康用NEF,索尼用ARW,佳能用CR2/CR3。但好在,有个通用格式叫DNG(Digital Negative),Adobe搞的,很多相机也支持。

我个人习惯,不管什么格式,第一步都转成DNG再处理。为什么?因为DNG是开源的,解析起来最省心。NEF和ARW都是各家闭源的,虽然libraw能解,但偶尔会踩坑。

举个例子,我几年前处理索尼A7R3的ARW文件,libraw解出来暗部总有奇怪的条纹。后来发现是版本兼容问题,换成DNG就没事了。所以,如果你不想在数据读取上浪费时间,建议先转DNG。

3.1.1 用libraw读取RAW文件

libraw是C++库,Python里可以用rawpy封装。下面这段代码,是我项目里常用的读取方式:

import rawpy
import numpy as np

def read_raw(file_path):
    # 用rawpy打开RAW文件
    with rawpy.imread(file_path) as raw:
        # 获取原始Bayer数据,不经过任何处理
        raw_data = raw.raw_image.copy()
        # 获取黑电平和白平衡参数
        black_level = raw.black_level_per_channel
        white_level = raw.white_level
        camera_wb = raw.camera_whitebalance
        # 获取Bayer模式
        bayer_pattern = raw.raw_pattern
    return raw_data, black_level, white_level, camera_wb, bayer_pattern

注意,这里用的是raw_image,不是postprocess()。后者会帮你做去马赛克、白平衡、gamma校正,但那不是我们想要的。我们要的是最原始的传感器数据。

警告:千万别用rawpy.postprocess()来获取训练数据!它会自动做很多处理,包括插值、白平衡、色彩校正,这些都会破坏原始噪声分布。我见过有人拿处理后的图像训练去噪模型,结果模型学到的全是插值伪影。

3.2 Bayer阵列模式

大部分相机传感器用的是Bayer阵列。说白了,就是一个像素只感光一种颜色,红、绿、蓝按一定模式排列。最常见的模式是RGGB,也就是左上角是R,右边是G,下面也是G,右下是B。

为什么会这样?因为人眼对绿色最敏感,所以绿色像素占了一半,红色和蓝色各占四分之一。

但不同相机,Bayer模式可能不一样。有的用BGGR,有的用GRBG。读取的时候一定要搞清楚,不然颜色就乱了。

3.2.1 如何获取Bayer模式

从rawpy里可以直接拿到:

# 假设raw是rawpy对象
pattern = raw.raw_pattern  # 返回一个2x2的数组
print(pattern)
# 比如输出 [[0, 1], [1, 2]],表示:
# 0=R, 1=G, 2=B
# 所以模式是 RGGB

拿到模式后,我们可以把Bayer数据拆成四个通道:

def split_bayer(raw_data, pattern):
    h, w = raw_data.shape
    # 根据pattern确定偏移
    r_offset = np.where(pattern == 0)
    g1_offset = np.where(pattern == 1)
    g2_offset = np.where(pattern == 1)  # 两个G位置不同
    b_offset = np.where(pattern == 2)
    
    # 提取各通道
    r_ch = raw_data[r_offset[0]::2, r_offset[1]::2]
    g1_ch = raw_data[g1_offset[0]::2, g1_offset[1]::2]
    g2_ch = raw_data[g2_offset[0]::2, g2_offset[1]::2]
    b_ch = raw_data[b_offset[0]::2, b_offset[1]::2]
    
    return r_ch, g1_ch, g2_ch, b_ch
小技巧:有些传感器用四合一(Quad Bayer)模式,比如手机上的高像素模式。那种情况Bayer模式是4x4的,处理起来要更小心。我处理过三星HMX传感器的数据,那个模式是4x4的RGGB,每个2x2块里四个像素同色。

3.3 黑电平与白平衡校正

传感器读出来的原始值,不是直接就能用的。有两个东西必须处理:黑电平(Black Level)和白平衡(White Balance)。

3.3.1 黑电平校正

黑电平是什么?简单说,就是传感器在完全无光的情况下,依然会输出一个非零值。这个值通常是512或1024(12bit或14bit)。

为什么要减去黑电平?因为噪声是叠加在信号上的,如果不减掉这个偏置,噪声的均值就不为零,会影响后续处理。

我曾经犯过一个错误:直接拿原始值做归一化,没减黑电平。结果模型在暗部区域表现极差,因为暗部的信噪比被低估了。

正确的做法:

def apply_black_level(raw_data, black_level):
    # black_level可能是一个值,也可能是每个通道不同
    if isinstance(black_level, (list, np.ndarray)):
        # 每个通道单独减
        # 这里简化处理,实际要根据Bayer模式对应
        raw_data = raw_data.astype(np.float32)
        raw_data -= black_level[0]  # 假设所有通道相同
    else:
        raw_data = raw_data.astype(np.float32) - black_level
    # 确保不为负
    raw_data = np.maximum(raw_data, 0)
    return raw_data

注意,黑电平可能每个通道不一样。有些传感器R通道的黑电平比G通道高一点,这个要查datasheet。

3.3.2 白平衡校正

白平衡校正,说白了就是让白色物体在图像里看起来是白的。不同光源下,传感器对RGB的响应不一样。比如白炽灯下,红色会偏多。

相机厂商会在RAW文件里存一组白平衡增益,通常是R和B通道的增益,G通道作为参考(增益为1)。

应用白平衡:

def apply_white_balance(r_ch, g_ch, b_ch, wb_gains):
    # wb_gains通常是 [R_gain, G_gain, B_gain]
    # 但G_gain一般是1.0
    r_gain, g_gain, b_gain = wb_gains
    r_ch = r_ch * r_gain
    g_ch = g_ch * g_gain
    b_ch = b_ch * b_gain
    return r_ch, g_ch, b_ch
重点:在深度学习去噪任务中,白平衡校正要不要做?我的建议是:要做。因为如果不做白平衡,R和B通道的强度差异太大,模型很难学到统一的噪声分布。但注意,白平衡增益要作为元数据保存下来,因为最终输出图像时还要用。

3.4 完整的数据读取流程

把上面这些串起来,一个完整的RAW读取流程是这样的:

  1. 用rawpy读取RAW文件,获取原始Bayer数据
  2. 获取Bayer模式、黑电平、白平衡参数
  3. 减去黑电平
  4. 应用白平衡增益
  5. 将数据归一化到[0,1]范围(除以white_level - black_level)
  6. 保存元数据(Bayer模式、白平衡增益等)

下面这张图,是我自己总结的RAW数据解析流程,你可以参考一下:

RAW数据解析与预处理流程 读取RAW文件 提取原始Bayer数据 获取Bayer模式/黑电平/白平衡 黑电平校正 + 白平衡校正 归一化 + 保存元数据 注意点 • 使用raw_image获取原始数据 • 不要用postprocess() • 黑电平可能各通道不同 输出数据 • 归一化后的Bayer数据 • Bayer模式(2x2或4x4) • 白平衡增益(R/G/B)

3.5 实战中的坑与经验

最后,分享几个我实际项目中踩过的坑:

  • 黑电平不是固定值:有些相机的黑电平会随ISO变化。我处理过一组数据,ISO 100时黑电平是512,ISO 6400时变成了528。如果不注意,高ISO下的暗部噪声会被错误放大。
  • 白平衡增益不要超过阈值:有些极端光源下,R或B通道的增益可能很大(比如3.0以上)。这会导致该通道被过度放大,噪声也跟着放大。我一般会限制增益不超过2.5,超出部分用相邻像素插值。
  • Bayer模式要验证:别完全相信相机厂商的标注。我遇到过一台相机,官方说模式是RGGB,实际读出来是BGGR。怎么验证?拍一张纯色物体,看哪个通道响应最高就知道了。
我的习惯:每次拿到新相机的RAW数据,我都会先写一个简单的验证脚本:读取数据、减黑电平、应用白平衡、显示四个通道的直方图。如果直方图分布合理,说明解析正确。如果某个通道明显异常,那就要检查Bayer模式或黑电平了。

嗯,RAW数据读取这部分,说难不难,说简单也不简单。关键是要理解传感器的工作原理,知道每一步在做什么。搞清楚了这些,后面的去噪模型才能建立在可靠的数据基础上。