1. RAW 域基础:从传感器到数字图像的第一站
做 HDR 去噪这几年,我越来越觉得:不懂 RAW 域,就别谈什么细节保留。很多同学拿着 RGB 图像做处理,折腾半天,噪点去掉了,细节也糊了。为什么?因为你在错误的地方做了错误的事。
这一章,我们先把地基打牢。我会带你看看 RAW 图像到底长什么样,Bayer 阵列是怎么回事,以及为什么我坚持在 RAW 域做 HDR 处理。
1.1 RAW 图像格式解析
相机传感器拍下来的原始数据,就是 RAW。它不像 JPEG 那样经过白平衡、伽马校正、色彩空间转换——说白了,它就是传感器每个像素的原始电荷读数。
我习惯把 RAW 比作「数字底片」。你想想看,底片能记录的信息量,远大于最终冲印出来的照片。RAW 也一样:
- 位深高:通常是 12bit 或 14bit,高端相机能到 16bit。而 JPEG 只有 8bit。
- 线性响应:RAW 数据与场景亮度呈线性关系。这很重要,后面讲 HDR 合并时会反复用到。
- 无压缩或无损压缩:信息零丢失。
关键指标: 12bit RAW 能记录 4096 个灰度级,14bit 是 16384 级。相比之下,8bit JPEG 只有 256 级。你在暗部想保留细节?RAW 域有 16 倍以上的优势。
我曾经接手过一个项目,客户坚持用 JPEG 做多帧 HDR 合成。结果暗部全是色块,亮部高光 clipping 得一塌糊涂。后来我让他们改采 RAW 流,同样的算法,效果天差地别。嗯,从那以后我再也不接 JPEG HDR 的活了。
1.2 Bayer 阵列与 CFA 模式
大多数相机传感器只有一个感光层。它本身分不清颜色。那彩色照片怎么来的?靠的是彩色滤光片阵列(CFA)。
最常见的 CFA 就是 Bayer 阵列。它长这样:
R G R G R G
G B G B G B
R G R G R G
G B G B G B
每个像素只采集一种颜色。绿色占一半,红蓝各占四分之一。为什么?因为人眼对绿色最敏感。这个设计很聪明,对吧?
我刚开始做 ISP 时,总觉得 Bayer 阵列浪费了 2/3 的光线。后来才明白,这是成本和画质之间的最优解。你想想看,如果每个像素都采集全色,那传感器得堆三层,工艺复杂不说,还会引入串扰。
| CFA 类型 | 排列方式 | 常见应用 | 特点 |
|---|---|---|---|
| Bayer RGGB | 2×2 单元 | 绝大多数相机 | 绿色敏感,成本低 |
| X-Trans | 6×6 单元 | 富士相机 | 减少摩尔纹 |
| Quad Bayer | 4×4 单元 | 高像素手机 | 支持 binning |
我的经验: 做 HDR 去噪时,Bayer 阵列的绿色通道信噪比天然比红蓝高。所以去噪强度可以按通道分开调。我一般对 R 和 B 通道多降一点,G 通道少降一点,这样能保住更多细节。
1.3 RAW 与 RGB 的差异
很多人觉得 RAW 转 RGB 不就是个 demosaic 嘛。其实远没那么简单。我列个表,你一看就明白:
| 维度 | RAW | RGB(sRGB) |
|---|---|---|
| 位深 | 12-16bit | 8bit |
| 色彩空间 | 传感器原生 | sRGB / Adobe RGB |
| 响应曲线 | 线性 | 伽马校正(非线性) |
| 数据密度 | 每个像素 1 通道 | 每个像素 3 通道 |
| 噪声分布 | 与信号相关(泊松) | 被非线性扭曲 |
| 处理灵活性 | 高 | 低 |
这里有个坑,我踩过。RGB 图像经过伽马校正后,暗部噪声被放大,亮部细节被压缩。如果你在 RGB 域做去噪,很容易把暗部细节当噪声抹掉,或者把亮部纹理保留得过多。说白了,噪声模型在 RGB 域已经变了,你拿线性域的算法去处理非线性数据,结果能好吗?
1.4 RAW 域处理的优势
为什么我坚持在 RAW 域做 HDR 去噪?三个理由:
- 噪声模型简单:RAW 域的噪声主要是光子散粒噪声和读出噪声,两者都与信号强度有明确的数学关系。我可以精确建模,然后针对性去噪。
- 动态范围完整:RAW 保留了传感器能记录的全部动态范围。一旦转成 RGB,高光和暗部都被截断了。HDR 合并需要的就是这个完整的动态范围。
- 处理顺序灵活:在 RAW 域,我可以先做去噪,再做 demosaic,最后做色调映射。去噪在 demosaic 之前做,能避免彩色伪影被放大。
注意: RAW 域处理虽然好,但计算量大。12bit 或 14bit 的数据,处理起来比 8bit RGB 慢不少。如果你的项目对实时性要求极高(比如手机预览),可能需要权衡。我一般建议:静态场景用 RAW 域,视频场景可以考虑 YUV 域。
1.5 本章知识结构图
下面这张图,是我自己梳理的 RAW 域基础框架。你可以把它当作本章的「地图」:
这张图把本章的核心知识点串起来了。你从中心出发,往四个方向走,每个分支都有对应的子知识点。我个人习惯在开始一个新项目前,先画这么一张图,把「我到底要处理什么数据」搞清楚。不然做到一半才发现数据格式不对,那才叫崩溃。
一个小建议: 如果你刚开始接触 RAW 域,可以先拿一张 .dng 文件,用 Python 的 rawpy 库读出来,看看里面的数据长什么样。我当年就是这么入门的。代码很简单:
import rawpy
import numpy as np
raw = rawpy.imread('example.dng')
bayer = raw.raw_image # 直接拿到 Bayer 数据
print(f'Shape: {bayer.shape}, dtype: {bayer.dtype}')
print(f'Min: {bayer.min()}, Max: {bayer.max()}')
看到那 12bit 或 14bit 的数据范围了吗?这就是你接下来所有处理的基础。