23

我想使用 Java 将 gif 图像转换为 jpeg。它适用于大多数图像,但我有一个简单的透明 gif 图像:

输入 gif 图片 http://img292.imageshack.us/img292/2103/indexedtestal7.gif

[如果图像丢失:它是一个带有透明像素的蓝色圆圈]

当我使用以下代码转换此图像时:

File file = new File("indexed_test.gif");
BufferedImage image = ImageIO.read(file);
File f = new File("indexed_test.jpg");
ImageIO.write(image, "jpg", f);

此代码在不引发异常的情况下工作,但会导致无效的 jpeg 图像:

输出JPEG图像

[如果图像丢失:IE 无法显示 jpeg,Firefox 显示的图像颜色无效。]

我正在使用 Java 1.5。

我还尝试使用 gimp 将示例 gif 转换为 png,并将 png 作为 Java 代码的输入。结果是一样的。

这是JDK中的错误吗?如何在没有 3rd 方库的情况下正确转换图像?

更新:

答案表明 jpeg 转换无法正确处理透明度(我仍然认为这是一个错误),并建议使用预定义颜色替换透明像素的解决方法。两种建议的方法都非常复杂,所以我实现了一个更简单的方法(将作为答案发布)。我接受使用此解决方法的第一个已发布答案(由 Markus 提供)。我不知道哪种实现更好。我选择了最简单的一个,但我发现了一个不起作用的 gif。

4

7 回答 7

43

对于 Java 6(我认为也是 5):

BufferedImage bufferedImage = new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_RGB);
g = bufferedImage.createGraphics();
//Color.WHITE estes the background to white. You can use any other color
g.drawImage(image, 0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), Color.WHITE, null);
于 2009-10-09T18:39:16.827 回答
11

正如问题更新中已经提到的那样,我已经实现了一种用预定义颜色替换透明像素的更简单方法:

public static BufferedImage fillTransparentPixels( BufferedImage image, 
                                                   Color fillColor ) {
    int w = image.getWidth();
    int h = image.getHeight();
    BufferedImage image2 = new BufferedImage(w, h, 
        BufferedImage.TYPE_INT_RGB);
    Graphics2D g = image2.createGraphics();
    g.setColor(fillColor);
    g.fillRect(0,0,w,h);
    g.drawRenderedImage(image, null);
    g.dispose();
    return image2;
}

我以这种方式在jpeg转换之前调用此方法:

if( inputImage.getColorModel().getTransparency() != Transparency.OPAQUE) {
    inputImage = fillTransparentPixels(inputImage, Color.WHITE);
}
于 2009-01-22T09:08:11.587 回答
4

问题(至少对于 png 到 jpg 的转换)是配色方案不一样,因为 jpg 不支持透明度。

我们已经成功地完成了这些方面的事情(这是从各种代码中提取的 - 所以请原谅格式的粗略):

File file = new File("indexed_test.gif");
BufferedImage image = ImageIO.read(file);
int width = image.getWidth();
int height = image.getHeight();
BufferedImage jpgImage;

//you can probably do this without the headless check if you just use the first block
if (GraphicsEnvironment.isHeadless()) {
  if (image.getType() == BufferedImage.TYPE_CUSTOM) {
      //coerce it to  TYPE_INT_ARGB and cross fingers -- PNGs give a    TYPE_CUSTOM and that doesn't work with
      //trying to create a new BufferedImage
     jpgImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_ARGB);
  } else {
     jpgImage = new BufferedImage(width, height, image.getType());
  }
} else {
     jgpImage =   GraphicsEnvironment.getLocalGraphicsEnvironment().
        getDefaultScreenDevice().getDefaultConfiguration().
        createCompatibleImage(width, height, image.getTransparency()); 
}

//copy the original to the new image
Graphics2D g2 = null;
try {
 g2 = jpg.createGraphics();

 g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, 
                    RenderingHints.VALUE_INTERPOLATION_BICUBIC);
 g2.drawImage(image, 0, 0, width, height, null);
}
finally {
   if (g2 != null) {
       g2.dispose();
   }
}

File f = new File("indexed_test.jpg");

ImageIO.write(jpgImage, "jpg", f);

这适用于 png 到 jpg 和 gif 到 jpg。您将在透明位所在的位置获得白色背景。您可以通过在调用 drawImage 之前让 g2 用另一种颜色填充图像来更改此设置。

于 2009-01-21T16:58:17.190 回答
4

晚了 3 个月,但我遇到了一个非常相似的问题(虽然甚至没有加载 gif,但只是生成了一个透明图像 - 比如说,没有背景,彩色形状 - 当保存到 jpeg 时,所有颜色都搞砸了,不仅的背景)

在 java2d-interest list 的这个相当老的线程中找到了这段代码,我想分享一下,因为经过快速测试,它比您的解决方案性能要高得多

        final WritableRaster raster = img.getRaster();
        final WritableRaster newRaster = raster.createWritableChild(0, 0, img.getWidth(), img.getHeight(), 0, 0, new int[]{0, 1, 2});

        // create a ColorModel that represents the one of the ARGB except the alpha channel
        final DirectColorModel cm = (DirectColorModel) img.getColorModel();
        final DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(), cm.getRedMask(), cm.getGreenMask(), cm.getBlueMask());

        // now create the new buffer that we'll use to write the image
        return new BufferedImage(newCM, newRaster, false, null);

不幸的是,我不能说我完全理解它的作用;)

于 2009-05-13T16:37:45.540 回答
3

如果你创建一个 BufferedImage.TYPE_INT_ARGB 类型的 BufferedImage 并保存到 JPEG,就会出现奇怪的事情。在我的情况下,颜色被切成橙色。在其他情况下,生成的图像可能无效,其他阅读器将拒绝加载它。

但是,如果您创建 BufferedImage.TYPE_INT_RGB 类型的图像,则将其保存为 JPEG 可以正常工作。

因此,我认为这是 Java JPEG 图像编写器中的一个错误 - 它应该只编写没有透明度的情况(就像 .NET GDI+ 所做的那样)。或者在最坏的情况下引发异常,并带有有意义的消息,例如“无法编写具有透明度的图像”。

于 2011-01-05T10:14:17.980 回答
2

JPEG 不支持透明度。因此,即使您正确获得了圆圈颜色,您仍然会有黑色或白色背景,具体取决于您的编码器和/或渲染器。

于 2009-01-21T11:01:09.363 回答
1
BufferedImage originalImage = ImageIO.read(getContent());
BufferedImage newImage = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), BufferedImage.TYPE_3BYTE_BGR);

    for (int x = 0; x < originalImage.getWidth(); x++) {
        for (int y = 0; y < originalImage.getHeight(); y++) {
            newImage.setRGB(x, y, originalImage.getRGB(x, y));
        }
    }
 ImageIO.write(newImage, "jpg", f);

2020 年 7 月 9 日编辑:添加imageIO.write

于 2016-03-07T18:41:44.020 回答