从JPEG图像中提取亮度的正确方法是什么(伽玛等)?

时间:2012-01-20 08:58:10

标签: c++ image image-processing camera exif

简而言之:我无法从JPEG照片的RGB值中提取有意义的光强度,并且试图考虑伽马校正或sRGB只会使事情变得更糟。

我正在做一个玩具项目,涉及处理一堆用间隔计拍摄的照片图像。基本上,我想用它们做一个时间流逝,并进行一些修正,以便剪辑更整洁。我使用佳能dSLR。

我需要一个函数,在给定JPEG文件的情况下,它会计算出“平均场景亮度”。结果应该是一个简单的数字;不需要在任何绝对光度单位中表达,我只做相对比较。因此,例如,你拍摄一些房间的照片,然后函数返回“5.0”。然后在灯光下添加第二个灯泡,与第一个灯泡完全相同,放在旁边,再次拍摄。该功能现在应该为您提供“10.0”。

因此,我目前对此功能的实现结合了以下几点:ISO感光度,快门速度,光圈(从EXIF中提取)和平均图像亮度。 Exif的东西显然更重要,因为在自动模式下,相机会尝试使用这样的设置,因此图像亮度会出现在灰色中点附近。 然而,ISO /快门/光圈设置的分辨率均为1/3或更低,因此检测图像亮度对于“微调”非常重要。

当我这样做的时候,我得到了一些明显的虚假结果,而且我挖的越多,我就越困惑。所以最后我设置了一个“几乎认真”的实验:

测试设置: 房间里有一个简单的墙壁,用白炽灯点亮,照明非常均匀。 使用两个相机来比较结果:5D与50mm素数,350D与35mm素数。 到墙的距离:约3米。 所有照片均以1/10秒的快门速度拍摄。 相机设置:手动,“忠实模式”(无增强功能,无饱和度或对比度凹凸),Tungsten WB,无自定义功能,JPEG-Fine,sRGB色彩空间。镜头没有过滤器。照明不会改变,我只改变ISO和光圈设置。 以下是我得到的结果:

     Avg   Spd   ISO  Aperture
1. 0.3507, 0.10, 100, f/2.8
2. 0.5382, 0.10, 200, f/2.8
3. 0.3557, 0.10, 200, f/4.0
4. 0.2709, 0.10, 200, f/5.0
5. 0.2118, 0.10, 200, f/5.6
6. 0.1718, 0.10, 200, f/6.3
7. 0.1459, 0.10, 200, f/7.1
8. 0.1112, 0.10, 200, f/8.0
9. 0.0883, 0.10, 200, f/9.0

第一列是平均像素值(直接来自JPEG),在整个图像上取平均值,转换为灰度为(R + G + B)/ 3。通过将[0..255]范围除以255,将颜色在[0..1]范围内标准化。 因此,在1)和2)之间,我只改变ISO设置,图像应该变为亮度的两倍,但平均像素值仅上升53%(没有任何过度曝光的区域)。

2..3:光圈一个停止,所以图像应该变亮一半,所以1)和3)同意​​(额外的亮度可能是由于渐晕减少)

3..5:再次,一次停止,5)应该是3)的亮度的一半

5..8:同样,应该是一半(这基本上没问题)。

这一切都非常非常奇怪。顺便说一句,两台相机之间的结果是一致的,这表明这不仅仅是特定型号的特殊性。

这不会应用任何伽马校正。 JPEG读取代码使用C ++,基本上遵循IJG示例代码(djpeg实用程序)。现在,JPEG保存了伽马校正值,因此像素值应被视为sRGB颜色空间中的值(获取源像素,转换为[0..1],并应用sRGB->linear RGB transform。让我们试试:

     Avg   Spd   ISO  Aperture
1. 0.1140, 0.10, 100, f/2.8
2. 0.2746, 0.10, 200, f/2.8
3. 0.1175, 0.10, 200, f/4.0
4. 0.0682, 0.10, 200, f/5.0
5. 0.0424, 0.10, 200, f/5.6
6. 0.0287, 0.10, 200, f/6.3
7. 0.0213, 0.10, 200, f/7.1
8. 0.0133, 0.10, 200, f/8.0
9. 0.0092, 0.10, 200, f/9.0

我也尝试了“普通”伽马校正(gamma = 2.2),结果与sRGB校正案非常相似。

所以我非常,非常困惑。 有人可以解释相机JPEG的RGB强度如何真正被解释为,因为我没有想法:)

1 个答案:

答案 0 :(得分:8)

所以,当我继续阅读时,这个谜团慢慢展开。

虽然相机的传感器理论上能够测量线性光强度,但它显然不会这样做,而是模仿摄影胶片的行为,这有着众所周知的非线性响应(例如,见this ,图3)。 因此,dSLR的响应曲线远不是线性的,但更像是这样:

exposure response on a digital camera

因此,如果没有精确校准,从像素值中获取绝对场景亮度是不切实际的。

然而,我只想对照片进行亮度调整,并且对我来说有一个近似正确的亮度估计就可以了,所以我继续重建相机的传输功能(佳能350D):

log-linear response curve of Canon 350D's sensor

白色数据点对应于各种光圈值下的不同曝光(f / 22,f / 20,f / 18,f / 16等,以1/3级为增量)。正如上图所示,X轴在输入亮度方面是对数的,而Y轴在像素值方面是线性的(在伽马校正之后)。假设图形在单位平方中,我还通过五阶多项式计算了近似拟合曲线:

(((((- 6.76219 * x) + 12.0459) * x - 5.8683) * x + 1.72338) * x - 0.148753) * x + 0.0105364;

表示[0.05,1]

中的x

所以,如果你有原始(“真实”)亮度,获得像素值会像这样:

  1. 获取输入亮度的对数并对其进行线性变换,以便在[0..1]间隔内有7次曝光停止,
  2. 应用上面的多项式(虽然需要特殊处理[0..0.05])。
  3. 应用sRGB压缩。
  4. 将此转换定义为 T ,我的应用程序中的整个工作流现在都是这样的:

    1. 使用 T -1
    2. 处理输入的JPEG图像
    3. 结果值以浮点格式保存,并视为线性RGB值
    4. 在将结果保存为JPEG之前应用 T