为 JPEG 确定颜色 space

Determining color space for JPEG

我正在编写用于确定 JPEG 图像颜色的代码 space。我找到了两个可以帮助我实现这一点的参考资料。一个在 oracle.com, the other one is a C source code from the ijg.com 上,其中 "is responsible for the reference implementation of the original JPEG standard"。

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

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

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

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

我也很难理解您提到的 Oracle 文档。

根据我的经验,从编写 JPEG plugin for Java ImageIO 开始,正确的做法是遵循 IJG 实现。这就是大多数软件所做的,因此它会在您的用户中造成最少的混淆(即 "Why does my image look different in your software and software X?")。在许多情况下,Sun/Oracle 算法与 "rest of the world" 不一致。

我最终实现了一个稍微不同的算法,它考虑了 "extra" 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");
}

在对旧的 JPEG 标准发表意见后,我终于找到了答案。

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

  • Images encoded with only one component are assumed to be grayscale data in which 0 is black and 255 is white.

  • Images encoded with three components are assumed to be RGB data encoded as YCbCr unless the image contains an APP14 marker segment as specified in 6.5.3, in which case the colour encoding is considered either RGB or YCbCr according to the application data of the APP14 marker segment. The relationship between RGB and YCbCr is defined as specified in Rec. ITU-T T.871 | ISO/IEC 10918-5.

  • Images encoded with four components are assumed to be CMYK, with (0,0,0,0) indicating white unless the image contains an APP14 marker segment as specified in 6.5.3, in which case the colour encoding is considered either CMYK or YCCK according to the application data of the APP14 marker segment. The relationship between CMYK and YCCK is defined as specified in clause 7.

并且 APP14 标志是“Adobe[=10=]”,AP12 具有转换标志:

Transform flag values of 0, 1 and 2 shall be supported and are interpreted as follows:

0 – CMYK for images that are encoded with four components in which all four CMYK values are complemented; RGB for images that are encoded with three components; i.e., the APP14 marker does not specify a transform applied to the image data.

1 – An image encoded with three components using YCbCr colour encoding.

2 – An image encoded with four components using YCCK colour encoding.

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