确定JPEG的颜色空间

12

我正在编写一个用于确定JPEG图像颜色空间的代码。我发现两个参考文献可能有助于我实现这一点。一个在oracle.com上,另一个是来自ijg.com的C源代码,该网站“负责原始JPEG标准的参考实现”。

然而它们确实不同。例如,在IJG中,如果没有Adobe标记并且有4个通道,则假定为CMYK,但在oracle中则为YCCA。另外,IJG的实现不考虑子采样,而在oracle规格中,对于4通道子采样,其颜色空间为YCCK等等。

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

还有一点是,我发现JPEG不支持alpha通道中的透明度,这里为什么会有oracle谈论有关JPEG元数据规范的YCCA和RGBA的内容呢?

当使用IJG的逻辑检查图像时,结果告诉我它是CMYK格式(在ubuntu上使用ImageMagick检查图像时也显示为CMYK),但使用Oracle的逻辑却显示为YCCA格式。应该相信谁?为什么Oracle不依赖于原始的JPEG规范?还是有其他我不知道的原因?


你可能想查看Exif(可交换图像文件格式)的规范。如果你会读Perl,那么最常用的工具是Phil Harvey的ExifTool - AlexP
ExifTool被广泛认为是黄金标准,我提供的参考资料是后中世纪数字相机所产生的JPEG文件格式的实际官方定义。 - AlexP
1
从JPEG规范中可以看出:“应用程序相关信息,例如颜色空间,不在本规范的范围内。” - 注意:该标准是在sRGB之前指定的,并且在视频颜色空间发生变化(从Rec.601到Rec.709)期间。 - Giacomo Catenazzi
JPEG颜色模型中颜色的含义取决于提供该JPEG的传输元数据,无论是通过文件、UVC还是其他方式。由于问题中未提及传输方式,因此答案不确定。 - G Huxley
Oracle不是JPEG标准的规范来源,ISO/ITU才是。IJG独立于标准之外,在某个时候,IJG拥有一个被广泛接受的无参考库,直到ITU因为一些专有扩展而要求他们放弃。尽管如此,请不要指望从Oracle那里得到任何理由或者他们做事情的原因,哈哈。如果您想要规范,请参考ITU或ISO。仅供记录:在官方方式下,JPEG不支持A alpha透明通道(如果我没记错,IJG在8版本中添加了支持,但很少有软件支持带有alpha的jpeg)。 - Myndex
显示剩余2条评论
2个回答

15
在我对旧的JPEG标准发表评论后,我终于找到了答案。在ISO/IEC 10918-6:2013 (E)的第6.1节中:
- 只使用一个分量编码的图像被认为是灰度数据,其中0表示黑色,255表示白色。 - 使用三个分量编码的图像被认为是以YCbCr编码的RGB数据,除非图像包含如6.5.3所指定的APP14标记段,此时颜色编码根据APP14标记段的应用数据被视为RGB或YCbCr。RGB和YCbCr之间的关系如Rec. ITU-T T.871 | ISO/IEC 10918-5中所指定的那样。 - 使用四个分量编码的图像被认为是CMYK格式,其中(0,0,0,0)表示白色,除非图像包含如6.5.3所指定的APP14标记段,此时颜色编码根据APP14标记段的应用数据被视为CMYK或YCCK。CMYK和YCCK之间的关系如第7条中所指定的那样。
如果APP14标记是“Adobe\0”,则AP12具有变换标志。
支持转换标志值0、1和2,解释如下: 0-对于使用四个组件编码的图像,其中所有四个CMYK值都被补充,应支持CMYK;对于使用三个组件编码的图像,即APP14标记未指定应用于图像数据的变换,应支持RGB。 1-使用YCbCr颜色编码的三个组件编码的图像。 2-使用YCCK颜色编码的四个组件编码的图像。
因此,这取决于:应该是CMYK,但如果APP14和AP12具有正确的值,则可能是YCCK。

以上似乎完全符合IJG实现(或反之亦然),唯一的例外是它没有提到JFIF标记的存在(尽管这不会影响结果,因为如果存在,JFIF必须具有组件ID 1、2、3和Adobe YCC)。我以前从未阅读过这个文档,所以我喜欢这个答案。 :-) - Harald K
同样的规范对你的回答说:“在没有其他信息或元数据的情况下,例如文件格式、容器或其他打印系统机制,指定图像的颜色或灰度值的解释。”由于提问者没有提及jpeg是如何传输的,因此他们的回答是含糊不清的。 - G Huxley

3
我一直很难理解你所提到的Oracle文档。从我编写Java ImageIO的JPEG插件的经验来看,正确的做法是遵循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");
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接