Java 无法使用 ImageIO.read(File file) 读取 JPEG 图像

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2408613/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 07:11:43  来源:igfitidea点击:

Unable to read JPEG image using ImageIO.read(File file)

javaimagejpegjavax.imageio

提问by Malakim

I'm having problems reading this one JPEG file using ImageIO.read(File file) - it throws an exception with the message "Unsupported Image Type".

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

I have tried other JPEG images, and they seem to work fine.

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

The only differance I've been able to spot is that this file seems to include a thumbnail - is that known to cause problems with ImageIO.read()?

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

Troublesome image

Troublesome image

EDIT:

编辑:

Added the resulting image:

添加了结果图像:

Strange colors

Strange colors

采纳答案by medopal

Your image "Color Model" is CMYK, JPEGImageReader(the inner class that reads your file) reads only RGB Color Model.

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

If you insist on reading CMYK images, then you will need to convert them, try this code.

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

UPDATE

更新

Read a CMYK image into RGB BufferedImage.

将 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);

UPDATE - March 2015 - Adding simulation images

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

Original images were removed from OP's dropbox. So I'm adding new images (not the originals) that simulates the problem that was happening with them.

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

First imageis how a normal RGB image looks like.

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

Image RGB

Image RGB

Second imageis how the same image will look like in CMYK color model.

第二个图像是相同图像在 CMYK 颜色模型中的外观。

You cannot actually see how it looks on the web because it will be converted to RGB by the host. To see exactly how it looks, take the RGB image and run it through an RGB to CMYK converter.

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

Third imageis how the CMYK image will look like when read then written using Java ImageIO.

第三个图像是 CMYK 图像在使用 Java ImageIO 读取然后写入时的外观。

Image CMYK read through Java RGB

Image CMYK read through Java RGB

The problem that was happening with OP is they had something like image 2 which throws an exception when you try to read it.

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

回答by enkor

I found https://stackoverflow.com/questions/22409...on here as well, this one does a great colour conversion

我在这里也找到了https://stackoverflow.com/questions/22409...,这个做了很好的颜色转换

And combined both to get this:

并将两者结合起来得到这个:

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;
}

回答by Peter

ImageIO.read()->

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();

回答by Codo

I'm a bit late to the party. But it's probably still worth that I post my answer as none of the answers really solves the problem.

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

The solution requires Sanselan (or Apache Commons Imaging as it's called now) and it requires a reasonable CMYK color profile (.icc file). You can get the later one from Adobe or from eci.org.

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

The basic problem is that Java - out of the box - can only read JPEG files in RGB. If you have a CMYK file, you need to distinguish between regular CMYK, Adobe CMYK (with inverted values, i.e. 255 for no ink and 0 for maximum ink) and Adobe CYYK (some variant with inverted colors as well).

基本问题是 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;
    }
}

The code first tries to read the file using the regular method, which works for RGB files. If it fails, it reads the details of the color model (profile, Adobe marker, Adobe variant). Then it reads the raw pixel data (raster) and does all the necessary conversion (YCCK to CMYK, inverted colors, CMYK to RGB).

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

I'm not quite satisfied with my solution. While the colors are mostly good, dark areas are slightly too bright, in particular black isn't fully black. If anyone knows what I could improve, I'd be glad to hear it.

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

回答by haraldK

Old post, but for future reference:

旧帖子,但供将来参考:

Inspired by this question and links found here, I've written a JPEGImageReader plugin for ImageIO that supports CMYK color models (both with original color model, or implicitly converted to RGB on read). The reader also does proper color conversion, using the ICC profile embedded in the JPEG stream, in contrast to other solutions mentioned here.

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

It's plain Java and does not require JAI. The source code and binary distributions are freely available at github.com/haraldk/TwelveMonkeys, and is covered by a BSD-style license.

它是普通的 Java,不需要 JAI。源代码和二进制发行版可在github.com/haraldk/TwelveMonkeys上免费获得,并受 BSD 风格的许可。

Once you have it installed, it allows you to read CMYK JPEGs using ImageIO.read(...)like this:

安装后,它允许您使用以下方式读取 CMYK JPEG ImageIO.read(...)

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

I.e.: In most cases, it's not necessary to modify your code.

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

回答by yaoning

i fix it by this. only need add this dependency. i can read CMYK image by ImageIO. TwelveMonkeys

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

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