【问题标题】:JPEG images have different pixel values across multiple devicesJPEG 图像在多个设备上具有不同的像素值
【发布时间】:2014-05-09 13:29:06
【问题描述】:

我注意到,在跨设备读取 JPEG 格式的相同照片时,像素值不匹配。它们很接近,但又不同。转换为 PNG 文件时,像素值似乎匹配。

这似乎是由于跨设备的(非)压缩算法造成的。无论如何,这就是我想到的。有没有办法读取 JPEG 文件,以便跨设备从照片中检索相同的像素?我在 BitmapFactory 选项组件中看不到选项。

当前在跨设备处理图像的像素值时应用以下内容来保持大小:

Options options = new Options();
options.inScaled = false;
options.inPreferQualityOverSpeed = true;

目前将像素与以下像素进行比较只是为了查看一些(接近匹配,但不相等):

int[] pixels = new int[bitmapF.getWidth() * bitmapF.getHeight()];
bitmapF.getPixels(pixels, 0, bitmapF.getWidth(), 0, 0, bitmapF.getWidth(), bitmapF.getHeight());
Log.d("pixel entries", "pixels = " + pixels[233] + " - " + pixels[4002] + " - " + pixels[11391]);

注意:如果读取同一个未压缩文件的 PNG 版本,则值与预期相同。

例如,三星 Galaxy S4 和三星 Galaxy S5 甚至在 assets 文件夹中存储了来自相同 jpeg(运行相同测试活动)的不同像素。

pixel[233] 例如在 s5 上为 -5205635,但在 s4 上为 -5336451。像素[4002] 也有点偏离。但是在这张 jpeg 图片上,两个设备的像素 [11391] 是相等的。

【问题讨论】:

    标签: android jpeg


    【解决方案1】:

    JPEG 标准不要求解码器实现产生逐位相同的输出图像。不幸的是,指定解码器要求的标准文档ISO 10918-2 显然不是在线免费提供的,而是Wikipedia says

    ...JPEG 标准(和类似的 MPEG 标准)包括一些解码精度要求,包括解码过程的所有部分(可变长度解码、逆 DCT、去量化、输出重新归一化);参考算法的输出不得超过:

    • 每个像素分量最多相差 1 位
    • 每个 8×8 像素块的均方误差低
    • [等]

    使用相同输入的不同解码器输出之间的差异通常是由于内部精度水平不同,尤其是在执行 IDCT 时。另一个可能的差异来源是平滑,它试图减少“块状”伪影。

    和你一样,我希望设置 inPreferQualityOverSpeed 会产生相同的输出,但实际上并不能保证这一点。我至少可以想到几种方法可以让您在两部不同的手机上获得细微的变化:

    1. 手机可能运行不同版本的 Android,其中 BitmapFactory 的实现发生了变化(例如,inPreferQualityOverSpeed 可能被破坏然后修复,反之亦然),或者
    2. 手机可能会提供BitmapFactory 利用的不同硬件功能(例如矢量指令集、DSP 协处理器等)。即使是标量浮点单元的差异也可能导致差异,尤其是在生成实际机器指令的 JIT 编译时。

    鉴于标准中的回旋余地以及您的实验观察,看来保证位对位一致的唯一方法是在您自己的应用程序中执行解码。也许您可以找到一些替代的 Android 兼容库。

    【讨论】:

    • 这为我提供了许多我可以跟进的潜在解决方案,我感谢您的宝贵反馈。我认为如果四舍五入对我来说可能是一个问题,我将不得不按照您的建议查看其他库进行解码,看看是否可以为我产生相同的结果。
    • 在相同的操作系统上,JPEG 仍然会产生不同的结果,因此虽然这也是一个很好的建议,但它可能会在您的回复中作为某种内部硬件或库本身被提及,并且它与硬件交互的方式。
    • @JaySnayder - 当你说相同的操作系统时,你是指操作系统和设备吗?您是否尝试过多次在同一设备上解压缩 JPEG?那么这些值是否相同? 8x8 块可以由多个线程并行完成,所以我想知道处理顺序以及抗锯齿算法是否解释了这些差异。如果在同一个图像/操作系统/设备上运行的多个测试显示差异或没有差异,它可以帮助阐明正在发生的事情。
    • @Les 不幸的是,我没有完全相同的实验设备。所以,我只是说在同一个操作系统上。这只是参考它是否是特定于操作系统的。但是,是的,如果在同一设备上执行多次,相同的 JPEG 会解压缩相同的内容。
    • 最大 1 位像素差异:我有 actual_npy[x, y, :] , expected[x, y, :] (array([176, 176, 168], dtype=uint8), array([176, 175, 170], dtype=uint8)) 用于通过 PIL 加载不同 libjpeg 版本的相同图像
    【解决方案2】:

    我想您还应该检查压缩的 PNG 在不同设备上的显示方式是否相同。

    http://pngquant.org/

    如果答案是肯定的,那么剩下的唯一事情就是弄清楚如何在手机上以编程方式将这些图像转换为相同类型的压缩 png。

    【讨论】:

    • 我确实使用 pngquant 的 GUI 版本做了一个快速测试。未压缩的 png 产生的值与预期的压缩 png 不同,但压缩的 png 在所有设备上产生相同的值,这与 jpeg 不同。
    【解决方案3】:

    JPEG 解码器的大部分工作都涉及实数计算。通常这是使用定点整数算法来完成的,以提高性能。这引入了舍入误差。细微的变化是使用 JPEG 的自然组成部分。

    【讨论】:

    • 为什么整数运算会因具有相同内部图片的设备而异?在解码相同的 JPEG 图像并收到相同的舍入误差时,操作系统不会使用相同的计算吗?
    • 假设解码器使用缩放整数进行 DCT 计算。对于相同的计算,64 位系统可以提供比 32 位系统更高的精度。解码器可能使用浮点计算,给出不同的舍入误差。解码器可以使用不同的操作顺序来给出不同的舍入。有多种方法可以优化 DCT/IDCT 计算。如果您希望像素值相同,无论硬件如何,都可以在一台机器上转换 PNG,然后在不同的硬件设备上解码为 PNG。
    • 这些图片仅用于实验。实际图像将是使用 Android 设备的内置相机拍摄的照片。图像不会在设备之间传递。截至目前,Android 操作系统仅处理 32 位操作系统,但它没有理由不支持 64。因此,根据您提到的第一个计数,这在不久的将来也可能是一个有效的点。
    【解决方案4】:

    是的,不同设备的像素颜色值不同。这很烦人,特别是如果您想比较颜色。解决方案是比较视觉上相同的颜色(通过人类感知)。

    通过人类感知比较两种颜色的最佳方法之一是 CIE76。这种差异称为 Delta-E。小于1时,人眼无法识别。

    您可以找到精彩的颜色实用程序类(ColorUtils),其中包括 CIE76 比较方法。由苏黎世大学的 Daniel Strebel 撰写。

    从 ColorUtils.class 我使用方法:

    static double colorDifference(int r1, int g1, int b1, int r2, int g2, int b2)
    

    r1,g1,b1 - 第一种颜色的 RGB 值

    r2,g2,b2 - 您要比较的第二种颜色的 RGB 值

    如果你使用 Android,你可以像这样得到这些值:

    r1 = Color.red(pixel);

    g1 = Color.green(pixel);

    b1 = Color.blue(pixel);

    【讨论】:

      【解决方案5】:

      将媒体重新调整为所需大小或使用 HTML 属性禁用图像缩放。

      另一种选择是允许用户在加载缩略图表示后做出决定以节省带宽。

      【讨论】:

        【解决方案6】:

        我遇到了同样的问题。 PNG 和 JPEG 图像似乎是由不同设备以近似值呈现的。 我们使用 BMP 图像解决了这个问题(不幸的是,这些图像的尺寸要大得多)。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-09-19
          • 2011-04-29
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多