新相机的 RAW
索尼最新发布的 a7M5,如果你第一时间拍摄 RAW 图像,并尝试用 Lightroom 打开编辑,会发现 Lightroom 尚未支持,为了在 Adobe 正式更新之前在软件中打开 RAW,可以通过 LibRAW 和 DNG 曲线救国。只要新相机的 RAW 文件没有发生特别大的变化(比如新的 HQ 压缩),该方法是相对通用的。

RAW 的必要组成部分
相机的 RAW 数据主要分两部分,RAW 数据和附加数据。
RAW 数据
第一是 RAW 数据本身,即每个像素输出的一个数字,对于绝大多数全画幅相机来说,它应当是一个二维数组,每个位置上是一个 14 位的码值。在 Python 里,可以放进一个形状为 (h, w) 的 numpy array,类型为 uint16。
这样的数组可能很大,比如一个 33MP 的图片,需要占用 63 MB 空间。因此,需对其进行压缩。最常见的压缩是位打包,每个码值的范围是 0–16383,只需 14 个比特,却占用了一个 16 位无符号整数(2 个字节)。可以将 4 个 14 位打包进 7 个字节。另外的压缩方式还有 DNG 采用的 JPEG 或 JXL 的无损模式,以及佳能、索尼、尼康各自开发的压缩算法。对 RAW 进行有损压缩是相对少见的,比如索尼被诟病已久的 Compressed 选项,和在 a7M5 上最新推出的 Compressed HQ 选项。这些都是不同的压缩算法,如何从压缩后的数据解码出原始数组是“解 RAW”的最主要工作。
好在以 LibRAW 为代表的开源库可以高效的完成这一步,即便 a7M5 上把 ARW 版本号刷到了 6.0.0,增加了 HQ 压缩选项,但 Lossless 模式还是和以往一样的,LibRAW 依旧可以正确解码。
于是,我们便轻松得到了所需的最关键数据:RAW Data 数组。此处我们使用 rawpy,一个 LibRAW 的 Python 包装。
with rawpy.imread(image_path) as raw:
raw_image = raw.raw_image.copy()
附加数据
光有 RAW Data 是看不到正确颜色的图片的,只代表了传感器某种程度上的原始输出,并无色彩信息。要转换到常见的各种色彩空间,还需要至少一个矩阵,描述了如何从传感器的 RAW RGB 空间转换到 XYZ 或 sRGB 这些标准空间。
对于由相机产生的 RAW 文件,可以在 EXIF 里找到相关的矩阵,比如索尼就是放在 0x7800 ID 的 EXIF MakerNote 中。这个矩阵的源空间,目标空间,转换方向各有不同,不过好在这种数据在同一品牌内是具有连贯性的,a7M5 的矩阵和近几代的机身大概是以一样的格式放在一样的位置,LibRAW 可以识别并将其转换到标准的样式。
相似的,白平衡信息、黑电平和白电平也都能在 EXIF 中找到并由 LibRAW 取出。
with rawpy.imread(image_path) as raw:
raw_matrix = raw.rgb_xyz_matrix[:3, :3]
raw_wb = raw.camera_whitebalance
black_levels = raw.black_level_per_channel
white_level = raw.white_level
重新组装成 RAW 文件
ACR 等第三方软件读取原始 RAW 文件后,发现这是由它未知的相机拍摄的,数据库中没有该相机的附加数据(比如 D65 和 A 光下的两个色彩转换矩阵),因此拒绝解码。
虽然我们也没有这两个矩阵,但对于当前拍摄光源,我们有一组相机自己产出的转换矩阵,由于 ACR 是采用插值策略,只需在 D65 和 A 光的色彩转换矩阵里都填这一个,无论怎么插值之后,必定也是这个矩阵,就实现了当前光源下的颜色还原。
现在,我们需要将刚才提取的 RAW Data 数组和这些附加信息重新组装成一个 RAW 文件供第三方软件识别和读取,这里选择 DNG 格式,这是一种通用 RAW 格式。
DNG 格式至少需要以下内容:
- RAW Data 数组:刚才已经提取出来
- 黑、白电平:直接使用提取的值
- Color Matrix:等同于 Rawpy 中的
rgb_xyz_matrix - As Shot Neutral:这个是用来白平衡的,物理含义是中性色在 RAW 中的码值,等于白平衡增益的倒数,即
1/camera_whitebalance。
此处的 Color Matrix 和 As Shot Neutral 都是以两个整数按照分子,分母的顺序保存浮点数的,需要进行一些转换,比如变成 x/10000 的样式。
t = DNGTags()
t.set(Tag.BlackLevel, black_level)
t.set(Tag.WhiteLevel, white_level)
t.set(Tag.ColorMatrix1, cm1)
t.set(Tag.ColorMatrix2, cm2)
t.set(Tag.AsShotNeutral, as_shot_neutral)
有了这些必要内容之后,就可以组装成 DNG 格式,此处选用的库是 PiDNG,虽然已经长期没有维护,但还是勉强可用的。DNG 可选 JPEG 压缩,PiDNG 使用了一个 C 语言的库来完成,如果用无压缩选项的话,PiDNG 就是纯 Python 的。
r = RAW2DNG()
r.options(t, path="", compress=False)
r.convert(image, filename=output_dng_path)
至此,我们成功将一个由 a7M5 拍摄的 RAW 图像,转换成了通用的 DNG 格式,可以导入 Lightroom 正确的编辑(除了调整白平衡外),虽然非常简陋,但在 Adobe 正式更新之前,应应急还是没问题的。
