【问题标题】:Determining color space for JPEG确定 JPEG 的色彩空间
【发布时间】:2018-11-20 17:17:59
【问题描述】:

我正在编写用于确定 JPEG 图像色彩空间的代码。我找到了两个可以帮助我实现这一点的参考资料。一个在oracle.com上,另一个是来自ijg.com的C源代码,“负责原始JPEG标准的参考实现”。

但是它们确实不同。例如。在 IJG 中,当没有 Adob​​e 标记且有 4 个通道时,假定为 CMYK,但在 oracle 中为 YCCA。此外,IJG 的实现并不关注二次采样,而对于 4 通道二次采样,它是 oracle 规范中的 YCCK,等等。

ColorSpace 类也有很多缺失,当我实现 oracle 的逻辑时,我需要指定 3 个额外的颜色空间,如 YCCK、YCCA、RGBA。

还有一点是我发现JPEG不支持alpha通道透明度here的信息,为什么oracle会在JPEG元数据规范的上下文中谈论YCCA和RGBA?

结果当用 IJG 的逻辑检查图像时,它告诉我它是 CMYK(在 ubuntu 上用 ImageMagick 检查图像,它还说它是 CMYK),用 oracle 的逻辑它是 YCCA。相信谁?为什么 oracle 不依赖原始的 JPEG 规范?或者还有什么我不知道的?

【问题讨论】:

  • 您可能想查看Exifspecification(可交换图像文件格式)。如果您可以阅读 Perl,那么规范实用程序是 Phil Harvey 的 ExifTool
  • 好的,但它仍然没有回答为什么它们不同(oracle 和 ijg)
  • ExifTool 被广泛认为是黄金标准,我提供的参考是中世纪后数码相机生成的 JPEG 文件格式的实际官方定义。
  • 来自 JPEG 规范:“与应用程序相关的信息,例如色彩空间,不在本规范的范围内。” - 注意:标准是在 sRGB 之前指定的,在视频色彩空间变化期间(从 Rec.601 到 Rec.709)。
  • 2012 版 (ISO/IEC 10918-5:2012) 指定 YCC Rec601(或仅 Y)作为基础,并且只有 1 或 3 个通道(可互换)。但建议使用 ICC 配置文件。所以我认为或者有一个ICC指定了4个通道的含义,或者程序应该只是猜测。而且由于 4 通道 JPEG 不那么频繁,似乎没人关心

标签: image jpeg color-space


【解决方案1】:

我也很难理解您所指的 Oracle 文档。

根据我的经验,从写JPEG plugin for Java ImageIO 开始,正确的做法是遵循 IJG 实施。这就是大多数软件所做的,因此它将在您的用户中产生最少的混淆(即“为什么我的图像在您的软件和软件 X 中看起来不同?”)。 Sun/Oracle 算法在许多情况下与“世界其他地方”不一致。

我最终实现了一个稍微不同的算法,它考虑了“额外的”Java 颜色空间,但在其他方面与 IJG 实现非常接近:

// Adapted from libjpeg jdapimin.c:
// Guess the input colorspace
// (Wish JPEG committee had provided a real way to specify this...)
switch (startOfFrame.componentsInFrame()) {
    case 1:
        return JPEGColorSpace.Gray;
    case 2:
        return JPEGColorSpace.GrayA; // Java special case: Gray + Alpha
    case 3:
        if (jfif != null) {
            return JPEGColorSpace.YCbCr; // JFIF implies YCbCr
        }
        else if (adobeDCT != null) {
            switch (adobeDCT.transform) {
                case AdobeDCT.Unknown:
                    return JPEGColorSpace.RGB;
                case AdobeDCT.YCC:
                    return JPEGColorSpace.YCbCr;
                default:
                    // TODO: Warning!
                    return JPEGColorSpace.YCbCr; // assume it's YCbCr
            }
        }
        else {
            // Saw no special markers, try to guess from the component IDs
            int cid0 = startOfFrame.components[0].id;
            int cid1 = startOfFrame.components[1].id;
            int cid2 = startOfFrame.components[2].id;

            if (cid0 == 1 && cid1 == 2 && cid2 == 3) {
                return JPEGColorSpace.YCbCr; // assume JFIF w/out marker
            }
            else if (cid0 == 'R' && cid1 == 'G' && cid2 == 'B') {
                return JPEGColorSpace.RGB; // ASCII 'R', 'G', 'B'
            }
            else if (cid0 == 'Y' && cid1 == 'C' && cid2 == 'c') {
                return JPEGColorSpace.PhotoYCC; // Java special case: YCc
            }
            else {
                // TODO: Warning!
                return JPEGColorSpace.YCbCr; // assume it's YCbCr
            }
        }

    case 4:
        if (adobeDCT != null) {
            switch (adobeDCT.transform) {
                case AdobeDCT.Unknown:
                    return JPEGColorSpace.CMYK;
                case AdobeDCT.YCCK:
                    return JPEGColorSpace.YCCK;
                default:
                    // TODO: Warning!
                    return JPEGColorSpace.YCCK; // assume it's YCCK
            }
        }
        else {
            // Saw no special markers, try to guess from the component IDs
            int cid0 = startOfFrame.components[0].id;
            int cid1 = startOfFrame.components[1].id;
            int cid2 = startOfFrame.components[2].id;
            int cid3 = startOfFrame.components[3].id;

            if (cid0 == 1 && cid1 == 2 && cid2 == 3 && cid3 == 4) {
                return JPEGColorSpace.YCbCrA; // Java special case: YCbCrA
            }
            else if (cid0 == 'R' && cid1 == 'G' && cid2 == 'B' && cid3 == 'A') {
                return JPEGColorSpace.RGBA; // Java special case: RGBA
            }
            else if (cid0 == 'Y' && cid1 == 'C' && cid2 == 'c' && cid3 == 'A') {
                return JPEGColorSpace.PhotoYCCA; // Java special case: YCcA
            }
            else {
                // TODO: Warning!
                // No special markers, assume straight CMYK.
                return JPEGColorSpace.CMYK;
            }
        }

    default:
        throw new IIOException("Cannot determine source color space");
}

【讨论】:

    【解决方案2】:

    在我对旧 JPEG 标准的 cmet 之后,我终于找到了答案。

    ISO/IEC 10918-6:2013 (E),第 6.1 节:

    • 仅用一个分量编码的图像被假定为灰度数据,其中 0 为黑色,255 为白色。

    • 用三个分量编码的图像被假定为编码为 YCbCr 的 RGB 数据,除非图像包含 6.5.3 中指定的 APP14 标记段,在这种情况下,根据应用程序数据,颜色编码被认为是 RGB 或 YCbCr APP14 标记段。 RGB 和 YCbCr 之间的关系在 Rec. 中定义。 ITU-T T.871 | ISO/IEC 10918-5。

    • 用四个分量编码的图像被假定为 CMYK,其中 (0,0,0,0) 表示白色,除非图像包含 6.5.3 中指定的 APP14 标记段,其中根据APP14标记段的应用数据,颜色编码被认为是CMYKYCCKCMYKYCCK 之间的关系定义见第 7 条。

    并且 APP14 标志是“Adobe\0”,AP12 具有转换标志:

    应支持0、1和2的变换标志值,解释如下:

    0 – CMYK 表示用四个分量编码的图像,其中所有四个 CMYK 值都是互补的; RGB 用于用三个分量编码的图像;即,APP14 标记未指定应用于图像数据的变换。

    1 – 使用 YCbCr 颜色编码由三个分量编码的图像。

    2 – 使用 YCCK 颜色编码由四个分量编码的图像。

    所以,这取决于:它应该是 CMYK,但如果 APP14 和 AP12 具有正确的值,它可能是 YCCK。

    【讨论】:

    • 以上似乎完全符合 IJG 实现(或相反),除了它没有提到 JFIF 标记的存在(它不影响结果,因为 JFIF 必须具有组件 ID 1、2、3 和 Adob​​e YCC(如果存在)。我以前从未读过这个文件,所以我喜欢这个答案。 :-)
    • 同一规范说明了您的答案:“在没有其他信息或元数据的情况下,例如文件格式、容器或其他打印系统机制,指定了颜色或灰度值的解释图片”。由于提问者没有提到jpeg是如何传输的,所以答案是模棱两可的。
    猜你喜欢
    • 1970-01-01
    • 2012-11-09
    • 2019-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-20
    • 1970-01-01
    • 2018-08-19
    相关资源
    最近更新 更多