ImageIO无法写入JPEG文件。

62

我有一个BufferedImage,想将其写入JPEG文件,但我的Java程序抛出了异常。我可以成功地将相同的缓冲区保存为gif和png。我尝试在Google上寻找解决方案,但没有结果。

代码:

   File outputfile = new File("tiles/" + row + ":" + col + ".jpg");
   try {
       ImageIO.write(mapBufferTiles[row][col], "jpg", outputfile);
   } catch (IOException e) {
        outputfile.delete();
        throw new RuntimeException(e);
   }

异常:

 Exception in thread "main" java.lang.RuntimeException: javax.imageio.IIOException: Invalid argument to native writeImage
 at MapServer.initMapBuffer(MapServer.java:90)
 at MapServer.<init>(MapServer.java:24)
 at MapServer.main(MapServer.java:118)
 Caused by: javax.imageio.IIOException: Invalid argument to native writeImage
 at com.sun.imageio.plugins.jpeg.JPEGImageWriter.writeImage(Native Method)
 at com.sun.imageio.plugins.jpeg.JPEGImageWriter.writeOnThread(JPEGImageWriter.java:1055)
 at com.sun.imageio.plugins.jpeg.JPEGImageWriter.write(JPEGImageWriter.java:357)
 at javax.imageio.ImageWriter.write(ImageWriter.java:615)
 at javax.imageio.ImageIO.doWrite(ImageIO.java:1602)
 at javax.imageio.ImageIO.write(ImageIO.java:1526)
 at MapServer.initMapBuffer(MapServer.java:87)
 ... 2 more

1
你所使用的平台是否允许文件名中包含冒号(:)? - mwittrock
2
你正在使用OpenJDK吗?据我所知,OpenJDK没有本地的JPEG编码器。 - Rui Vieira
@Karan:如果Sun的JDK是默认设置,那就不应该有问题。 mapBufferTiles[row][col]是什么类型的数据?它应该是一个BufferedImage。 - Rui Vieira
@Rui,是的,它是一个BufferedImage。我现在已经默认启用了Sun的JDK,因为它确实在使用OpenJDK。它不再抛出异常,但由于输入缓冲区包含透明度,我认为jpeg正在输出粉色。无论如何,供将来参考,我会在这里放置这个链接:http://ianma.wordpress.com/2010/05/03/access-restriction-java-openjdk-rt-jar/ - Karan
@Karan,好的。我把它作为答案放上去? - Rui Vieira
显示剩余2条评论
6个回答

47
OpenJDK没有原生的JPEG编码器。尝试使用Sun的JDK,或者使用一个库(比如JAI)。
据我所知,关于“粉红色色调”,Java将JPEG保存为ARGB(仍带有透明度信息)。大多数查看器在打开时会假设四个通道应该对应于CMYK(而不是ARGB),从而产生红色色调。
如果将图像重新导入到Java中,透明度仍然存在。

1
关于粉色色调问题,我只是按照以下链接的方法将透明像素转换为白色:https://dev59.com/RXRB5IYBdhLWcg3w7riV#1545417 - Karan
1
第二段结束处 - “..as thus the red tint.” 应该改为“..and thus the red tint.”,对吗? - Andrew Thompson
9
顺便提一句,OpenJDK确实有原生的JPEG编码器。如果你尝试保存32位色彩文件,会失败。而Sun JDK不会失败,但会形成着色文件。不确定哪个更好。 - Andrey Regentov
@AndreyRegentov 可能现在有了,但我非常确定在写作时(2010年)没有。 - Rui Vieira
谢谢这个信息!虽然有点离题,但我正在运行Arch Linux并尝试使用Apophysis-J + OpenJDK v7(一个有趣的分形创作应用程序),但我无法使渲染函数输出有效的.jpg文件--我也找不到任何文档(这就是为什么我在这里留言的原因),但结果很容易解决--只需将渲染文件输出为.png即可正常工作。希望这能帮助到某个人。再次感谢这个信息,让我省去了一些沮丧! - hypervisor666

40

我在使用同样的OpenJDK 7版本时遇到相同的问题,通过使用TYPE_3BYTE_BGR而不是TYPE_4BYTE_ABGRimageType,我成功地绕过了此异常。


3
没错!我创建了一个新的TYPE_3BYTE_BGR BufferedImage,并从TYPE_INT_ARGB BufferedImage中使用getRGB(),然后在新的BufferedImage上使用setRGB(),最后调用ImageIO.write()。它在Linux上运行良好。 - Peter Quiring
1
谢谢。将图像从TYPE_4BYTE_ABGR更改为TYPE_3BYTE_BGR对我有用。 - IBRAR AHMAD
1
有人能告诉我他的意思吗?我像这样读取图像 ImageIO.read(new File(path));,但没有图像类型参数? - centenond
@PeterQuiring,你能分享一下你是怎么做到的吗?正如centenond所提到的,没有参数可以指定图像类型,并且根据javadoc,图像类型会自动从系统注册表中确定。你如何影响这个过程呢? - jotadepicas
@IBRARAHMAD,你能分享一下你是如何实现这个的代码吗? - jotadepicas
显示剩余3条评论

26

2019年的回答:确保您的BufferedImage没有Alpha透明度。 JPEG不支持Alpha通道,因此如果您的图像具有Alpha通道,则ImageIO无法将其写入JPEG。

使用以下代码确保您的图像没有Alpha透明度:

static BufferedImage ensureOpaque(BufferedImage bi) {
    if (bi.getTransparency() == BufferedImage.OPAQUE)
        return bi;
    int w = bi.getWidth();
    int h = bi.getHeight();
    int[] pixels = new int[w * h];
    bi.getRGB(0, 0, w, h, pixels, 0, w);
    BufferedImage bi2 = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
    bi2.setRGB(0, 0, w, h, pixels, 0, w);
    return bi2;
}

我遇到了这个问题,因为我正在读取带有alpha通道的PNG文件,并尝试写入JPG文件(好吧,不是我在做,而是我在处理一个旧代码...)。你能为这种情况建议一个解决方法吗?谢谢! - jotadepicas
1
使用 BufferedImage.TYPE_INT_RGB 解决了这个问题。完整示例(由Oracle提供) - Cempoalxóchitl

8

以下是一些代码,用于说明@Thunder的想法将图像类型更改为TYPE_3BYTE_BGR

try {
  BufferedImage input = ImageIO.read(new File("input.png"));
  System.out.println("input image type=" + input.getType());
  int width = input.getWidth();
  int height = input.getHeight();
  BufferedImage output = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
  int px[] = new int[width * height];
  input.getRGB(0, 0, width, height, px, 0, width);
  output.setRGB(0, 0, width, height, px, 0, width);
  ImageIO.write(output, "jpg", new File("output.jpg"));
} catch (Exception e) {
  e.printStackTrace();
}

我可以确认,这适用于从NITF文件的ImageSegments中提取的BufferedImages。 - Benjamin

1
您得到了相同的错误。
Caused by: javax.imageio.IIOException: Invalid argument to native writeImage
at com.sun.imageio.plugins.jpeg.JPEGImageWriter.writeImage(Native Method)
at com.sun.imageio.plugins.jpeg.JPEGImageWriter.writeOnThread(JPEGImageWriter.java:1055)

如果您使用的不是受支持的颜色空间(在我的情况下为CYMK),请参阅如何在Java中正确转换从CMYK到RGB?以解决此问题。

0
当我得到的图像是一个RenderedImage时,我遇到了一个变化,而不是复制字节数组,我使用的是Raster,它是一个矩形图像表示。
var newBufferedImage = new BufferedImage(
    renderImg.getWidth(),
    renderImg.getHeight(),
    BufferedImage.TYPE_INT_RGB
);
renderImg.copyData(newBufferedImage.getRaster());

根据您的需求,从ColorModel获取光栅可能会很有趣。

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