Digital Imaging · 2021-03-08

如何『更科学』的测定某相机的宽容度

高品质黑白摄影的作业案例,觉得比较有进一步研究的意义,故在blog同步一份作业草稿

DA?ZE

请注意,这可能不是一个「完美严谨」的实验

不过从数据上出发,至少比张口用Bit Depth来判断宽容度的键摄们靠谱一些

数字测试实验报告

现场使用可控光源,相机白平衡已经经过校准

拍摄:

拍摄时选择ISO100,曝光范围-10Stop - +5Stop,步长为0.3Stops进行曝光变换拍摄,得到原始RAW文件。

为了绕过相机内部以及图像处理软件自带的DRT(显示渲染变换)对图像的干预,以至于影响宽容度测量,使用直接解码RAW的方式。

预处理

解码RAW具体步骤如下

img = img.postprocess(
                output_bps=16, #解码精度为16bit
                use_camera_wb=True, #使用相机白平衡设定
                no_auto_bright=False, #不使用自动曝光增益
                exp_shift=1.0, #曝光增益设置为1,即不做更改
                half_size=True  #半尺寸图像

                                  )

上述设定中,我使用16bit精度进行解码,并保存为16bit TIFF,用于后续分析,这一步目的是让后续步骤更加精准,这样一来,实验不会由于位深度不够导致测量误差。最后再标准化到8bit的数值即可得出结果。

除上述设定,解码RAW时,设定Gamma=1.0 (Scene-Linear场景线性),这样可以获得真实传感器获得的线性光信息,后续可以将Gamma再度进行变换,例如摄影师大多在sRGB (Gamma=2.2)下处理图像,即可再对结果图像进行Gamma转换并进行新的计算。

获取用户划定的ROI区域

def on_mouse(event, x, y, flags, param):
    global img, point1, point2
    img2 = img.copy()
    if event == cv2.EVENT_LBUTTONDOWN:  # 左键点击
        point1 = (x, y)
        cv2.circle(img2, point1, 10, (0, 255, 0), 5)
        cv2.imshow('image', img2)
        print('LBD')
    elif event == cv2.EVENT_MOUSEMOVE and (flags & cv2.EVENT_FLAG_LBUTTON):  # 按住左键拖曳
        cv2.rectangle(img2, point1, (x, y), (255, 0, 0), 5)
        cv2.imshow('image', img2)
    elif event == cv2.EVENT_LBUTTONUP:  # 左键释放
        point2 = (x, y)
        cv2.rectangle(img2, point1, point2, (0, 0, 255), 5)
        cv2.imshow('image', img2)
        min_x = min(point1[0], point2[0])
        min_y = min(point1[1], point2[1])
        width = abs(point1[0] - point2[0])
        height = abs(point1[1] - point2[1])
        cut_img = img[min_y:min_y + height, min_x:min_x + width]
        cv2.imwrite('lena3.jpg', cut_img)


测量

获得了不同曝光结果的「线性光」图像,我们对每组图像的灰板区域进行均值统计

通过划定一块ROI(感兴趣区域)矩形,并对这之内所有的像素值取平均值,以排除噪声对测量结果的影响

实际测量时,划定了一块约400x400的灰版干净区域

计算

解码RAW生成的TIF为16bit,数值区间0-65535,为了计算精确,我们进一步提升精度到32bit浮点。

将像素值除以65535,这步将画面中所有像素码值「标准化」到0.0 - 1.0。 

img = cv2.imread(os.path.join(dirpath, i), cv2.IMREAD_UNCHANGED) 
img = np.array(img) / 65535  # 标准化到0-1
img = img.astype('Float32')  # 设置精度为32位浮点
img = img[img != 0.0]  # 去除0值

如图所示

接下来只需要对之前划定的「目标区域」进行「均值统计」即可得出编码值 

avgnum = np.average(img)

数据分析

样本统计实际使用了 53 张 14bit RAW 图像,中间处理共耗时 42 秒,计算ROI区域共耗时 2 秒,得到 Gamma=1.0 时的响应曲线

并获得了如下的「 曝光档位-码值 」数据表

 

制图

直接由以上数据制图,可以得到Gamma=1.0的线性光响应图像

观察图像,发现在ISO100时,高光区域的宽容度约为3挡,超过3挡后出现白切(White Clamp)

初步观察图像容易以为-2.0档下的信息不足,但由于Gamma=1.0,因此其实在暗部区域依然「挤了」大量信息,将Gamma转换到摄影师处理图像常用的sRGB标准-Gamma约为2.2,并标准化到8bit,获得以下图像:

在对数坐标轴下,能够更好的评估传感器的极限响应

观察图像发现,曝光为-9档时,码值变化趋于平缓,可以近似认为这是传感器的弱光响应阈值

-

进一步思考

使用该方法测量,传感器的响应能力极限接近12档,但实际这12档是否都可用?

我们知道,当传感器接收到的光照严重不足时,噪声将会占领图像的主导,所以即使能够通过后期将画面的曝光重新配准到 0 Stop ,画面的质量也会非常糟糕。

为了进一步了解画面干净程度随着传感器接收到的光照变化,我们增加一步计算,对ROI区域内像素值的「方差」进行计算,得到如下结果,能近似反应噪声水平随着曝光的变化

也就是说,当传感器实际曝光低于-5 Stops,画面的干净程度将会迅速下降,在-6 Stops后几乎为不可用的状态。

用实际图像进行验证

拍摄一张-6 Stop的图像,然后在Photoshop将曝光配准到 ~0 Stops 

因此推测该传感器在ISO100时,最佳的可用宽容度(实用宽容度)为6-7档,[ 高光+3 - 暗部-4 ] 

RAW DATA

-10.00.0002362.35.70.2892308
-9.70.0002362.85.70.27700475
-9.30.0002463.25.70.27086622
-9.00.0002564.65.90.2517439
-8.70.0002866.66.20.21758626
-8.30.0003370.46.60.14211775
-8.00.0003672.97.00.10361197
-7.70.0004477.47.60.082401454
-7.30.0005582.38.40.10961231
-7.00.0007088.09.40.13559353
-6.70.0009394.410.70.13570395
-6.30.00123100.912.10.102633685
-6.00.00158106.713.60.059583493
-5.70.00199112.015.10.022942163
-5.30.00248117.116.70.012010948
-5.00.00312122.318.50.011867664
-4.70.00395127.820.60.010038988
-4.30.00494132.922.80.005481495
-4.00.00628138.525.40.0047046957
-3.70.00790143.728.20.0027132255
-3.30.00996149.131.40.0021490718
-3.00.01251154.334.80.0014286381
-2.70.01567159.538.60.0014590789
-2.30.01978164.842.90.0010625005
-2.00.02506170.347.70.00082777767
-1.70.03164175.753.10.0006198432
-1.30.03983180.958.90.0005162238
-1.00.06388191.873.00.0004276487
-0.70.06528192.373.80.0003652612
-0.30.07995197.080.90.00028825534
0.00.10081202.389.90.0002481517
0.30.12681207.699.70.0002322236
0.70.16012212.9110.90.0001931208
1.00.19847217.9122.30.10792131
1.30.25383223.5136.70.004079156
1.70.32104228.9152.10.0052663633
2.00.40515234.3169.10.0011798091
2.30.51071239.6187.90.00021078068
2.70.64501245.0208.90.00016942676
3.00.81359250.3232.20.00028660803
3.30.99953255.1254.91.4080001E-05
3.70.99998255.1255.04.7159956E-09
4.00.99998255.1255.04.7159956E-09
4.30.99998255.1255.04.7159956E-09
4.70.99835255.0254.80.0015111227
5.00.99905255.1254.90.0006000461
5.30.99924255.1254.90.00038734142
5.70.99964255.1255.00.00010429707
6.00.99998255.1255.04.7159956E-09
6.30.99998255.1255.04.7159956E-09
6.70.99998255.1255.04.7159956E-09
7.00.99998255.1255.04.7159956E-09