利用 MATLAB 和 Dcraw 处理数码相机 Raw 文件的完整流程

这篇文章要说的当然不是如何用 PS、LightRoom 来处理 Raw 文件,而是一种更加彻底、数据化的办法 -- 利用 MATLAB 直接处理数码相机的 CFA(Color Filter Array) 数据。

接触过摄影的人都知道,对于数码相机来说 Raw Data 是原始的、未被处理的数据,相比 JPEG,Raw 文件记录了更多的场景信息,保留了更宽广的色域以及动态范围,也留下了更为自由的后期处理空间。Raw Data 只是一种图像数据的封装形式而并不是一种文件格式,不同厂商的相机一般都拥有自己的 Raw Data 格式,比如常见的 *.CR2、*.NEF、*.ARW、*.ORF(Olympus)、*.RAF(Fuji)、*.DNG(Adobe、Leica)等。

对于大部分人来说,处理 Raw Data 的软件不外乎就那么几种,Camera Raw、LightRoom、Aperture、DxO 以及各厂商自带的处理软件。这些软件虽然通过图形界面提供了简洁易懂的处理流程,但是遗憾的是它们能导出的都并非真正意义上的 “Raw Data”,即传感器直接记录的、与照度成线性关系的第一手数据。对于摄影、媒体、艺术领域,这些软件已经足够强大,但是在图像处理、计算机视觉等研究领域,我们需要的是把照片中的信息转换为能够通过数字来定量表示的形式,或者通过一些公式、算法直接对图像(或图像的某一部分)进行处理,再或者对两张图片之间的差别进行量化表示,这时候 LightRoom 这些软件就显得有些无能为力了。

这篇文章要讲的就是如何通过 MATLAB 对 Raw Data 进行处理并从中提取出我们想要的图像信息,这是一种我所了解的最彻底、最根本的获取相机传感器原始信息的方法,并且这些信息都是以数字的形式记录下来,可以很方便地在此基础上进行图像的存储、传输或者进一步操作。实际上 MATLAB 本身就提供了非常强大的图像处理功能,正所谓 “普通青年处理图片用 PS,文艺青年处理图片用 LightRoom,2B 青年处理图片用 MATLAB ”。

由于各家相机厂商对 Raw Data 采取了不同的封装方式,MATLAB 并不能一一识别这些文件格式,因此我们需要先利用 Dcraw 将不同扩展名的文件转换为 MATLAB 能够读取的图片格式 -- tiff。Dcraw 是一种 Raw Data 解析方案,它能够识别市面上大部分的相机型号,并将相应的 Raw 文件导出为 tiff 或 PGM/PPM/PAM 格式文件。事实上 Dcraw 本身就可以算作一种 Raw Data 的处理程序,它拥有白平衡设置、伽马校正、色彩空间转换、去马赛克、亮度调节等一系列功能,并且提供了 C 源代码,让用户可以在任何操作系统上对其进行编译。关于 Dcraw 更详细的设置可以在其官方文档或这个博客中查看,这里我们仅仅把它作为 Raw Data 到 tiff 的一种媒介,而不对图像做任何的处理 -- 把所有的操作都留到 MATLAB 里。

目录

0. Dcraw 的预处理

以下操作以 Windows 平台为例,使用的相机为 Nikon D3X。为了简化流程,我们使用 Dcraw 现成的可执行文件(http://www.centrostudiprogressofotografico.it/en/dcraw/)而不在本地对其编译。一般将这个 Dcraw.exe 文件放在 C:\windows 路径下,这样可以直接从运行(Win + R)中执行。在运行中输入 cmd 进入命令行窗口,这时已经可以直接调用 Dcraw,或者输入 dcraw 查看相关的一些命令。

由于我们不准备用 Dcraw 对 Raw Data 做任何处理,只需要输入

dcraw -4 -T -D -v pathfilename

其中 pathfilename 为图像的绝对路径,比如 E:\photosimg1.NEF 这样。这里的 -T 表示将图像以 tiff 格式导出,-D 表示不对图像做任何的彩色插值和亮度调节(彩色插值的问题后面会提到),-v 表示在 Dcraw 处理结束后在屏幕上显示相关信息(这一步有没有都无所谓),而 -4 等价于 -6 -W -g 1 1,即表示导出的图像为16位(而不是常见的 JPEG 的8位 )、不进行任何白平衡校正不进行任何伽马校正。在一些需要获取拍照时白平衡设置的场合也可以使用 -6 -w -g 1 1 -T -D 这样的参数组合,但这里我们使用 -4 -T -D 就好。各参数的意义在上面提到的官方文档页面中有详细的说明。

完成这一步之后在原 Raw 文件同一个文件夹下就会多出了一幅 tiff 格式的图像,这时候大部分图片浏览软件都可以打开它了(一些看图软件在安装插件后能够直接浏览 Raw 文件,比如我的 IrfanView,但实际上它们打开的只是嵌入在 Raw Data 中的经过一系列转码的缩略图而并非 Raw 本身)。但是为了避免各种图像浏览软件不同的解码方式对预览图像造成的影响,下面都使用 MATLAB 中的 imshow 函数来浏览图像。

同样是这幅 tiff 图片,如果直接使用 IrfanView 查看,得到的是下面的效果,明显比 MATLAB 中的要亮得多。

下面的图片截图如果不特殊说明都是指在 MATLAB 中预览的效果。得到的这幅 tiff 图像除了一些缩略图和文件头之外,基本上记录了与 Raw Data 同样多的图像信息,并且是 MATLAB 可读取的。由于编码上的差异,并且 Dcraw 不像大多数软件那样去除了图像边缘的一些像素,有时这个 tiff 文件的体积甚至会比 Raw Data 文件更大。与 Camera Raw、LightRoom 等软件打开的 Raw 文件最明显的区别就是,这张 tiff 图片是黑白的。这里牵涉到了去马赛克(Demosaic,但我更喜欢叫它彩色插值)的问题,下面我简单地谈谈我的理解,不一定完全正确。详细的介绍还是看 Wiki 吧。

大家都知道一般民用的数码相机只有一块 CMOS 或 CCD(这里不谈 3CCDFoveon X3),而无论是 CMOS 还是 CCD,都只对照射在它们有效面积之上的光通量敏感,并且产生与该光通量成正比的电流作为输出。这个输出实际上是由光源的光谱功率分布(SPD)、物体表面反射比以及相机传感器灵敏度共同作用的关于波长的一个积分结果。由于这是一个对整个可见光波段的关于波长的积分,显然不能够表示任何色彩信息。正因为这一点,目前消费级相机的传感器都是不具备任何颜色感知能力的,照片中所有色彩的信息实际上都是软件处理后的结果。正因为这点,我们一般不可能得到一张真正“未经处理过”的彩色图像。既然传感器只能感受光的“强度”而非“色彩”,我们就需要在传感器之前放置一些透过率满足一定波长条件的滤光片,使得经过这些滤光片后作用在传感器上的光信号只是某一波长范围内的积分形式(比如500 ~ 600nm),而非整个可见光波段(380 ~ 780nm)。实现这个功能最常见的滤光片就是拜耳滤镜,如下图所示。

灰色部分为 CMOS,彩色部分为对应颜色的滤镜,每个滤镜下方对应的像素点只能接收到该滤镜对应波长的光信号,而不是整个可见光谱范围的光信号。根据左上角四个滤镜从左至右、从上至下的顺序一般有“RGGB”、“GBRG”、“GRBG”几种。D3x 为“RGGB”型,具体相机的滤镜排列可以对着红绿蓝单色图片分别拍摄一张然后查看 Raw Data 的数值分布来获得。知道自己相机所用的拜耳滤镜的排列模式(Pattern)是进行彩色插值操作的前提(之所以绿色滤波片的数量是红蓝滤波片数量的两倍,主要是因为人眼对绿光最为敏感,这点不作展开说明)。既然知道了每个颜色滤波片背后 CMOS 对于像素单元上的光强度,以及各个像素之上的滤波片颜色,我们就可以对每个像素的三原色进行恢复,这就是去马赛克(Demosaic)或者说色彩插值的本质。举个例子,假设某个 CMOS 阵列上的滤波片为“RGGB”排列(参考上图),现已知某个像素点 x 对于为 R 滤波片,则其上、下、左、右四个像素点必定对于 G 滤波片,右上、左上、右下、左下四个像素点必定对于 B 滤波片,因此这时候像素点 x 对应的 R 通道数值就是该点 CMOS 上实际的电流大小;而 G 通道数值是对上、下、左、右四个像素 CMOS 电流大小取平均之后的值;同理,B 通道是对右上、左上、右下、左下四个像素取平均之后的值。当然这里说的只是最简单的插值方法 -- 领域取平均法,实际上各个厂商都需要考虑各方面因素对色彩插值进行算法上的优化。一般我们看到的 JPEG 图像或者彩色的所谓的 Raw Data,都必定经过这一步色彩插值。

但是我们现在希望把这一步骤留到 MATLAB 中进行,所以在 Dcraw 中选用 -D 参数使得插值先不被执行,这也是为什么上面得到的 tiff 图像仅仅是灰度图像 -- 因为它只记录了光强度信息,而不包含任何颜色信息。

这时可以在 MATLAB 中打开这幅灰度图像了。由于各个像素上记录的光强度是一个标量,这幅图像相当于一个 m × n 的矩阵,其中 m 和 n 分别为 CMOS 纵向和横向的像素数。

raw = double(imread('img1.tiff'));

现在终于可以开始在 MATLAB 中对相机传感器的原始数据进行处理了。整个处理过程大概遵循以下步骤:

如果不是要对图像进行处理,而仅仅是希望获得拍照时场景照度的相关数据,只需要进行到第三部 Demosaicking 即可。下面的步骤仅仅是在 MATLAB 中模拟一张相片从被 CMOS 记录到最终呈现在屏幕上的整个过程,相当于人为地重新干了一遍数码相机中图像处理芯片干的事情。若是出于研究的需要,可以对任意一步修改或增加操作。例如需要研究 Gamma 曲线,就不应该在 MATLAB 中执行我提供的 Gamma 校正(\(\gamma = 2.2\))的函数,而应该根据需要使用合适的 Gamma 曲线。

1. 线性处理(Linearization)

出于节省数据存储空间的目的,一些厂商(例如尼康和索尼)的 Raw Data 并不完全与像素点上的照度呈线性关系,而是会在编码上做一些处理,比如非线性压缩等。不过这里我们不需要担心这个问题,因为之前在 Dcraw 中使用 -4 参数时就已经解决了这个问题。我们只要确保各个像素的数值是分布在 14-bit(虽然 Dcraw 中的 -4 参数将图像设为16位,但其最大值仍然为 \({2^{14}} - 1 = 16383\))能够储存的范围之间即可,一般为 0 ~ 16383,并将超出这个区间的数值给拉回区间中。再将这些数值归一化至 0~1 区间中。

black = 0;
saturation = 16383;
lin_bayer = (raw-black)/(saturation-black); % 归一化至[0,1];
lin_bayer = max(0,min(lin_bayer,1)); % 确保没有大于1或小于0的数据;

要说明的是,我这里使用的 black = 0 以及 saturation = 16383 仅仅是针对 D3x 而言,不同厂家的相机,或者同一厂家不同型号的相机都可能不同。如果第一次使用某台相机不知道这两个参数,可以使用 dcraw -v -T 命令来查看,然后记下这两个数值供日后使用。要注意的是在命令行中每执行一次 dcraw 的命令,所生成的 tiff 文件都会覆盖掉原来的 tiff 文件,因此建议将原始的 Raw 文件先复制到另外一个路径下在执行 dcraw -v -T 命令。

下图中我使用的 Nikon D3x 和 SONY A7 就拥有完全不同的 black 和 saturation 值(索尼的 Raw 被压缩到了坑爹的12位...),因此在 MATLAB 中处理 A7 的图片时我就需要将上面的代码改为 black = 128 以及 saturation = 4095

线性处理到这里就完成了,在 MATLAB 中用 imshow 来查看这一步我们得到的图像(灯箱背景出现的四道条纹是摩尔纹的缘故,无视就好):

2. 白平衡校正(White Balancing)

其实将这一步叫作白平衡校正也并不是很恰当,因为这并不是指利用各种白平衡算法对图片的色调进行修复的那个白平衡校正,而仅仅是对 RGB 三通道乘上不同的增益系数,以补偿因为三种滤波片具有不同光谱灵敏度带来的影响。如果不考虑图像亮度(亮度的处理我们放在后面),将 R 通道乘以2并保持 G 通道不变,或者将 G 通道乘以0.5并保持 R 通道不变,这两种方式对画面颜色变化的影响是等效的。因此我们通常将 G 通道的增益系数固定为1,仅仅考虑 R 和 B 的系数。关于这两个系数具体数值应该取多少,则取决于相机的型号以及拍摄时使用的白平衡参数。实际上,在相机的白平衡设置里选择不同场景,就是在调整这两个增益系数。如果想还原为拍摄时使用的白平衡设置,可以在 Dcraw 中使用 -w -v 参数,这时屏幕上会显示出当时所使用的 R、B 通道的增益系数。

上图中的2.433594和1.347656分别表示拍摄这张图像时所使用的 R 通道和 B 通道的增益。要注意的是,一旦使用了 -w 参数,Dcraw 就会自动完成彩色插值的工作,这样得到的 tiff 图像就不再是原始灰度图像了。因此我们仅仅是使用 -w 来查看增益系数。如果不希望使用拍摄时的白平衡设置,则可以使用 -W 参数,这样不管当时用的是哪种白平衡档位,其 R、B 增益都是一个固定的值。例如对于这台 D3x 来说固定的 R、B 增益分别为2.625910和1.263930。在实际的图像处理应用中,通常需要固定相机的白平衡参数,即在相机的白平衡设置中手动输入色温。对于使用相同相机白平衡设置(Auto 除外)拍出的图片,它们都具有相同的增益系数。可以通过 -g 参数手动设置三通道的增益系数,例如 -g 1 1 1 1。

得到了 R、B 通道的增益后,我们需要将相应的像素值乘上这个系数。前面说过,不同相机具有不同的拜耳滤镜排列方式,因此需要根据实际情况进行增益系数的乘法。这里我使用的是相机拍摄时的白平衡参数,即 r_multiplier = 2.433594 和 b_multiplier = 1.347656。

wb_multipliers = [2.433594, 1, 1.347656]; % for particular condition, from dcraw;
mask = wbmask(size(lin_bayer,1),size(lin_bayer,2),wb_multipliers,'rggb');
balanced_bayer = lin_bayer .* mask;

上面代码中的 wbmask 函数就是根据实际拜耳滤镜的排列生成对应的掩板:

function colormask = wbmask(m,n,wbmults,align)
% COLORMASK = wbmask(M,N,WBMULTS,ALIGN)
% Makes a white-balance multiplicative mask for an image of size m-by-n
% with RGB while balance multipliers WBMULTS = [R_scale G_scale B_scale].
% ALIGN is string indicating Bayer arrangement: 'rggb','gbrg','grbg','bggr'
colormask = wbmults(2) * ones(m,n); % Initialize to all green values;
switch align
 case 'rggb'
  colormask(1:2:end,1:2:end) = wbmults(1); % r
  colormask(2:2:end,2:2:end) = wbmults(3); % b
 case 'bggr'
  colormask(2:2:end,2:2:end) = wbmults(1); % r
  colormask(1:2:end,1:2:end) = wbmults(3); % b
 case 'grbg'
  colormask(1:2:end,2:2:end) = wbmults(1); % r
  colormask(2:2:end,1:2:end) = wbmults(3); % b
 case 'gbrg'
  colormask(2:2:end,1:2:end) = wbmults(1); % r
  colormask(1:2:end,2:2:end) = wbmults(3); % b
 end
end

完成白平衡调整后的图像如下。由于 R 和 B 通道都乘以了大于1的数,图像的平均亮度较上一张略有提高了。

3. 色彩插值(又称去马赛克,Demosaicking)

上文提到的插值步骤在这一步中实现,经过色彩插值之后原来的灰度图像就成为了一幅三通道的彩色图像。空间插值有非常多的方法,这里为了方便我们使用 MATLAB 内置的 Demosaic 函数,它能够直接把单通道的灰度图像转换为三通道的彩色图像。由于 Demosaic 函数的输入必须为 uint8 或 uint16 类型,我们需要把原来的 double 型先转换为 uint16 型。注意这里的 'rggb' 应该根据相机的具体情况而调整。

temp = uint16(balanced_bayer/max(balanced_bayer(:)) * (2^16-1));
lin_rgb = double(demosaic(temp,'rggb'))/(2^16-1);

完成这一步之后我们就得到了最原始的彩色信息。一些应用中所需要的就是这幅图像的数据,可以使用 imwrite 函数将其保存在硬盘中。后续的色彩空间转换、Gamma 校正等步骤视情况决定是否需要执行。色彩插值后得到的图像如下:

4. 色彩空间转换(Color Space Conversion)

关于色彩空间这里不作过多介绍,举一个最简单的例子,同样一幅图像文件分别在两台显示器上显示,其各个像素的 RGB 值肯定是一样的,但是人眼看上去往往都存在细微的颜色偏差,这就是因为 RGB 色彩空间是设备相关的(Devices-Dependent),而任何两台显示器的 RGB 色彩空间一般都不会完全相同。具体的解释参考 Wiki。为了使一幅图片在各种显示设备上有尽量一致的视觉效果,我们就需要一个设备无关(Devices-Independent)的色彩空间作为传递媒介。目前在电子设备中用的最多的设备无关的色彩空间(有时也称绝对色彩空间)就是 sRGB 和 AdobeRGB。如果在 Dcraw 中使用了色彩插值,则自动包含了一个色彩空间变换的过程。Dcraw 先将相机相关的 RGB 空间转换至 XYZ 空间,然后再从 XYZ 转换到 sRGB 作为输出。在 MATLAB 中我们将这两个步骤合二为一。下面我将与相机相关的 RGB 色彩空间称作 Camera。对于大部分相机,我们可以得到从 XYZ 空间到相机相关空间的变换关系,即已知 XYZ-to-Camera。而作为两种绝对色彩空间,sRGB-to-XYZ 也是固定的。根据矩阵运算法则,我们可以得到从相机相关空间到 sRGB 空间的变换关系:\[{A_{sRGB \leftarrow Camera}} = {\left( {{A_{Camera \leftarrow XYZ}} \cdot {A_{XYZ \leftarrow sRGB}}} \right)^{ - 1}}\]

不同相机的 Camera 不同,因此我们必须获得适合自己相机的 \({A_{Camera \leftarrow XYZ}}\)。在 Dcraw 官网提供的 c 文件中收集了市面上大多数相机的 \({A_{Camera \leftarrow XYZ}}\),可以在 dcraw.c 中的 adobe_coeff 函数下找到,并且这个数据库是定期更新的。adobe_coeff 函数下的数字是 \({A_{Camera \leftarrow XYZ}}\) 中各元素乘以10000后逐行排列的数值,

或者也可以使用 Adobe DNG Converter 这个软件来查看相机的 \({A_{Camera \leftarrow XYZ}}\)。这种方法得到的数值对应了矩阵中逐列排列的各元素。以 D3x 为例,有:\[{A_{Camera \leftarrow XYZ,{\rm{D3x}}}} = \frac{1}{{10000}}\left[ {\begin{array}{*{20}{c}}{7171}&{ - 1986}&{ - 648}\\ { - 8085}&{15555}&{2718}\\ { - 2170}&{2512}&{7457}\end{array}} \right]\]

sRGB-to-XYZ 可以在国际照明委员会(CIE)公布的标准中查到,有:\[{A_{XYZ \leftarrow sRGB}} = \left[ {\begin{array}{*{20}{c}}{0.4124564}&{0.3575761}&{0.1804375}\\ {0.2126729}&{0.7151522}&{0.0721750}\\ {0.0193339}&{0.1191920}&{0.9503041}\end{array}} \right]\]

得到了这两个矩阵,自然也就能够算出 \({A_{sRGB \leftarrow Camera}}\)。在色彩空间转换过程中必须考虑这样一个问题:由于白色(客观意义上的白色)在相机的 RGB 空间和 sRGB 空间中都是用\({\left[{\begin{array}{*{20}{c}}1&1&1\end{array}}\right]^T}\) 来表示,而我们上述白平衡调整的目的就是要确保图像中白色的部分在任何空间中都呈现出白色。因此以下关系必须成立:\[{\left[{\begin{array}{*{20}{c}}1\\ 1\\ 1 \end{array}} \right]_{Camera}} = \left[ {\begin{array}{*{20}{c}}{ }\\ {{A_{Camera \leftarrow sRGB}}}\\ { }\end{array}} \right]{\left[ {\begin{array}{*{20}{c}}1\\ 1\\ 1\end{array}} \right]_{sRGB}}\]

根据线性代数的知识,要满足上式,矩阵 \({A_{Camera \leftarrow sRGB}}\) 的每一行元素之和必须为1,因此在 MATLAB 中我们必须再加上一个步骤,将 \({A_{Camera \leftarrow sRGB}}\) 各行归一化为1。色彩空间变换的代码如下。注意矩阵 XYZ2Cam 请根据自己使用的相机型号进行修改。

sRGB2XYZ = [0.4124564 0.3575761 0.1804375;0.2126729 0.7151522 0.0721750;0.0193339 0.1191920 0.9503041];
% sRGB2XYZ is an unchanged standard
XYZ2Cam = [7171 -1986 -648;-8085 15555 2718;-2170 2512 7457]/10000;
% Here XYZ2Cam is only for Nikon D3X, can be found in adobe_coeff in dcraw.c
sRGB2Cam = XYZ2Cam * sRGB2XYZ;
sRGB2Cam = sRGB2Cam./ repmat(sum(sRGB2Cam,2),1,3); % normalize each rows of sRGB2Cam to 1
Cam2sRGB = (sRGB2Cam)^-1;
lin_srgb = apply_cmatrix(lin_rgb, Cam2sRGB);
lin_srgb = max(0,min(lin_srgb,1)); % Always keep image clipped b/w 0-1

其中 apply_cmatrix 函数就是把我们得到的 \({A_{sRGB \leftarrow Camera}}\) 应用到原图像的各个通道上:

function corrected = apply_cmatrix(im,cmatrix)
% Applies CMATRIX to RGB input IM. Finds the appropriate weighting of the
% old color planes to form the new color planes, equivalent to but much
% more efficient than applying a matrix transformation to each pixel.
if size(im,3) ~=3
 error('Apply cmatrix to RGB image only.');
end
r = cmatrix(1,1) * im(:,:,1)+cmatrix(1,2) * im(:,:,2)+cmatrix(1,3) * im(:,:,3);
g = cmatrix(2,1) * im(:,:,1)+cmatrix(2,2) * im(:,:,2)+cmatrix(2,3) * im(:,:,3);
b = cmatrix(3,1) * im(:,:,1)+cmatrix(3,2) * im(:,:,2)+cmatrix(3,3) * im(:,:,3);
corrected = cat(3,r,g,b);

经过色彩空间变换后的图像如下,可以看出相比变换之前的图像,各个彩色色块饱和度明显增加,而白色色块颜色保持不变。

5. 亮度校正与伽马校正(Brightness and Gamma Correction)

对于大部分处于研究目的的图像处理流程,这一步不建议执行。在这一步之前,我们得到的图像仍然是与拍摄场景呈线性的,而线性数据往往才是对分析图像有帮助的。但是为了得到更好的显示效果,亮度与 Gamma 校正通常是必不可少的。如果对最后输出的图像存有异议,强烈建议首先返回到这一步中来寻找问题。根据经验,一张图像的平均亮度是像素最大值的四分之一时我们认为它是亮度合适的(注意这条法则不适用于所有场景,例如一张夜景图像中平均亮度往往会很小)。因此我们调整全局亮度使其符合这一假设:

grayim = rgb2gray(lin_srgb); % Consider only gray channel
grayscale = 0.25/mean(grayim(:));
bright_srgb = min(1,lin_srgb * grayscale); % Always keep image value less than 1

接下来是 Gamma 校正。Gamma 曲线是图像、信号处理领域使用最为广泛的非线性处理,我们最容易见到的就是 Photoshop 中的“曲线”功能,如果将曲线拉成 \(y = x^{\gamma}\) 的形状,就相当于对图像做了一次 Gamma 校正。Gamma 校正是一个很大的话题,这里不具体介绍,可以参考 Wiki 。在 sRGB 的官方文档中使用的是 \(\gamma = \frac{1}{{2.4}}\),并在函数值较小的部分应用了小范围的线性函数。但是现在大多数平台(Windows,Mac)都使用了 \(\gamma = \frac{1}{{2.2}}\) 的曲线,因此这里我们也使用 2.2 作为参数,并且不考虑局部的线性化。如果需要精确的 sRGB 标准的校正函数,可以查看其官方文档。

nl_srgb = bright_srgb.^(1/2.2);

经过亮度校正和 Gamma 校正后的图像如下。由于使用的 Gamma 曲线是一条凸函数,相当于把图像暗部的细节展宽,因此得到的图像要比校正前更亮。

到此为止一套通用的 Raw Data 处理流程就完成了,接下来可以根据需要再进行一系列的处理过程,比如使用一条 S 型曲线增加图像对比度、进行白平衡处理等等,或者直接保存为 tiff 文件再导入到其他图片处理软件中进行处理。

下面换一张比较生活化的图片,再对整个流程做一个展示。


▲ MATLAB 直接读取由 Dcraw 导出的 tiff 文件


▲ 线性处理后的图片


▲ 白平衡调整后的图片,这里使用 R、B 的增益系数分别为2.203125和1.378906


▲ 色彩插值后得到的彩色图片


▲ 转换至 sRGB 空间后的图片


▲ 经过亮度校正后的图片


▲ 经 Gamma 校正后的图片

References


103 Comments

  1. pingping 2017-10-17 Reply

    我做了色彩插值之后图片是绿色的或者是紫色的,原因不知道出在哪儿。

    • Author
      Captain 2017-10-24 Reply

      bayer pattern 搞错了

  2. Anonymous 2017-09-25 Reply

    OK,我重新编译下dcraw试下

  3. jiangxi 2017-09-25 Reply

    博主,你好,看过你的文章,写得很精彩。能把你用的那个TIFF格式的RAW文件发我下吗,我想在matlab上做下后期处理的算法,我这边也是那个dcraw出现“out of memory”问题没有解决,想直接用tiff格式的raw。邮箱:jb8776@126.com,十分感谢!

    • Author
      Captain 2017-09-25 Reply

      抱歉,那个文件我硬盘里已经没有了。“out of memory”的问题建议本地编译dcraw.c,不要用现成的.exe

  4. PHW 2017-09-12 Reply

    你好楼主,我刚接触dcraw我用你的代码处理了一张图片,但最后matlab运行后没有显示出图片,不知都出了什么问题。能帮忙解决一下吗?我的邮箱是1026224829@qq.com

    • Author
      Captain 2017-09-13 Reply

      能具体描述一下问题吗?

  5. aaa 2017-09-11 Reply

    您好,我和之前有一位的问题一样,结果颜色失真很严重,颜色明显偏红,相机EOS 5D mark II,请问有什么解决方法吗?

    • aaa 2017-09-11 Reply

      就是dcraw转成tiff时又暗又绿,白平衡处理后变红

    • Author
      Captain 2017-09-11 Reply

      你是用dcraw的白平衡算法还是在MATLAB中手动设置白平衡的?
      另外你不留邮箱的话即使我回复你了你也看不到呀…

      • aaa 2017-09-11 Reply

        614434938@qq.com ,matlab里设置白平衡的,我是按照你的步骤做下来的,一开始是.cr2转.tiff时就是绿色的

        • Author
          Captain 2017-09-11 Reply

          不是,我的意思是你在blog上留言的时候应该写上邮箱,这样我回复你的时候你会收到提示。
          cr2转tiff偏绿是对的,因为G通道灵敏度要高于另外两个通道。之前我遇到过类似Canon偏红的问题,不过时间太久我已经忘了当时是怎么处理的了。你方便的话可以把cr2和转换后的tiff都发给我邮箱,jqx1991#gmail.com

          • aaa 2017-09-11

            您好,已经发你了,是jqx1991@gmail.com吗?jqx1991#gmail.com格式错误

  6. yuan 2017-07-05 Reply

    是否可以改变 DCraw中的默认光源呢?

  7. yuan 2017-03-20 Reply

    “其实用 -g 1 1 1 1 得到的才是真正的 raw data” 是什么意思呢?

    • Author
      Captain 2017-03-23 Reply

      未进行白平衡校正的结果

  8. Polar Bear 2017-02-06 Reply

    首先感谢博主好文。
    有两个问题想请教一下。因为不是学习这方面的,如果问题很幼稚请多包涵。

    运行dcraw后的black和saturation的意义是什么呢?这个数值和传感器信号经过ADC处理后的信号是一个事物吗?而且我尝试的Canon 60D的的saturation是12279,貌似和2的幂没有关系。而且black也不是0。是因为传感器的动态范围喂不饱ADC,所以就在0和16383中间挑了两个数字作为上下限吗?

    第二个问题是为什么经过dcraw加载出来的tif看起来很暗呢?我在拍摄照片的时候,发现最右边的峰已经卡到histogram的边界了。图片应该达到了能达到的最大的亮度,但是解出来后反而变弱了,还需要后面的亮度校正。不知道这个是否跟tif是16位的有关。因为之前试过一个直接输出16位图片的相机的照片也很暗。

    • Author
      Captain 2017-02-06 Reply

      black是暗电流,可以理解为传感器的底噪;saturation是ADC理论能输出的最大值,数值本身没有意义,不同厂商甚至不同型号机子之间往往不同。
      如果你已经把图像转换至double类型的话(即像素值位于0~1之间),tif仍然很暗的原因有可能是没进行gamma校正。

    • Anonymous 2017-04-05 Reply

      您好,我对您的问题很感兴趣,如果有博主回复您,是否可以抄送我一份?liudaohui0805@sina.com

  9. 何金水 2016-12-01 Reply

    http://www.cybercom.net/~dcoffin/dcraw/dcraw.c 程序无法在VS2010中编译啊,缺少头文件

    无法编译呢,怎么弄哈?

  10. Anonymous 2016-10-19 Reply

    依据博主对Dcraw的详细阐释,后来终于解决了相机测量火焰温度的问题。我想问一下为什么我下载的源程序文件 http://www.cybercom.net/~dcoffin/dcraw/dcraw.c 无法在VC++ 6.0中编译啊

  11. dreamboy 2016-09-13 Reply

    感谢博主,看了后收益颇多。我现在想用单反拍的RAW来求图像中每一点的亮度,用于流水线上的检测,那么就是做完上述第2步白平衡后,matlab中读取的就应该是m*n矩阵的灰度图,由于是线性处理,也没有经过校正,可以直接理解成亮度之比。不知道我上述的理解是否正确。同时问题也来了,在此问题中,R和B的增益系数取多少,按我的理解,这个系数是指光源的光谱功率分布照射到R、G、B三片滤光片上的积分的比值,对于不同光谱类型的光源,这个比值应该差很大(此处应该可以理解成不同的参考光源,也就是数码相机里的白平衡设置吧),比如LED和钨丝灯。在哪里可以查到数码相机R、G、B滤光片的透过率曲线数据呢?

    • Author
      Captain 2016-09-13 Reply

      先纠正一个概念,RAW 中的矩阵灰度值对应的是传感器平面的辐射照度(irradiance)而不是场景的辐射亮度(radiance),而用来做流水线检测的话我猜应该是亮度,所以还需要考虑一下被检测物体与相机之间的几何关系,因为辐亮度和辐照度之间差一个余弦修正系数。

      第二,我不知道你提的亮度是物理量(单位是瓦每平方米每球面度)还是视觉量(单位是坎德拉每平方米)。如果是前者的话,直接用标准亮度光源做一下标定就行;如果是后者的话,需要事先获知相机三通道的光谱灵敏度(R、G、B 滤光片的透过率函数也是光谱灵敏度的一部分),然后在这三个光谱灵敏度和人眼明视觉函数之间做一个线性拟合,拟合出的系数就是 R、G、B 三个通道的增益系数。但是无论是哪种情况,增益系数都是光源无关的,只要固定住即可,不需要对每张图像进行计算。这个时候相机实际上就是作为一个标准测量仪器在使用。

      • dreamboy 2016-09-13 Reply

        首先我赞同第一点里面的余弦修正,但是为什么在第一点里用的是辐射量而不是光学量。在此时的RAW矩阵中的灰度值应该是经过上述步骤0、1、2之后的,我觉得已经是光照度了。接着说第二点,你提到了相机三通道的光谱灵敏度与明视觉函数线性拟合来求得增益系数,我觉得RGB三通道的光谱灵敏度是无法与V(人眼光谱光视效率)线性拟合的,从原理上就可以知道,R通道后照到CMOS上的是一个红色的光,也就是说R通道光谱灵敏度是红光响应高,其他波段响应低,同理GB通道。那么这3个通道的光谱灵敏度曲线和V的形状就差异很大,无法用K(系数)*V的方式表示,也就无法线性拟合。(同时这里在多问一句,如果真是线性拟合的话,那么第一点也应该是光照度了。)然后上述回答中说增益系数与光源无关,但在上文的例子中明显增益是随着相机白平衡的设置而改变的,而白平衡改变的就是光源的色温,也即改变了光源的光谱功率分布。(这条也许我在提问时没有讲清楚,我在流水线上检测的是灯具自身发出的光而非有一个固定光源照射到样品上的图像)
        接下来说一下我的理解,在单个CMOS上只能获得单个通道上的灰度值(比如上述例子中的R通道),然后根据四周的G、B通道灰度值,来计算出该CMOS上所对应的G、B灰度值。这里就设计到增益系数的问题,对于相机制造商来说,三个通道的光谱灵敏度曲线是已知的,对于不同的白平衡下的照射光源(也就是不同色温的光源),求出光谱功率分布(这个还存在疑点,相同的色温,光谱功率分布有很多啊,不知道是什么样的算法),那么就可以求出对应的增益系数了(具体怎么求还是不太明白,是通过RGB能量相等来计算的吗,因为我看到G=1的时候,RB都是大于1的,难道是由于绿光的光谱光视效率最高?如果我用相机自定义白平衡,然后用一个含有绿光较少,含有较多红光和蓝光波段的LED灯,会出现G=1时RB<1的情况么?》)。

      • dreamboy 2016-09-13 Reply

        回复完突然意识到,如果把增益系数定义成(照射光源光谱与明视觉函数的积分)/(照射光源光谱与通道灵敏度的积分)是不是就都能说得通了?

        • Author
          Captain 2016-09-13 Reply

          你反复提到白平衡这个概念,但是用相机来做亮度监测的话,只需要把它当作一个标准仪器来使用就行,白平衡是人眼主观的一种适应,而作为产品监测的话,关心的应该是物理属性,也就是辐射亮度。我的建议是,不要管白平衡增益系数,在标定时确定一个系数,监测时固定使用该系数就行了。如果你的相机G通道和人眼光谱光视效率曲线足够接近的话,甚至直接抛弃R、B通道的数据都没有问题。另外顺便说一下,用三通道相机去求光源光谱功率分布是不可能的,除非光谱功率分布有非常明确的基函数且自由度要小等于3,这个是最典型的同色异谱问题。

          其他一些细节我们可以QQ讨论。QQ88113009。

        • Author
          Captain 2016-09-13 Reply

          刚才关于白平衡的说的不是很清楚,我再补充一个,相机中白平衡算法的目的是为了让任意光源在图像中都具有和D65一样的色品,这是一种以人眼感知为导向的修正,而不是反映光源真实的色度特性,所以在把相机用作测量仪器时,不应该涉及任何白平衡的操作。

  12. Chlheng 2016-08-07 Reply

    不好意思,第一次尝试失败了,博主把这个回复删掉吧。
    我在上面重新回复了一次。

  13. Chlheng 2016-08-07 Reply

    很感谢博主的修改和回复,我也产生了三个问题:
    以下回复的公式部分是用MathType转化成LaTex后发出来的,不知道能否正常显示。

    问题一:
    wb_multipliers总是和XYZ2Cam一起使用的:
    $WB \cdot \left[ {\begin{array}{*{20}{c}}
    {{R_{raw}}}\\
    {{G_{raw}}}\\
    {{B_{raw}}}
    \end{array}} \right] = {A_{Camera \leftarrow XYZ}}\left[ {\begin{array}{*{20}{c}}
    X\\
    Y\\
    Z
    \end{array}} \right]$,也就是$\left( {{\mathbf{A}}_{Camera\leftarrow XYZ}}^{\mathbf{-1}}\cdot \mathbf{WB} \right)\cdot \left[ \begin{matrix}
    {{R}_{raw}} \\
    {{G}_{raw}} \\
    {{B}_{raw}} \\
    \end{matrix} \right]=\left[ \begin{matrix}
    X \\
    Y \\
    Z \\
    \end{matrix} \right]$,其中$\mathbf{WB}$就是以multipliers作为对角元的对角阵。
    两个矩阵是乘在一起才作用到$\left[ \begin{matrix}
    {{R}_{raw}} \\
    {{G}_{raw}} \\
    {{B}_{raw}} \\
    \end{matrix} \right]$,从数学上看其实就分不开了。所以我也很难区分哪个是WB,哪个是CC。

    • Chlheng 2016-08-07 Reply

      问题二:
      在原文中提到(记为方法一):
      ${{\left[ \begin{matrix}
      1 \\
      1 \\
      1 \\
      \end{matrix} \right]}_{Camera}}={{\mathbf{A}}_{Camera\leftarrow sRGB}}\cdot {{\left[ \begin{matrix}
      1 \\
      1 \\
      1 \\
      \end{matrix} \right]}_{sRGB}}\,.$ 因为需要保持这个映射关系,所以需要对${{\mathbf{A}}_{Camera\leftarrow sRGB}}$进行行归一化。
      然而这个表达式可以等价地写成(方法二):
      ${{\left[ \begin{matrix}
      1 \\
      1 \\
      1 \\
      \end{matrix} \right]}_{sRGB}}={{\mathbf{A}}_{sRGB\leftarrow Camera}}\cdot {{\left[ \begin{matrix}
      1 \\
      1 \\
      1 \\
      \end{matrix} \right]}_{Camera}}\,.$ 如果我不是对${{\mathbf{A}}_{Camera\leftarrow sRGB}}$进行行归一化,而是根据这个式子,对${{\mathbf{A}}_{sRGB\leftarrow Camera}}$进行行归一化,我实测过并不等价,出来的颜色是不同的。
      还有,如果假设下式(方法三):
      ${{\left[ \begin{matrix}
      1 \\
      1 \\
      1 \\
      \end{matrix} \right]}_{Camera}}={{\mathbf{A}}_{Camera\leftarrow AdobeRGB}}\cdot {{\left[ \begin{matrix}
      1 \\
      1 \\
      1 \\
      \end{matrix} \right]}_{AdobeRGB}}\,.$ 根据这个式子,也可以对${{\mathbf{A}}_{Camera\leftarrow AdobeRGB}}$进行行归一化,转换成AdobeRGB空间后,再把AdobeRGB空间转换到sRGB空间,这个我没试过,但是我觉得也是跟方法一不等价的。
      所以我想问,为何只对${{\mathbf{A}}_{Camera\leftarrow sRGB}}$行归一化,而不选择其它的方法?

      • Chlheng 2016-08-07 Reply

        问题三:
        就是把光谱D分解成RGB的问题,这个也是跟同色异谱相关的:
        $D\left( \omega \right)=r\cdot R\left( \omega \right)+g\cdot G\left( \omega \right)+b\cdot B\left( \omega \right)+O\left( \omega \right)$,其中RGB分别是某个色彩空间的RGB原色的光谱,O是一个误差项,因为利用RGB是无法叠加出所有的光谱,它只能叠加出人眼看到的所有的颜色(在某个色域内),这个O有很大的变化空间,在这个变化空间内,光谱D是变化的,但是人眼不能察觉到变化,这算是同色异谱的另外一种描述形式吧。
        那么就有:
        \[{{C}_{i}}=\int{{{T}_{i}}D}\,d\omega=\int{{{T}_{i}}\left( \sum\limits_{j}{{{x}_{j}}{{L}_{j}}+O} \right)}\,d\omega=\sum\limits_{j}{\left(\int{{{T}_{i}}{{L}_{j}}}\,d\omega \right){{x}_{j}}}+\int{{{T}_{i}}O}\,d\omega=\sum\limits_{j}{{{M}_{ij}}{{x}_{j}}}+\int{{{T}_{i}}O}d\omega\,.\]
        其中$L_{j}$分别代表RGB,$x_{j}$分别代表rgb,也就是以下矩阵形式:
        $\mathbf{C=MX+N}\left( O \right)\mathbf{=}{{\mathbf{A}}_{Camera\leftarrow RGB}}\left( O \right)\mathbf{X}$。这里边$\mathbf{M}$是一个常数矩阵,跟所选用的颜色空间和相机相关,$\mathbf{N}\left( O \right)$是以函数O为自变量的泛函,这应该是RGB值和相机raw通道刺激值之间的非线性和不可逆性的来源,同色异谱所带来的问题应该完全体现在这里了。${{\mathbf{A}}_{Camera\leftarrow RGB}}\left( O \right)\mathbf{X}$只是数学上写成线性变换的形式而已。
        那么问题来了,即使wb_multipliers和XYZ2Cam是D65的,并且拍照的时候光源也是D65的,但是光源照射物体后,反射光谱就成了$D\left( \omega \right)$了,这个$D\left( \omega \right)$是存在误差项$O\left( \omega \right)$的,因此依然存在上述所讲的非线性和不可逆性。因此我觉得即使是这种最理想的情况,也依然无法还原人眼看到的颜色。

    • Author
      Captain 2016-08-07 Reply

      我原来有设置单条评论的字数限制,现在已经取消掉这个限制,把三个问题统一回复你一下。

      问题一:你之前也提到了,要实现准确的颜色再现,$\mathbf{A}_{\textrm{Camera}\leftarrow XYZ}$(下面简称 CCM, Color Correction Matrix)必须是场景光源相关的。就我所知的相机图像处理引擎中,一般都是根据 AWB 增益来选择或者实时计算对应的 CCM。所以在图像信号的处理流程中,一般都是先做 AWB,再根据 AWB 的结果进行颜色校正。当然也可能有的厂商的算法能够把这两个过程在一步搞定,这我就不知道了。

      问题二:你说的方法二与方法一应该是完全等价的,如果出来的颜色不同,有两个可能的原因:a) 你的相机光谱灵敏度与人眼色匹配函数,或者说 linear sRGB 的光谱灵敏度之间无法用线性矩阵来描述,这时候无论是 $\mathbf{A}_{\textrm{Camera}\leftarrow XYZ}$ 还是 $\mathbf{A}_{XYZ\leftarrow \textrm{Camera}}$ 都不应该是简单的 $3\times3$ 矩阵,所以直接在 $3\times3$ 矩阵上求逆肯定是不等价了;b) 计算过程中的数据精度问题,例如你用的是 uint 型数据的话,某个操作使得数值超出了255,造成不可逆的信息丢失(这点可能性比较小)。
      Adobe RGB 空间我不太熟悉,也没看过它的相关标准,这个问题抱歉没法回答你。

      问题三:你的推导我没太看明白,你的公式中同时出现了 $T$ 和 $L$,照你的定义,$T$ 是相机的光谱灵敏度,$L$ 是人眼的响应,那么怎么可能在一个系统中同时出现相机和人眼?你好像把问题想复杂了,简单来说,只要你所使用的相机三通道光谱灵敏度与人眼色匹配函数之间能够用一个 $3\times3$ 线性矩阵来表示,相机就能够在 D65 下实现准确的颜色再现。就算这个线性关系不能被严格满足,也还可以通过更复杂的设备特征化方法来实现尽可能准确的颜色再现。

      • Chlheng 2016-08-07 Reply

        问题一:大致明白了,跟光源相关的也许归到问题三来说明吧。

        问题二:我用的double类型,读取tiff后马上转化成了double类型,图片足够暗不存在clip,但是方法一和二就是不同。其实这个纯粹数学问题,可以随意假设一个可逆矩阵M,先行归一化后求逆得到M1,或者先求逆再行归一化得到M2。通常情况下,M1≠M2的。至于你说无法用3×3矩阵描述,这个我就不清楚了。至少从Adobe DNG Converter和DxO Mark里边看到的都是WB矩阵+Color Martix矩阵的形式,而且都是3×3的。

        问题三:
        $D\left( \omega \right)=r\cdot R\left( \omega \right)+g\cdot G\left( \omega \right)+b\cdot B\left( \omega \right)+O\left( \omega \right)={{x}_{1}}\cdot {{L}_{1}}\left( \omega \right)+{{x}_{2}}\cdot {{L}_{2}}\left( \omega \right)+{{x}_{3}}\cdot {{L}_{3}}\left( \omega \right)+O\left( \omega \right)$
        我只是为了推导方便,把符号换了一下而已。也就是${{\mathbf{X}}^{\mathbf{T}}}=\left( {{x}_{1}},{{x}_{2}},{{x}_{3}} \right)=\left( r,g,b \right)$,$\left( R\left( \omega \right),G\left( \omega \right),B\left( \omega \right) \right)=\left( {{L}_{1}}\left( \omega \right),{{L}_{2}}\left( \omega \right),{{L}_{3}}\left( \omega \right) \right)$。这里边没有人眼,L不是人眼的响应,RGB是某个色彩空间(譬如sRGB)的RGB颜色谱(RGB三种光混色,那么每一种光都有自己的光谱,譬如红色R的光谱就是$R\left( \omega \right)$)。
        $D\left( \omega \right)$是进入Sensor的光谱,其实与什么光源照明没什么关系,因为相机不知道这个$D\left( \omega \right)$是直接光源照射的,还是经过1次或者2次或者更多次物体反射的。
        我再补充一下,如果$O\left( \omega \right)=0$,也就是${{D}_{1}}\left( \omega \right)=r\cdot R\left( \omega \right)+g\cdot G\left( \omega \right)+b\cdot B\left( \omega \right)$,那么$\mathbf{C=MX}$,这个式子完全可逆,且$\mathbf{M}$与${{D}_{1}}\left( \omega \right)$无关,是彻底的线性关系。但是大部分情况下$O\left( \omega \right)\ne 0$,那么${{D}_{2}}\left( \omega \right)=r\cdot R\left( \omega \right)+g\cdot G\left( \omega \right)+b\cdot B\left( \omega \right)+O\left( \omega \right)\text{=}{{D}_{1}}\left( \omega \right)+O\left( \omega \right)$,由于rgb三个值都没改变,人眼是无法区分${{D}_{1}}\left( \omega \right)$和${{D}_{2}}\left( \omega \right)$的,即同色异谱。但是这个$O\left( \omega \right)$所带来的影响被相机记录下来了,即$\mathbf{C=MX+N}\left( O \right)$,这时候既不是线性关系,也无法根据$\mathbf{C}$求出$\mathbf{X}$了,也就是无法还原rgb三个值了,由于rgb三个值和人眼视觉是一一对应关系,也就无法还原人眼的视觉了。

        • Author
          Captain 2016-08-07 Reply

          问题三里面,为什么 rgb 三个值和人眼视觉是一一对应关系?r、g、b 不是某个色彩空间的RGB颜色谱各自的权重吗?
          如果两个物体对于传感器A来说是同色异谱的,但是对于传感器B来说是可分辨的,那么唯一的原因就是A与B并非线性关系啊。所以你说如果两个表面对人眼来说是同色异谱,但是对相机来说是不同的,那么就说明了这台相机的光谱灵敏度没法用人眼色匹配函数来线性表示,那么自然就不可以用 $3\times3$ 线性矩阵来进行颜色校正啦,这时候可以用 LUT 或者非线性模型来校正。

          • Chlheng 2016-08-07

            r、g、b 确实是某个色彩空间的RGB颜色谱各自的权重。
            “如果两个物体对于传感器A来说是同色异谱的,但是对于传感器B来说是可分辨的,那么唯一的可能就是A与B并非线性关系啊。”这个说得没错,是非线性关系,但是是否非线性不是最重要的,最重要的是能否从B的刺激值算出A的刺激值,而问题三就是说明这是不行的。哪怕照明光源是D65,所有的矩阵都是D65下的矩阵也不行。不过我说的照明光源,并不是说把D65的光直接照到Sensor上,而是用D65作为光源照明一个场景,再拍摄这个场景。

          • Chlheng 2016-08-07

            $D\left( \omega \right)=r\cdot R\left( \omega \right)+g\cdot G\left( \omega \right)+b\cdot B\left( \omega \right)+O\left( \omega \right)$中rgb的值可以用这个方法求得:用人眼直接看$D\left( \omega \right)$,然后通过格拉斯曼颜色混合定律配上颜色匹配实验,通过人眼匹配出一组rgb值,这就是上式当中的rgb了。然而人眼无法察觉$O\left( \omega \right)$,这个$O\left( \omega \right)$是有无限多种可能的(当然它不是任意的),然而相机能察觉诸多$O\left( \omega \right)$中的一部分并且跟前面的部分一起叠加到相机的刺激值当中,分不开了。所以就无法通过相机刺激值,还原rgb了。这个$D\left( \omega \right)$的任意性很强,跟用什么光照明没什么关系了。

  14. Chlheng 2016-08-07 Reply

    感谢博主的文章,让我受益匪浅。但是我按照博主的思路,得出的照片一直都是偏红,相机为Canon EOS 500D。后来经过思考,发现其实从物理原理上看,相机根本拍不出人眼看到的那样子,这跟人眼的颜色恒常性无关。
    如果用$C_{ki}$, $T_{ki}$, 表示第$k$个相机第$i$个raw通道的刺激值和响应函数,这个响应函数是CFA的透射谱和CMOS响应谱的乘积,用$D$表示进入传感器的光谱分布。其中$C_{ki}$是一个数,$T_{ki}$和$D$都是一个以频率$ω$为自变量的函数。
    那么有$C_{ki}=\int_{-\infty}^{\infty}T_{ki}(\omega)D(\omega)\,\mathrm{d}\omega$。这意味着由$D$可以算出$C_{ki}$,但是由$C_{ki}$是无法算出$D$的,也就是所谓同色异谱。
    那么现在有两台相机,在同一个光谱$D$入射的情况下,可以拍出:相机1的三通道刺激值:($C_{11}$, $C_{12}$, $C_{13}$),相机2的三通道刺激值:($C_{21}$, $C_{22}$, $C_{23}$)。
    如果想用相机1的刺激值求出相机2的刺激值,一般情况下就需要用($C_{11}$, $C_{12}$, $C_{13}$)求出$D$,再用$D$求出($C_{21}$, $C_{22}$, $C_{23}$)。然而因为$D$无法求出,所以这个目标是不可能完成的。

    • Chlheng 2016-08-07 Reply

      那么如果把相机1看成相机,相机2看成人眼,那就意味着相机拍出来的数据将无法转化为人眼的视觉刺激。当然,这里边有一个特例,就是$T_{2i} = a_i\cdot T_{1i}$,其中$a_i$是一个常数,这种情况下可以在不需要求出$D$的情况下,直接得出$C_{2i} = a_i\cdot C_{1i}$,事实上这已经意味着相机1和相机2除增益因子$a_i$外是同一台相机了,归一化光谱响应完全相同。然而目前没有相机能做到与人眼的归一化光谱响应完全相同,因此用相机拍摄真实的照片是不可能的,根源就是同色异谱。
      既然是不可能的,那么就需要自动白平衡或者灰卡校准白平衡这些“艺术”手段来“美化”图片,在保证图片颜色中性的情况下依然能够有足够的饱和度。然而这只是使得图片好看,但是真实性从物理上是不可能的了。我在自动白平衡方面一直是个新手,因此看过很多文章,一直以为自动白平衡是因为人眼的颜色恒常性才需要引入的。现在开始明白了,根本原因还是弥补同色异谱造成的相机无法还原真实颜色的缺陷。
      事实上博主用到的XYZ2Cam, wb_multipliers都是随着入射光谱的改变而改变的,这意味着虽然形式上是线性变换,实质上是非线性的,甚至是无法求出反函数的,因此使用博主的方法处理出来的照片会是偏色的。

      • Author
        Captain 2016-08-07 Reply

        首先原谅我把你评论中的数学表达式都改成 LaTeX 格式了,原先的表达式看起来太累了…

        你说的几个观点都非常正确,这几个点也都恰恰是我的研究方向。理论上 XYZ2Cam 和 wb_multipliers 的确都应该随着拍摄场景的照明光源光谱的改变而改变,但是实际场景的光源光谱是无穷多种的,相机的硬件资源有限,不可能通过 LUT 的方法覆盖所有可能的 XYZ2Cam 和 wb_multipliers。如果你把 wb_multipliers 和 XYZ2Cam 放在一起讨论,其实就是在讨论如何在成像系统中实现人眼恒常性,但我个人更倾向于把这两者分开来处理:wb_multipliers 负责的是 AWB,XYZ2Cam 负责的是颜色校正(Color Correction)。

        Dcraw 中的 wb_multipliers 和 XYZ2Cam,实际上有一个前提:图片的拍摄场景照明光源为 D65(我验证过这一点)。如果你的输入图像的确是在 D65 下拍摄的,那么用这篇文章所述的方法得到的处理后的图像,可以认为是场景色品的真实再现;但是如果拍摄光源并非是 D65,按照这种方法,Dcraw 也会默认光源是 D65,因此出现偏色。这个时候有必要重做 AWB 以及 CC。其实我在文中就已经强调了,这里并不讨论如何进行白平衡校正或者颜色的准确复现,仅仅是介绍如何得到相机的 raw data(其实用 -g 1 1 1 1 得到的才是真正的 raw data)。

        另外,你说没有相机能做到与人眼的归一化光谱响应完全相同,其实早几年就有一些高端的设备会单独配一块低分辨率的 sensor 或者探头用来检测场景光谱,这种探头的光谱灵敏度是可以做到非常接近人眼色匹配函数的。

        如果你对同色异谱在成像上的应用有兴趣的话,可以考虑看一看 A. D. Logvinenko, How metamer mismatching decreases as the number of colour mechanisms increases with implications for colour and lightness constancy 以及 P. Urban, Metamer density estimated color correction.

  15. dongqing yin 2016-07-24 Reply

    能不能提供一下raw 图?

    • Author
      Captain 2016-07-25 Reply

      哪个的raw图?

      • dongqing yin 2016-07-30 Reply

        灯箱中拍摄色板的照片

  16. Shenyan Zhang 2016-07-15 Reply

    非常急切想知道dcraw的正确运行方式 按照网上说的在cmd里面运行输入dcraw根本是没法运行dcraw 本人不太懂电脑编程 所以那个cmd输入dcraw后也没有所谓的命令符号

    • Author
      Captain 2016-07-15 Reply

      文件名得是dcraw.exe才行。

      • Shenyan Zhang 2016-07-15 Reply

        已经解决了 非常感谢 另外想请教一下这个软件是不是有bug

  17. Shenyan Zhang 2016-07-15 Reply

    想请教一下我在 http://www.centrostudiprogressofotografico.it/en/dcraw/ 上面下载的dcraw 但是在电脑里面用cmd运行不了 放在c盘windows里面也运行不了

Leave a reply

Your email address will not be published.