无法使用 ImageIO.read(文件文件)读取 JPEG 图像

Posted

技术标签:

【中文标题】无法使用 ImageIO.read(文件文件)读取 JPEG 图像【英文标题】:Unable to read JPEG image using ImageIO.read(File file) 【发布时间】:2011-01-25 09:38:54 【问题描述】:

我在使用 ImageIO.read(File file) 读取这个 JPEG 文件时遇到问题 - 它会引发异常并显示“不支持的图像类型”消息。

我尝试过其他 JPEG 图像,它们似乎工作正常。

我能够发现的唯一区别是该文件似乎包含一个缩略图 - 是否已知会导致 ImageIO.read() 出现问题?

编辑:

添加了结果图像:

【问题讨论】:

从异常中查看堆栈跟踪会很有用。 请恢复图片! 【参考方案1】:

旧帖,但供将来参考:

受此问题和此处找到的链接的启发,我为 ImageIO 编写了一个 JPEGImageReader 插件,该插件支持 CMYK 颜色模型(均具有原始颜色模型,或在读取时隐式转换为 RGB)。与此处提到的其他解决方案相比,阅读器还使用嵌入在 JPEG 流中的 ICC 配置文件进行适当的颜色转换。

它是纯 Java,不需要 JAI。源代码和二进制分发版在github.com/haraldk/TwelveMonkeys 上免费提供,并且受 BSD 风格的许可保护。

安装后,它允许您使用 ImageIO.read(...) 读取 CMYK JPEG,如下所示:

File cmykJPEGFile = new File(/*path*/);
BufferedImage image = ImageIO.read(cmykJPEGFile);

即:在大多数情况下,没有必要修改您的代码。

【讨论】:

谢谢!我想要这样的东西。你有自述文件/文档吗? :) 还是我应该只检查测试?再次感谢 抱歉,目前文档很少。但是,它是一个 ImageIO 插件,所以如果您只想读取 CMYK JPEG,请执行以下操作:使用 Maven 构建 JAR,放置在类路径中,并且 ImageIO.read(cmykJPEGFile) 应该可以正常工作。随意问,如果有什么具体的事情你想做。 :-) 酷,不错的插件。 @anon58192932 ImageIO.read() 将使用第一个能够读取输入的ImageReader。注册 TwelveMonkeys 插件时,它会确保它始终是ordered 之前 com.sun...JPEGImageReader。因此,如果这些是您看到的唯一插件,则应该始终选择 TwelveMonkeys (并且应该始终是测试迭代器中的第一个)。 这个库运行良好,Unsupported Image Typejava.lang.IllegalArgumentException: Numbers of source Raster bands and source color space components do not match 异常停止出现。【参考方案2】:

您的图像“颜色模型”是 CMYK,JPEGImageReader(读取文件的内部类)仅读取 RGB 颜色模型。

如果你坚持阅读 CMYK 图像,那么你需要转换它们,试试这个代码。

更新

将 CMYK 图像读入 RGB BufferedImage。

    File f = new File("/path/imagefile.jpg");

    //Find a suitable ImageReader
    Iterator readers = ImageIO.getImageReadersByFormatName("JPEG");
    ImageReader reader = null;
    while(readers.hasNext()) 
        reader = (ImageReader)readers.next();
        if(reader.canReadRaster()) 
            break;
        
    

    //Stream the image file (the original CMYK image)
    ImageInputStream input =   ImageIO.createImageInputStream(f); 
    reader.setInput(input); 

    //Read the image raster
    Raster raster = reader.readRaster(0, null); 

    //Create a new RGB image
    BufferedImage bi = new BufferedImage(raster.getWidth(), raster.getHeight(), 
    BufferedImage.TYPE_4BYTE_ABGR); 

    //Fill the new image with the old raster
    bi.getRaster().setRect(raster);

更新 - 2015 年 3 月 - 添加模拟图像

原始图像已从 OP 的保管箱中删除。因此,我添加了新图像(不是原始图像)来模拟它们所发生的问题。

第一张图片是普通 RGB 图像的样子。

第二张图片是同一张图片在 CMYK 颜色模型中的样子。

您实际上无法在网络上看到它的外观,因为它会被主机转换为 RGB。要准确查看它的外观,请获取 RGB 图像并通过 RGB 到 CMYK 转换器运行它。

第三张图像是 CMYK 图像在使用 Java ImageIO 读取然后写入时的样子。

OP 发生的问题是它们有类似图像 2 的内容,当您尝试阅读它时会引发异常。

【讨论】:

好极了,我会试一试的。它也适用于 RGB 图像,还是我需要以某种方式检测类型? 你会发现很多方法来检测颜色模型,我的首选是使用 JPEGImageReader,如果它抛出Unsupported Image Type 异常,那么它很可能是 CMYK。 这很好用,但是颜色变得一团糟。请参阅我附加到问题的新图片。您对此有什么建议吗?谢谢! 是的,颜色渗色很常见。也许您应该尝试使用 Javas ColorConvertOp,但不确定它是否会有所帮助。如果那是您的兴趣,有很多方法可以从一种颜色模型转换为另一种颜色模型。我只给了最简单的一个。如果它是一次性图像,我会说只需将其保存为专业图像编辑软件中的 RGB 模型。 好的,问题是我无法控制用户将上传哪些图像。所以我想我需要更深入地研究颜色模型转换,以支持用户可能会扔给应用程序的任何 JPEG。非常感谢您的帮助。【参考方案3】:

我参加聚会有点晚了。但我发布我的答案可能仍然值得,因为没有一个答案能真正解决问题。

该解决方案需要 Sanselan(或现在称为 Apache Commons Imaging),并且需要合理的 CMYK 颜色配置文件(.icc 文件)。您可以从 Adob​​e 或从 eci.org 获得后者。

基本问题是 Java - 开箱即用 - 只能读取 RGB 格式的 JPEG 文件。如果您有 CMYK 文件,则需要区分常规 CMYK、Adobe CMYK(具有反转值,即 255 表示无墨水,0 表示最大墨水)和 Adob​​e CYYK(一些具有反转颜色的变体)。

public class JpegReader 

    public static final int COLOR_TYPE_RGB = 1;
    public static final int COLOR_TYPE_CMYK = 2;
    public static final int COLOR_TYPE_YCCK = 3;

    private int colorType = COLOR_TYPE_RGB;
    private boolean hasAdobeMarker = false;

    public BufferedImage readImage(File file) throws IOException, ImageReadException 
        colorType = COLOR_TYPE_RGB;
        hasAdobeMarker = false;

        ImageInputStream stream = ImageIO.createImageInputStream(file);
        Iterator<ImageReader> iter = ImageIO.getImageReaders(stream);
        while (iter.hasNext()) 
            ImageReader reader = iter.next();
            reader.setInput(stream);

            BufferedImage image;
            ICC_Profile profile = null;
            try 
                image = reader.read(0);
             catch (IIOException e) 
                colorType = COLOR_TYPE_CMYK;
                checkAdobeMarker(file);
                profile = Sanselan.getICCProfile(file);
                WritableRaster raster = (WritableRaster) reader.readRaster(0, null);
                if (colorType == COLOR_TYPE_YCCK)
                    convertYcckToCmyk(raster);
                if (hasAdobeMarker)
                    convertInvertedColors(raster);
                image = convertCmykToRgb(raster, profile);
            

            return image;
        

        return null;
    

    public void checkAdobeMarker(File file) throws IOException, ImageReadException 
        JpegImageParser parser = new JpegImageParser();
        ByteSource byteSource = new ByteSourceFile(file);
        @SuppressWarnings("rawtypes")
        ArrayList segments = parser.readSegments(byteSource, new int[]  0xffee , true);
        if (segments != null && segments.size() >= 1) 
            UnknownSegment app14Segment = (UnknownSegment) segments.get(0);
            byte[] data = app14Segment.bytes;
            if (data.length >= 12 && data[0] == 'A' && data[1] == 'd' && data[2] == 'o' && data[3] == 'b' && data[4] == 'e')
            
                hasAdobeMarker = true;
                int transform = app14Segment.bytes[11] & 0xff;
                if (transform == 2)
                    colorType = COLOR_TYPE_YCCK;
            
        
    

    public static void convertYcckToCmyk(WritableRaster raster) 
        int height = raster.getHeight();
        int width = raster.getWidth();
        int stride = width * 4;
        int[] pixelRow = new int[stride];
        for (int h = 0; h < height; h++) 
            raster.getPixels(0, h, width, 1, pixelRow);

            for (int x = 0; x < stride; x += 4) 
                int y = pixelRow[x];
                int cb = pixelRow[x + 1];
                int cr = pixelRow[x + 2];

                int c = (int) (y + 1.402 * cr - 178.956);
                int m = (int) (y - 0.34414 * cb - 0.71414 * cr + 135.95984);
                y = (int) (y + 1.772 * cb - 226.316);

                if (c < 0) c = 0; else if (c > 255) c = 255;
                if (m < 0) m = 0; else if (m > 255) m = 255;
                if (y < 0) y = 0; else if (y > 255) y = 255;

                pixelRow[x] = 255 - c;
                pixelRow[x + 1] = 255 - m;
                pixelRow[x + 2] = 255 - y;
            

            raster.setPixels(0, h, width, 1, pixelRow);
        
    

    public static void convertInvertedColors(WritableRaster raster) 
        int height = raster.getHeight();
        int width = raster.getWidth();
        int stride = width * 4;
        int[] pixelRow = new int[stride];
        for (int h = 0; h < height; h++) 
            raster.getPixels(0, h, width, 1, pixelRow);
            for (int x = 0; x < stride; x++)
                pixelRow[x] = 255 - pixelRow[x];
            raster.setPixels(0, h, width, 1, pixelRow);
        
    

    public static BufferedImage convertCmykToRgb(Raster cmykRaster, ICC_Profile cmykProfile) throws IOException 
        if (cmykProfile == null)
            cmykProfile = ICC_Profile.getInstance(JpegReader.class.getResourceAsStream("/ISOcoated_v2_300_eci.icc"));
        ICC_ColorSpace cmykCS = new ICC_ColorSpace(cmykProfile);
        BufferedImage rgbImage = new BufferedImage(cmykRaster.getWidth(), cmykRaster.getHeight(), BufferedImage.TYPE_INT_RGB);
        WritableRaster rgbRaster = rgbImage.getRaster();
        ColorSpace rgbCS = rgbImage.getColorModel().getColorSpace();
        ColorConvertOp cmykToRgb = new ColorConvertOp(cmykCS, rgbCS, null);
        cmykToRgb.filter(cmykRaster, rgbRaster);
        return rgbImage;
    

代码首先尝试使用适用于 RGB 文件的常规方法读取文件。如果失败,它会读取颜色模型的详细信息(配置文件、Adobe 标记、Adobe 变体)。然后它读取原始像素数据(光栅)并进行所有必要的转换(YCCK 到 CMYK、反转颜色、CMYK 到 RGB)。

我对我的解决方案不太满意。虽然颜色大多很好,但暗区有点太亮,特别是黑色不是全黑的。如果有人知道我可以改进什么,我会很高兴听到。

【讨论】:

这是我找到的最佳答案,但您不想在 finally 块中关闭 ImageInputStream 吗? 感谢代码 sn-p,工作正常。 (ISOcoat_v2_300_eci.icc 可以在这里找到:humburg.de/?page=4)【参考方案4】:

ImageIO.read() ->

File filePath = new File("C:\\Users\\chang\\Desktop\\05036877.jpg");
com.sun.image.codec.jpeg.JPEGImageDecoder jpegDecoder =  JPEGCodec.createJPEGDecoder (new FileInputStream(filePath));

BufferedImage image = jpegDecoder.decodeAsBufferedImage();

【讨论】:

来自 API 文档:Note that the classes in the com.sun.image.codec.jpeg package are not part of the core Java APIs. They are a part of Sun's JDK and JRE distributions. Although other licensees may choose to distribute these classes, developers cannot depend on their availability in non-Sun implementations. We expect that equivalent functionality will eventually be available in a core API or standard extension.【参考方案5】:

我也在这里找到了 https://***.com/questions/22409...,这个颜色转换效果很好

并结合两者得到这个:

private BufferedImage convertCMYK2RGB(BufferedImage image) throws IOException
    log.info("Converting a CYMK image to RGB");
    //Create a new RGB image
    BufferedImage rgbImage = new BufferedImage(image.getWidth(), image.getHeight(),
    BufferedImage.TYPE_3BYTE_BGR);
    // then do a funky color convert
    ColorConvertOp op = new ColorConvertOp(null);
    op.filter(image, rgbImage);
    return rgbImage;

【讨论】:

这是我发现的唯一一个解决了 JPEG 上的绿色问题的答案,该问题有几个关于 SO 的问题。【参考方案6】:

我通过这个来修复它。 只需要添加这个依赖。我可以通过 ImageIO 读取 CMYK 图像。 TwelveMonkeys

ImageIO.read(new URL("http://img3.tianyancha.com/api/9b80a61183787909e719c77fd0f78103.png"))

【讨论】:

以上是关于无法使用 ImageIO.read(文件文件)读取 JPEG 图像的主要内容,如果未能解决你的问题,请参考以下文章

怎么用java从文件中读取图片和写入图片到文件里

ImageIO.read() 返回 null

如何读取 .bmp 文件识别 Java 中哪些像素是黑色的

使用 ImageIO.read 的问题

Java Gif 读写

Java中BufferedImage的用法,如何读取保存一个图片