【问题标题】:GIF image becomes wrong after ImageIO read() and write() operationsImageIO read() 和 write() 操作后 GIF 图像出错
【发布时间】:2016-06-20 17:45:31
【问题描述】:

我有这个代码。它只是读取一个 GIF 文件,用背景重新绘制它,然后输出到一个新的 GIF 文件。

问题是结果文件变得奇怪。我不知道为什么它会变得质量差。该问题不会发生在 JPG 文件上。如何解决?

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class ImageTest {

    public static void main(String[] args) {
        f();
    }

    private static final String EXTENSION = "gif";
    private static final String FILENAME = "pinkHeart";
    private static final String PATH = "/Users/hieugioi/Downloads/";

    public static void f() {
        File file = new File(PATH + FILENAME + "." + EXTENSION);

        try {
            final BufferedImage originalImage = ImageIO.read(file);

            int imageType = getImageType(originalImage);
            final BufferedImage buff = new BufferedImage(originalImage.getWidth(), originalImage.getHeight(), imageType);
            final Graphics2D g = buff.createGraphics();

            Color backgroundColor = Color.GRAY;
            g.setColor(backgroundColor);
            g.fill(new Rectangle(0, 0, buff.getWidth(), buff.getHeight()));
            g.drawImage(originalImage, null, 0, 0);

            File out = new File(PATH + FILENAME + "Out." + EXTENSION);
            ImageIO.write(buff, EXTENSION, out);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static int getImageType(BufferedImage img) {
        int imageType = img.getType();
        if (imageType == BufferedImage.TYPE_CUSTOM) {
            if (img.getAlphaRaster() != null) {
                imageType = BufferedImage.TYPE_INT_ARGB_PRE;
            } else {
                imageType = BufferedImage.TYPE_INT_RGB;
            }
        } else if (imageType == BufferedImage.TYPE_BYTE_INDEXED && img.getColorModel().hasAlpha()) {
            imageType = BufferedImage.TYPE_INT_ARGB_PRE;
        }
        return imageType;
    }
}

输入图片(pinkHeart.gif):

输出图片(pinkHeartOut.gif):

更新案例 2

输入图片(example.gif):

输出图像(exampleOut.gif):输出的黄色完全消失!

【问题讨论】:

  • 输出是不是也漏掉了灰色背景?

标签: java gif javax.imageio


【解决方案1】:

这里有两个不同的问题。

第一个假设是您的输入图像具有透明度。据我所知,他们没有。因此,背景不会变为灰色,而是在两种情况下都保持纯白色。这没有什么问题,但可能不是您想要/预期的。

另一个(“真正的”问题)是getImageType(..) 的代码没有BufferedImage.TYPE_BYTE_INDEXED 没有alpha 的特殊分支。因此,图像类型将按原样返回。当使用BufferedImage.TYPE_BYTE_INDEXED 类型创建BufferedImage 时,它将有一个带有固定默认调色板 的颜色模型(事实上,它是老式的256 色“网络安全”调色板) .您原来的粉红色与此调色板中的粉红色不完全匹配,因此使用粉红色和白色进行了抖动处理。

第二个输入图像的“问题”在于它根本不是TYPE_BYTE_INDEXED,而是TYPE_BYTE_BINARY。这种类型用于每个像素有 1-4 位的图像,并且将多个像素“打包”到一个字节中。如上所述,当使用 BufferedImage.TYPE_BYTE_BINARY 类型创建 BufferedImage 时,它将有一个带有固定的、默认的 2 色黑白调色板的颜色模型(这就是黄色消失的原因)。

通过在返回TYPE_INT_RGBgetImageType(..) 方法中添加上述类型的分支,我得到与原始输出相同的输出(这是我所期望的,只要您的图像没有透明背景):

public static int getImageType(BufferedImage img) {
    int imageType = img.getType();
    switch (imageType) {
        case BufferedImage.TYPE_CUSTOM:
            if (img.getAlphaRaster() != null) {
                imageType = BufferedImage.TYPE_INT_ARGB_PRE;
            }
            else {
                imageType = BufferedImage.TYPE_INT_RGB;
            }
            break;
        case BufferedImage.TYPE_BYTE_BINARY:
            // Handle both BYTE_BINARY (1-4 bit/pixel) and BYTE_INDEXED (8 bit/pixel)
        case BufferedImage.TYPE_BYTE_INDEXED:
            if (img.getColorModel().hasAlpha()) {
                imageType = BufferedImage.TYPE_INT_ARGB_PRE;
            }
            else {
                // Handle non-alpha variant
                imageType = BufferedImage.TYPE_INT_RGB;
            }
            break;
    }

    return imageType;
}

PS:这是一种替代方法,它完全避免了创建原始图像副本的问题,而且速度更快,而且还节省了内存。它应该与您上面的代码完全一样:

public class ImageTest2 {

    public static void main(String[] args) throws IOException {
        f(new File(args[0]));
    }

    static void f(File file) throws IOException {
        BufferedImage image = ImageIO.read(file);

        // TODO: Test if image has transparency before doing anything else,
        // otherwise just copy the original as-is, for even better performance

        Graphics2D g = image.createGraphics();

        try {
            // Here's the trick, with DstOver we'll paint "behind" the original image
            g.setComposite(AlphaComposite.DstOver); 
            g.setColor(Color.GRAY);
            g.fill(new Rectangle(0, 0, image.getWidth(), image.getHeight()));
        }
        finally {
            g.dispose();
        }

        File out = new File(file.getParent() + File.separator + file.getName().replace('.', '_') + "_out.gif");
        ImageIO.write(image, "GIF", out);
    }
}

【讨论】:

  • 我不明白你是怎么知道图片类型的。如何选择相应的图像类型?我如何对这些有全面的了解?你能教我钓鱼吗?
  • 我只是从getImageType 方法中打印img.getType() 的值,并将它们与BufferedImage 中的TYPE_* 常量进行比较。您会看到第一个图像的值为 12,而第二个图像的值为 13。基本调试,真的。完全理解更难...... ;-) 我通常会查看 API 文档,并在需要深入挖掘时阅读源代码。
【解决方案2】:

我目前没有 java,但我认为你应该使用 BufferedImage 的 ColorModel。

ColorModel

【讨论】:

    【解决方案3】:

    我认为这是最好的方法。 detail

        BufferedImage src1 = ImageIO.read(new File("test.jpg"));
        BufferedImage src2 = ImageIO.read(new File("W.gif"));
        AnimatedGifEncoder e = new AnimatedGifEncoder();
        e.setRepeat(0);
        e.start("laoma.gif");
        e.setDelay(300); // 1 frame per sec
        e.addFrame(src1);
        e.setDelay(100);
        e.addFrame(src2);
        e.setDelay(100);
        e.finish();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-04-03
      • 1970-01-01
      相关资源
      最近更新 更多