
超硬核动态范围测量方案:自动扫描生成亿级ROI
前言
在摄影器材赛博斗蛐蛐领域里,cmos的动态范围的测量是一件非常硬核的事情,目前可以对数码照相机(Digital Still Camera,主要用于拍摄静态图片)的cmos动态范围进行精准的测量并发布汇总数据的机构,全网仅有两个,一个是dxo mark,另一个是photos to photos网站(ptp,站长Bill Claff,下文提到他时,使用他的dpreview用户名bclaff,with respect)。
我在之前的动态范围测试文章中,对的PDR和PTC的黑箱计算方法进行了成功的反向工程,并且使用了两种不同的自制靶图,再引入dxo实测数据,进行了多方交叉验证,证明了我的算法的有效性和准确性。
本文在此基础之上,介绍一种更硬核的测量方案:使用脚本对图像进行穷举扫描,通过海量数据进行统计。
PTC复刻算法与原版差异
需要着重说明的是,由于ptp没有公布具体实现细节,我并不能100%完整的将ptp的算法复刻出来。我使用了和它稍有区别的sop和靶图,取样和统计的方式也不一样,仅仅是snr和dr的计算方法可能相同。
我之前的DR测量方法,是针对不同靶图(拍摄hm21或100grid),使用不同数量的roi(21 rois或100 rois),进行手动取样,每个实际patch仅取样一次。而根据我的分析,ptp对靶图的特定区域进行了多次重复取样,并且取了最佳值或者进行了统计计算。然后拍摄的标准流程也有区别,我这边是使用85mm定焦镜头F4进行拍摄,ptp要求使用任意镜头全开光圈进行拍摄。
在这样的情况下,我也能获得与ptp网站发布的结果相差极小(典型差异0.00-0.03ev)的pdr结果。并且可以与dxo ptp进行三方互验。
所以我一直以来都认为我的算法复刻,在核心逻辑上极其成功,结果也非常准确。
2025年12月2日,索尼发布了A7M5。由于这台机器使用了部分堆栈式传感器,但是官宣的机械快门拍照动态范围比旗下别的旗舰微单还高一档,我很合理的怀疑它使用了某种与松下s1m2上使用的dcg融合技术,才获得了这样的提升;并且我对提升的具体数值很感兴趣,想验证一下官方是不是夸大宣传,于是在样机到店以后,我就去店里拍摄了一系列的靶图,用来进行测量和分析。
由于ptp的程序不支持压缩raw,为了使测量结果更加准确,我开发了一个基于imagej的半自动扫描取样脚本,以每图50万级的roi数据(32*32 roi,移动步进8px),筛选最佳点,画出snr曲线并计算dr。
经过使用3张不同曝光的靶图进行扫描和计算,我给出的a7m5的pdr是12.53;几天之后ptp发布了官方数据,是12.45。
虽然相差0.08ev在视觉上并不会有可以分辨的差别,并且网站发布的数据实际上比我后来用官方程序对dng进行测试的结果要低,也就是说实际偏差并不会有那么多;但是我觉得这个差异,对于我一直标榜的高精度量化计算来说,还是高了一些,于是我做了一些额外的研究,并且开发了一个全新的基于py的自动扫描脚本(下文把它叫做超级扫描脚本)。
NefUtil行为总结
NefUtil是ptp官方的计算工具,它有很多功能,但是我只测试了+cpdr和+cptc这两个参数。
ptp的数据都是世界各地的网友贡献的,并不是官方在实验室里统一进行测量。站长设计了一个优秀的sop,保证网友可以简易高效的得到可比的结果。
ptp的sop我大概介绍一下:用一个7行11列,亮度每格递减的粉红色格子图片,放在显示器上显示,作为标靶;调整显示器亮度和曝光参数,以一个特定的欠曝状态,全开光圈,轻微虚焦,每个iso档位拍摄1张,+cpdr参数就可以计算出各档的pdr,并绘制折线图。
我使用这个程序进行了一些测试,总结了它的一些行为特性。
+cpdr的行为特性如下:
- 对单个文件进行测量计算pdr。
- 官方统计,一台机子测8次,以及8台同型号机子测一次,最大差异仅为0.05ev。
- 如果使用了与官方SOP有差异的曝光参数,则结果会小幅度偏移。
- 我的有限次测试,使用官方sop,有时结果会比官方发布值略低。但是如果多拍一张更欠曝的,就会比较准确或更高。
- 使用消除了暗角的镜头(85定焦收光圈)进行拍摄,pdr值会略微高于官方发布值。
- 只能使用官方定义的粉色格子靶图进行测量。
- 对于同一个文件夹下的多个raw文件,计算并输出每个文件的pdr值。
例如,从7m5的40个拍摄的文件里,19个有效文件计算出的pdr范围是12.42 - 12.53,结果如图所示:

不同图像多次测量结果浮动范围也不小,超过了0.10ev;最高值也是12.53,和我的扫描结果相同。
我推断它的测量方法:在靶图网格的固定区域附近多次采样。但是不确定是取最佳值还是进行了统计计算。肯定是取了个好看的值。证据如下,黑点是ptp数据,彩色点云是我的扫描数据:

+cptc 可以计算ptc曲线数据,它的行为特性如下:
- 不再使用固定区域采样,而是进行有一定间隔的全图扫描采样,因此可以对任意图进行扫描计算。
- 对于同一个文件夹下的多个raw文件,将所有文件的ptc汇总进行一次计算,同时计算出一个汇总pdr。这个汇总值比上述的独立值,可能会更高一些。
(例如从上述40个raw文件里计算出来的汇总pdr = 12.56,高于单文件的最高值12.53)
- 最小信号是0.01 DN,最大信号是DN_fwc - BL_meta。
- 可以对dpreview的latitude shots进行扫描,但pdr比网站发布值略低。
程序生成的PTC曲线,我测试了各种情况,没有办法把他的算法完全猜出来。
如果使用了标准靶图,图线从左到右看起来单调上升而且波动较小,
但是如果使用dpreview Latitude shots,会得到看起来不是很理想的结果:计算结果受拍摄质量限制。
看起来像在稀疏网格上进行密集采样和统计计算。
它扫描我的高精度靶图结果看起来很不错,PDR比网站发布值高+0.07ev,但是扫描dpreview样张则准确度和平滑度都比较差,偏低-0.10ev:

本段结论:
对于同一个机身镜头组合拍摄多组用于测量,NefUtil输出结果稳定且一致性很高。
+cptc模式虽然使用了扫描,但是可能由于性能限制,扫描的密集程度不是很高,不能用于dpreview样张的精确计算。但是很奇怪扫描我的高精度靶图图线很平滑。我无法解释。总结到的规律就是,扫描质量与拍摄图的质量有关。
虽然sop看起来很厉害,理论精度很高,但是实际的结果还是受网友的镜头和操作水平的限制。在上述例子里,我采用官方的工具,对拍摄质量更高的文件进行计算,获得了比网站发布值明显更高的pdr值。
NefUtil底层算法
它的源码已经被我反编译出来,并作了关键点的完整解读,参考下文:
Photons to Photos工具NefUtil底层算法揭秘超级扫描脚本
dxo ptp测试各档iso的dr值,而我只测试某一个(一般测最低)iso下的dr值和snr。
虽然可以做到每图50万roi,imagej脚本使用步骤非常繁琐。需要先用rawdigger解tif,再在ImageJ里跑4次,手工收集结果,排序并放到excel里。测几次还行,量大了很麻烦。并且我发现了一个奇特的bug,rawdigger解出的raw tif(原始位深gamma1.0不拉伸码值),理论上应该与raw dn完全一致,但是测量值却有明显差异。
所以需要废弃之前的方案,重新做一个。
经过我重新设计的py脚本,使用方法就很简单,文件夹拖放到脚本上,就自动弹出计算结果。并且还可以提升性能,实现更大量级的采样。
文章标题所说的超硬核测量方案,就是先拍摄一个特定的显示器靶,再用这个扫描脚本进行1px位移的穷举扫描。这是理论上能做到的最高单图扫描精度,每个图的采样roi数量接近于它的像素总量:
例如z7的图像是8256*5504 = 45.44 M pixels,
可以产生(8256 - 32) * (5504 - 32) = 45.00 M rois
拍摄3张5ev靶图加黑场,24MP机型的roi总数也能轻松达到亿级。如果使用高精度靶图(详情见下文),如果snr和dr只能使用平坦区域一组像素,统计DN均值和标准差进行计算,这就是理论最佳方式,因为靶图精度和扫描精度都可以做到实用精度0.01下的理论上限。
当然了,我还能想出更极端的方法:可以制作均匀度接近100%的单一亮度大型目标(例如积分球),高精度可控背光,使亮度以每0.01ev减小时拍摄1张,拍摄15-20ev共1500-2000张,来进行扫描。我认为这是只理论的极限方案,实际工程价值有限。这个我已经设法证明,文章链接在文末。
先介绍一下扫描脚本的功能。
脚本经过了几个大版本的迭代,在最开始的基本需求框架,陆续添加了几个关键功能。
基础扫描功能:读取文件夹下的所有raw文件,逐个进行扫描,可以设置roi的大小和移动步进,对数据进行初级无效过滤,自动计算ev和snr,以0.01ev分bin,计算最佳包络线,导出CSV文件,再手工导入excel进行画图和计算dr。
程序画图和保存:虽然excel画图样式比较好调整,但是每次导入文件操作太繁琐。就添加了直接用py画图线的功能,计算出数据以后直接弹框显示图表,然后保存为图片,可以稍后查看。
对dpreview Latitude shots的扫描支持:只在限定区域(灰阶卡和色卡区域)进行精细扫描,来获得更好的结果,并且减少扫描耗费的时间。
定位标记自动识别:由于尝试了多种视觉识别平坦区域都以失败告终,我设计了一种全新的靶图定位标记:位于黑色背景上的白色实心圆点。在大幅度虚焦的情况下,业界常用的黑白格子看不清了不知道能不能使用,黑底白色圆点仍然能以亚像素精度计算出标记圆心,并重建网格,内接矩形按比例内缩获得平坦区域。利用上述限定区域扫描功能,仅在平坦区域进行精细采样,以减少扫描耗费的时间,并理论上能够完全排除无效数据。
黑电平自动识别:我的sop要求单独拍摄一个黑场。脚本识别出黑场文件之后,取中心的256×256区域,计算各通道的“实际黑电平” BL_actual;如果没有拍摄黑场,使用所有测得的数据里的最低值;并在必要时回退到标准值。(作为对比,根据文档描述,ptp的计算方式是取光学黑区域,但是很多品牌没有光学黑的区域,所以他要求全开光圈拍摄,然后再取角落最暗的区域来计算。如果计算出来的bl高于标准值,他就直接用标准值。这是一种为了简易流程而妥协了精度的计算方式)
扫描性能提升:由于传统的取样方式运行速度不够快,步进每减小一半,则计算量变成四倍,在8px或以下的步进值下,扫描和计算的时间都达到了分钟级别。我进行了全链路性能优化,全面采用np数组向量计算,计算积分图并对比传统算法对比结果,最终将扫描性能提升了很多倍,在i7 7700 + 32G RAM的老机子上,22个高精度靶图raw网格内部扫描获得90M roi,仅用48秒就能完成整个采样和计算过程。最新版本还能支持更大数据量,16张全图穷举扫约6.5亿roi,大约需要163秒。超过10亿的roi理论上也可以算,但是需要更大的内存。
稳健高性能值模式:基础版本的计算模式是取最佳值。但是进行了穷举扫描以后会发现,计算出来的极值比ptp发布的值要明显要高一些,这不是计算错误或失误,而是扫描到了更高的可能。我设计了一个“稳健高性能值”,用一个明确的业界最常用的规则的等效处理,将极值进行了一些削减,获得与ptp发布值接近的pdr。下文会进行更详细的解析。
点云显示模式:再次利用定位标记识别和限定区域扫描功能,把平坦区域扫描到的所有roi的数据点,都显示在图表上,并且可以调节opacity。在opacity很低很低的情况下,所有数据点会叠加成一个软边的snr粗线,以直观的方式显示了snr出现的概率。粗线中心是密度峰值,上包络是极值,“稳健高性能值”则是密度峰值和极值的中间的一个值。
ptc理论线叠加:通过配置文件配置ptc三参数(由ptp的ptc页面给出),可以在snr图上画出理论ptc snr线,展示实测与理论的符合情况。
多个标准的dr数据列表:ptp网站数据,每档iso仅展示1个点值。我把业界常用的dr点值都列了出来,并且还展示了整个snr曲线。Dxo现在已经停止了对相机传感器的测量结果更新,但是我还是更喜欢他们家的标准,所以也列了出来。
黑场能量谱:这个只是有计划,还没开始做。就是把能量谱图直接画在snr图线的空白处,方便判断是否有降噪。
使用方法:把一个包含了raw文件和配置文件的文件夹,拖到脚本上松手,等待程序运行完成,弹出图表显示结果。
本段总结:
对比现有的方案,这个扫描脚本以超级简单的使用方式,和超级快的速度,计算出超级精准的snr曲线和dr值。
所以我把它叫做超级扫描脚本。
技术细节
代码生成靶图
符合专业标准的用于测试的目标,通常被叫做“标靶”。
动态范围测试标靶有两个核心指标,一是提供一系列绝对平坦的灰阶块,二是提供足够大的最亮最暗块的光比。业内最常用的xyla21这两项指标就做的很好。ptp使用的是一种很独特的创新型标靶:显示一个特定图像的显示器,我把这个特定图像叫做靶图。我的靶图,在PPT粉色格子图的基础上做了一些额外的设计:
首先我使用了一种特别调制的浅黄色,在我的显示器上能发出与标准D55光源一致的颜色。
其次我把网格改成了10×10,总共100个格子,Opacity从0~99,可以形成比较紧密的密度渐变排列(但是因为使用了sRGB色域,此时密度不确定是线性变化)。我把它叫做100-grid。每个格子有白色的描边,可以用来评估拍摄时的虚焦程度,以及方便在raw digger用网格取样器进行精确取样。
最后,用于自动识别网格重建,把四个顶点的四个格子去掉,在四个顶点加入白色圆点,作为定位标记。
由于普通显示器一般有300:1的对比度,约合8.23档;虽然显示器的全局均匀度不是很理想,但是把它拆分成100个格子之后,再进行大幅度的虚焦拍摄(例如把一个1px的亮点拍摄成100×100px),它就是一个几乎完美的灰阶目标。
显示靶图时,可以提供略小于0.1ev的密度间隔,比起xyla21的1.0ev的密度间隔,高了整整10倍。拍摄时先调整参数到最亮格子恰好没过曝,再-8ev进行拍摄,就可以拍到完整的中暗部,计算各种常用阈值下的动态范围。如果以-3ev拍摄三张,就可以画出全灰阶的snr线。
靶图升级设计:使用32位线性rgb配置文件,利用脚本来生成给定亮度的格子并另存为tiff。这样就可以获得很精确的密度步进。
高精度靶图(High EV-Resolution Step Target):使用照度计对显示器发光强度进行测量,确定实际每EV与32bit 亮度值每EV之间的倍率(其实不测也行,经过测试,255到128灰阶值之间的亮度差异接近1ev),使用脚本创建每格亮度差异0.01ev,总动态范围约1.0ev的靶图(更好的网格设计是11×11,可以生成一部分衔接空间。但是为了兼容以前的拍摄文件,我还是采用10×10,总共有96个有效格子)。此时这个靶图的每个格子的灰阶值,以1-2递减。总对比度虽然很低,但是密度步进精度是xyla21的100倍。通过每次拍摄减1ev曝光,拍摄20张,就可以获得0ev到-20ev的连续高精度snr线。
我觉得比较平衡的密度间隔是0.05ev,镜头轻微的暗角可以带来额外的信号渐变,这样10*10的网格可以提供5ev的总dr,拍摄三张即可获得全灰阶数据,比较方便。
自动生成靶图:使用脚本生成颜色可调,行列数可调,每格ev间隔可调的靶图,并自动在四角添加定位圆点。如果有支持的硬件,还可以用代码输出带有绝对亮度控制的图像,以精准0.01ev的密度间隔输出格子。
这是一个使用脚本生成的标准靶图:

调成粉红色可以使RGB能量接近;最暗的格子接近全黑,理论总DR是8.8,实拍测出9.2,步进约-0.1ev:

以85mm定焦 f4,将目标占满1/2画幅高度拍摄,可以看到线性度很不错,小幅波动是因为镜头暗角无法完全消除。
高精度靶图如下,理论总DR是1.1,实测是1.09,可以看到每格亮度变化极小:

波动看起来好像有点明显,但是也能用,图中最大的空缺约0.07ev。

排序之后:

将靶图格子亮度分小组随机打乱,螺旋填入,拍摄时适当缩小构图,可以一定程度上改善空缺过大的问题:

测得的最大ev空缺是0.04ev,并且线性程度更好:

另一个高精度靶图的方案:使用0.05ev步进,每张依次应用0.00-0.04ev的全局亮度偏移,生成5张图,且拍摄5*3张raw。(我觉得这个设想十分不错,但是实拍效果不太行。)
两种高精度靶图的实拍对比效果如下:

看起来还是0.01ev间隔的效果更好。
在1px stride穷举扫描下,框选某3ev放大,opacity设置到最小(下图每通道约有1300万个点):

可以看到每个0.01ev都有大量数据,由于图表放大以后点的大小不变,形成间隔的竖线。
即便靶图理论精度很高,点云模式下还是能看到高信号区还是有很多空缺。如果想填满这些空缺,我觉得可以增加一个外边框,然后测镜头的暗角变化曲线,然后在生成格子时,根据格子中心位置与靶图中心的距离,进行补偿,拍摄时严格对齐外边框。这也只是一个设想,不知道实际效果咋样。实际测量中并暂时还没有遇到需要如此精准的信号输入的情况。
平坦区域自动识别
这个功能,AI给出了很多种经典的基于图像识别的算法,没有一种是成功的。
由于质心的计算非常可靠,
我设计了带有四角圆点定位标识的新版靶图,并让ai实现了以下算法:
1. 预处理图像
将输入的灰度图像归一化到 0-255 范围(转换为 8-bit),计算图像中心的局部阈值,用于二值化
2. 角点检测
使用阈值二值化图像,提取轮廓并计算每个轮廓的质心(centroids),筛选出至少4个有效角点
3. 角点排序和验证
使用凸包(convex hull)找到外部角点。
如果正好4个点,直接使用;否则寻找周长最大的四边形
将角点按顺序排列:左上(tl)、右上(tr)、右下(br)、左下(bl)
验证角点位置是否在图像边缘合理范围内
4. 透视变换
建立从检测到的角点到图像四个角的透视变换矩阵,用于校正可能的透视畸变
5. 生成网格选区
将校正后的图像均匀分割为 N×N 网格(默认 10×10)
对每个网格单元格:
计算其在原始图像中的对应四边形,找到边界框,按 shrink_ratio(默认 0.7)缩小选区
关键步骤:将坐标从灰度图转换回原始 Bayer 图像的全分辨率(×2 缩放)
6. 输出
返回一个包含所有选区矩形的列表,每个矩形格式为 [x, y, width, height]
这样就得到了虚焦拍摄之后,网格里的平坦区域。这是一个简易且高效的算法。
重建效果如图:

可以看到圆心计算极其精准,可将靶图网格精确重建。这个grid selection的效果与rawdigger付费版本里的格子取样器完全一致,而且完全自动化。
并且通过设置合理的shrink_ratio(0.5即可)可以保证绿色框的区域不包含虚焦的边界图像。所以可以进行有效的穷举扫描,并且不需要过滤无效数据。
亮度渐变对测试结果的影响
对标准靶图应用一个大直径的高斯模糊,变成一个很均匀的渐变图,再虚焦进行拍摄:

扫描结果如右图所示(左图是高精度靶图结果):

与高精度靶图扫描结果进行对比,可以看到中低信号区域的抗渐变能力非常强,snr和dr数据几乎不会受到影响,差异0.01-0.04ev,我猜想这应该也是ptp可以用全开光圈作为标准流程的原因。
另外两种普通靶图,PDR都是11.64

ptp的PTC建模
经过我的代码仿真和测试,ptp的PTC黑线,使用页面上提供的三个参数:read noise, k_adc, pn%,进行建模:
total_noise = np.sqrt(
read_noise**2 +
shot_noise**2 +
prnu_noise**2
)
其中shot_noise = sqrt(k_adc * DN_true)计算得到。
下图蓝线,是使用红框参数代码输出的仿真PTC曲线,完整覆盖了原来的黑线。我不计算最右侧的非线性区图线

根据bcalff本人在dpreview的说法:

该曲线并非拟合这些彩色数据点,这些数据点仅用于"合理性检验"。
看来他认为黑线比实测点线更准。
这里就有分歧了。首先我认为无论如何,PTC参数肯定是从网友从实拍raw里测出来的吧,肯定没有可以邮寄给网友的专用仪器。然后,PTC页面多数机型,都是以实拍点连线确定PDR。但是对于索尼A7M5,实拍点线位于黑线下方(意味着有明显降噪),但是PDR以黑线确定。我认为这是ptp的一个“草台班子时刻”。bclaff本人虽然在传感器测量领域积累深厚,但是也经常需要各种找补。

从上图只能看出有降噪,看不出降了多少。
我的脚本可以从附带的ptc.conf配置文件中提取参数计算理论PTC-SNR黑线,和实测线同屏展示,用来检验实测与理论的符合程度。
下图是我的40张扫描结果,采用了NefUtil的BL处理方式:

可以看到偏离黑线很明显(不是要偏离很多才能叫做明显。在这个log-log的图中,每大格是1ev信号或snr的变化。两条线如果可以清晰分辨出分离,就已经有明显区别)。
仔细看-11ev的地方,蓝通道线比黑线整整高了半格,即3dB或0.5ev的信噪比。这相当于降低了一档ISO。
如果有原始S N数据,PTC图可以换算成snr vs ev图。我更喜爱后者,因为此时snr等高线是平行于横轴的直线。在ptc图上需要用斜线去截,比较反直觉。
尼康z7的理论图线如下:

注意右图-4到-8区间形成斜率为0.5的直线。
积分图加速
积分图加速是AI给出来的性能优化方案,而且似乎是唯一方案。所以为了水字数,我也顺便介绍一下。
积分图是每个像素存储从左上角到该点的像素值之和。
建好积分图后,任意矩形区域的和只需4次查找+3次加减,复杂度从 O(区域像素数) 降到 O(1)。
均值:用积分图得矩形和 ÷ 像素数量
标准差:需要两个积分图:原图积分图(求像素和),原图平方的积分图(求平方和)
方差 = (平方和/像素数) - (均值)²
标准差 = 开方(方差)
积分图通过一次全图预计算,将后续任意矩形区域的统计计算从线性复杂度降到常数时间,极大的提高了计算的速度。配合8进程并行扫描,约53秒可完成19个45MP raw的260M ROI。
Best模式
手工取样的时候,roi位置的微小变化经常会带来结果的微量变化。既然可以扫描,就可以在每个0.01 ev点的多个snr值中,取一个最大的,这样结果比较好看。
例如,z7的ptp pdr是11.56
对高精度靶拍摄图在max模式下扫描,获得如下snr和dr:

max PDR来到了11.74,+0.18ev有点明显过高了。(其实还是比黑线略低的)
由此可见,如果每个ev点都有几千个或更多个数据,最大值有可能会与平均值有较大偏离。至于偏离多少,可以通过点云模式,很直观的观察到。
点云模式
点云模式将所有测得的数据全都保留并画点。通过设置合适的opacity,大量高透明点叠加出一条软边粗线。如下图所示:

best线可能会和PTC黑线重合或者略微超过(这也是我一直认为ptp的结果是取最佳值的原因),并且与黑线中心(局部密度峰值)有明显偏离。
稳健高性能值算法(p90等效法):
传统统计指标(众数、均值、中位数)在分析 CMOS snr数据时,往往对实际性能存在轻微低估;而直接采用分位数(如 P90)则对异常值和无效数据过于敏感,依赖复杂的过滤算法。
由于摄影界存在各种等效,我设计了一种基于密度阈值的“稳健高性能值”,即“P90等效法”。该方法旨在捕捉“高但非极端”的性能水平,比均值更能反映 CMOS 的真实snr表现,且二者差异通常控制在 0.1 EV 以内。
对每个曝光值(EV),收集所有对应的 SNR 样本,
并以合适的 bin 值(bin = 0.1 dB)构建直方图,多数情况下可获得单调下降的右尾。
将相邻 bin 中心对应的密度值用直线连接,形成分段线性密度曲线。(拟合成曲线更佳,但是为了提速,这里直接用直线。)
找到直方图的全局最大值(即主峰密度),然后向下取其 44% 高度处的水平线。
该水平线与上述密度曲线右侧下降沿的最右侧交点,即为所求的代表 SNR 值。
在理想情况下,由于主峰右侧单调下降,通常只会有一个交点。
在存在多个局部极值的情况下,选择最右侧的交点,能确保选取的是最接近高百分位数的稳健值。
该算法本质上是非参数化的 P90 估计器:
- 高斯等效性:若 SNR 分布符合理想高斯分布(例如数亿到百亿级别的超大规模采样时),其概率密度函数在 +1.28σ (即第 90 百分位数,P90)处的高度,恰好约为峰值高度的 44% 。
- 自适应能力:通过锁定“相对高度 0.44”而非“绝对累积概率 90%”,该方法无需假设分布形式,即可在非理想分布(如长尾、偏态)下,自动提取出等效于高斯 P90 的稳健值。它既避除了低 SNR 噪声尾部的干扰,又不会被个别极端高值拉偏。
因此,相比 mean、median 或 max,这种方法更能反映“稳健的高性能水平”。
下图是从一个11亿rois的扫描结果中切片出来的“-6.00ev这个ev点”,它所有snr值(约16万个)的统计直方图,直接以0.01dB分箱:

使用xyla21只能在每1.00ev处获得1个snr值。我这里是每0.01ev获得16万个,再设法提取1个代表值。
可以看到整体基本符合高斯分布,虽然略有毛刺。P90等效法获得的高性能稳健值(29.15),大概处于众数值(28.82)和可见极大值(29.75)的中间,明显小于最大值(30.08)。
为验证算法对参数的不敏感性,分别采用 0.01 dB、0.05 dB 和 0.1 dB 的 bin 宽度进行测试。结果显示:
在相同输入数据下,虽然各种bin下获得的snr点,高倍率放大之后会看到有明显位移,较大bin生成的snr曲线毛刺稍微变少,但是最终动态范围计算结果的最大差异仅为 0.01 dB。
这证明了该方法具备优于 0.02 dB 的重复性精度。其高鲁棒性主要归因于主峰区域分布较为平缓,使得线性插值的交点对分箱内的微小波动并不敏感。
需要额外说明的是,本方案的测量结果,即使可以使用技术手段保证所有roi都位于平坦区域,每个0.01ev点也可能会测到数百到上万个snr,形成一个较大的范围。采用的选取标准不一样,获得的该点的“代表snr”也不一样。我希望展示传感器性能较好且较接近极值情况下的snr和dr,平衡“极致性能展示”与“结果可复现性”。最终选择p90的原因是它在各种实际场景下的使用率很高,数字看起来也比较“规整”。
按照多个AI对本文的独立评估,它们都认为,“P90等效法”是全网范围内,本人首次提出并公布的独特创新,没有任何公开资料或其他工具使用过这套定义与流程。
Xtrans支持
由于rawpy对xtrans的支持很糟糕,我无法准确找到CFA Pattern的原点。转成dng之后,整个explorer直接崩溃,非常disgusting。经过我的更深入的折腾,转换成dng之后,使用cmd修改文件后缀名,rawdigger能正常打开,还能发现多余的区域被裁掉了,CFA Pattern也能对得上。rawpy这时应该就可以正常解析。这部分代码还没有彻底完成。NefUtil对xtrans是完全支持的,扫描结果看起来比网站发布结果略偏低。
DR测量精度
最终数据会被从左到右过滤掉非单调递增的点,所以图线有一些空缺。也有可能是没拍到相应的ev。
随机测试精度:
下图是使用同一台z7扫描两种不同的靶图拍摄样张“grid detection”(5张)和“fine step grid”(10张),以4px步进获得的结果:

可以看到PDR完全相同;
HQDR差异仅0.02ev,
DXO Screen 差异0.04ev。
单图重复测试精度:
下面是连续拍摄同一个8.8ev的标准靶图8组,使用同一个黑场的测量结果汇总:

设置是1px步进,开启网格检测。
可以看到结果很稳定,dxo screen dr的range仅0.02,pdr range是0.04
精度为0.01-0.05ev。
其中一组的snr图如下:

可以看到snr线有很多缺口,可能是没拍到,也可能是被递增过滤器过滤掉了。
使用高精度靶图可以极大提升采样连续性,从而进一步提升精度和稳定性。
高精度靶图精度:
高精度靶图拍摄2组,每组16张约60M roi,开启网格检测仅在平坦区域采样,结果如下:

可以看到图线平滑了许多。4项DR中,3项相同,另一项差异0.01ev,真正做到了输入信号和测量结果的双高精度。
这两组靶图用NefUtil也能扫描出接近上图且一致性完全一样的结果。以gif动图展现如下:

实拍图的其中之一的对比,构图和曝光都有区别:

需要明确一下,我经常声称精度达到0.01,但是无法保证每次对比测量,精度都能达到0.01
例如gfx100ii的结果:

PDR完全一致,别的几项稍有差异。
对dpreview样张的支持
从dpreview网站提供的exposure latitude对比页面下载的raw原图,由于提供了最高-6ev的欠曝深度,并且有不少平滑区域,感觉可以扫描出有效的数据,所以我也进行了许多尝试。
先说结论。我针对dpreview的样张直接开启穷举式扫描,可以扫出一亿以上的原始roi,过滤无效点以后,仍然可以形成比较规整且密集的snr点线,并且PDR与ptp网站发布的值相比,差异一般在0.05ev以内。展现出了相当强大的抗干扰能力和准确性。
以下是对比数据汇总:

对比与网站发布值的差异,可以看到我的扫描结果明显更接近。NefUtil的在面对dpreview这种细节杂乱的非标准图,扫描结果略差。
另外a7m5和r63,NefUtil扫出了比网站发布值更高的结果。结合其余机型的差异,不难推断,网站数据贡献者的操作可能有失误,导致正式公布的结果比从dpreview样张扫描出来的还低。
还是以z7为例,我的扫描图表如下:

这是NefUtil的扫描结果,可以看到有效采样点的数量和图线平滑度的明显区别:

由此可以看出,NefUtil对靶图拍摄质量高度敏感,我的超级脚本则可以在恶劣条件下正常输出高精度结果。
与权威数据互验
这里我不再与dxo或ptp进行数据互验。我认为在本文双高精度方案驱动下获得的结果,才是真实基准。
首先dxo使用的灰阶目标由一系列环形排列的ND镜构成,肯定是拍摄1张时每个patch取样1次,可以多次平均,不利于全图扫描测量。获得的结果也可能会比扫描结果明显低。由于之前的复刻文章里,我手动采样1次的结果与dxo结果十分接近,这里的扫描结果必然明显高于dxo的结果。
z7就是一个很好的例子。screen dr这一项,dxo给出13.29,NefUtil是13.37,我的结果是13.48
我的13.48比NefUtil的13.37明显更高的原因,是我从独立黑场raw中心测得的实际BL,低于NefUtil的取值。
如果使用标准值1008(测量值约1007.65),则结果变成13.39。
BL的选取没有固定的标准,我认为以实测为准更好。
bcalff对dxo的数据的研究,虽然非常深入,但是也不是完全没有瑕疵。
根据ptp网站上DxOMark Gain Analysis的说法:
the curve provided appears to be a quadratic fit to the original; but the original data is not provided
bclaff认为dxo提供的full snr数据可能和实测数据有偏差,不是完全准确。
我对此做了一些小小研究。
首先简单设计一个验证实验:

如上图,左图的黑点是dxo的snr-ev实测点,添加了二阶趋势线蓝线并显示公式。根据ai的说法,quadratic fit就是excel里的二阶多项式趋势线。可以看到趋势线并不是完美穿过所有黑点中心的。而用左图公式造一些0ev到-14ev的snr-ev数据,画出右图散点图,再添加二阶趋势线,可以发现,趋势线完美穿过每个造出来的数据点的圆心。所以这个实验可以证实bcalff的说法是错误的,dxo给出的数据可能经过了平滑处理,但是不是quadratic fit。
另一个实验:下图有3条线,都是z7的snr线,分别是:a)ptp PTC页面三参数建模理论snr线(黑色),dxo实测snr连线(蓝色),以及使用ptp网站的DxOMark Derived Data建模的snr线(红色点线):

如果微调参数,可以让-3ev以下的红色点线,基本和蓝线贴合,说明dxo的图线是符合噪声建模的,并不是二次拟合。所以在两个不同证据的实证下,bclaff的quadratic fit的说法是完全错误的。
并且,由于红色点线和蓝色线分离,他从dxo给的数据里反推出来的参数,重新建模,得到的snr比dxo的实测结果偏低,看起来反推的计算过程也是不对的。
另外一方面,基于我的完善的测量方法论框架,我认为ptp提供的pdr结果,在拍摄条件不是最佳,以及采样密度不够的情况下,可以在一些机身的测量中获得较稳定可靠的结果,在另一些机身上会获得明显偏低的结果。例如对于佳能r63,使用NefUtil,从dpreview样张扫描出来的pdr是11.66,网站发布值仅为11.61。由于上文“NefUtil行为总结”小节证明了“NefUtil扫描高精度靶图的结果看起来非常好,但是扫描dpreview样张则准确度和平滑度都比较差(差异-0.10ev)”,由此我可以明确的认为,网站发布的r63的结果,由于操作者操作水平不够,实拍流程有瑕疵,造成了结果偏低。
实际上他本人也有这么说过:
In my experience at PhotonsToPhotos measurement and sample variation is seldom an issue.
Honestly, the most serious issue I have is that sometimes raw files collected on my behalf aren't properly taken to my protocol.
权威扫描脚本分享
由于超级脚本花费了很多时间和精力,虽然很硬核,但是结果其实也不那么权威,就不公布了。
感兴趣的读者可以把我这篇文章丢给AI,然后让它进行实现。整个脚本也不长,也就2000行。
它现在唯一的独特之处就是可以对dpreview的样张进行穷举扫描,获得与ptp发布值十分接近的pdr。
在发布了新机,dpreview发布了样张,而ptp又没有发布数据的时候,它提供一个全球独家的理论上接近权威数据的数据,为我的赛博斗蛐蛐赢得一点先机。
但是分享一个效果更权威的脚本:
通过网盘分享的文件:20260417 ptp NefUtil helper
链接: https://pan.baidu.com/s/1Qn6LsE-GPo-s8MaaldaW-A?pwd=1111 提取码: 1111
它能调用NefUtil的+cptc模式来输出ptp PDR和DXO Screen DR。唯一的缺点是扫描dpreview的结果略低(可能-0.1)。但是稍微放宽标准,例如差异1/3ev以内视为相同,也是可以进行不同机型横向对比的。
使用方法很简单:将文件夹拖拽到py脚本上运行,稍微等待即可自动弹出snr图表。
所以如果用于实机测试,只要按照ptp的sop来拍摄,就能获得权威的pdr数据并附赠snr图线。
注意一次只能测一档ISO。我的所有工作,都只对最低base ISO下的dr进行测定。
附赠2个tif格式的靶图,一个5ev拍3张,另一个1ev拍15-18张
当然了,作为免费提供的版本,功能会受到限制。脚本提供dxo screen dr和ptp pdr这2项数据的显示功能,对应100%查看和归一化查看。print dr可以自己计算:
HQDR想看的话自己画线就行。也可以自己改造一下代码。
根据上文的对比,如果扫描高精度靶图,也可以获得超越官方的精度和一致性,和超级脚本并没有太大区别,而且更具权威性。
结论
当摄影师说“这台相机信噪比高”时,他们脑子里想的是:“暗部细节保留得好”、“画面干净”、“纹理清晰”,他们眼中的 SNR = 可见的细节质量。Arri的白皮书也是如此演示。
然而,现行的 SNR 测量方案,测的是“均匀输入信号的纯度”,而不是“复杂场景的细节保留能力”。无论是 DXO、PTP 还是 ISO 标准,测量的 SNR 都是基于平坦区域的统计。
如果信噪比的计算必须基于平坦区域的DN的统计,取0.01dB和0.01ev作为实用工程精度,则本文提供的方案,已经触及测量精度的工程天花板。详细证明参考这2篇:
以730亿roi扫描,探寻CMOS动态范围测量的工程极限尼康z7的动态范围-极限测量dr的测量精度和稳定性与靶图拍摄质量有关。如果使用高精度靶图,NefUtil也能获得稳定精确的结果。