当前位置: 首页 > 知识库问答 >
问题:

GIF图像在ImageIO read()和write()操作后出错

孟沛
2023-03-14

我有这个代码。它只是读取一个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):输出的黄色完全消失!

共有3个答案

仲鸿风
2023-03-14

我认为这是最好的方法。细节

    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();
包丁雨
2023-03-14

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

彩色模型

江恩
2023-03-14

这里有两个不同的问题。

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

另一个(“真正的”问题)是getImageType(..)的代码 没有用于缓冲图像的特殊分支。键入不带alpha的\u BYTE\u index。因此,图像类型将按原样返回。当使用缓冲图像创建缓冲图像时。TYPE\u BYTE\u indexTYPE,它将有一个带有固定默认调色板的颜色模型(实际上,这是一个很好的旧256色“web安全”调色板)。原始颜色中的粉红色与此调色板中的粉红色不完全匹配,因此使用粉色和白色进行html" target="_blank">抖动。

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

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

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

附:这里有一种替代方法,可以避免创建原始图像副本的问题,而且速度更快,节省内存。它应该与上面的代码完全相同:

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);
    }
}
 类似资料:
  • 如果要对图像进行进一步的处理,就可以先通过getImageData()方法获取图像像素,进行处理后再通过putImageData()方法,把处理后的像素重新绘制到画布中。 1)getImageData()方法 该方法用于获取画布上指定区域的图像像素数据。调用格式如下: var data = context.getImageData(sx, sy, sWidth, sHeight) 其中,sx、s

  • 我看到这个问题问了很多,但我还没有看到一个非常普遍的方法。我正在使用JavaFX在JavaEclipse Oxygen中开发一个桌面应用程序,我有一个加载器,我想在任何时候通过超文本传输协议加载图像。当用户单击不同的行时,图像必须通过超文本传输协议加载。所以我把我的加载器放在图像的顶部,我只是使用;来显示或隐藏它。fxml中的加载器ImageView在GridPane中看起来像这样。 设置加载器映

  • 读取图像文件信息 使用图像读取器读取文件头中的信息: #include <stdio.h> #include <LCUI/LCUI.h> #include <LCUI/image.h> ​ int main(int argc, char *argv[]) { FILE *fp; LCUI_ImageReaderRec reader = { 0 }; if (ar

  • 本文向大家介绍Python图像处理之gif动态图的解析与合成操作详解,包括了Python图像处理之gif动态图的解析与合成操作详解的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了Python图像处理之gif动态图的解析与合成操作。分享给大家供大家参考,具体如下: gif动态图是在现在已经司空见惯,朋友圈里也经常是一言不合就斗图。这里,就介绍下如何使用python来解析和生成gif图像。 一

  • 基本上我的问题分为两部分。 > 我想知道将要操纵像素并将特定颜色转换为透明像素的首选/快速方法。 我想知道我是否能够使用这个“BuffereImage”,而不必将其保存为支持“png”等透明度的文件格式 我找到了一种设置单个像素的方法 这儿呢 正如它所提到的,这是一种“缓慢的方法” 我发现这个线程Java:用透明像素填充BufferedImage 哪一条评论提到了“int[]”和操纵像素。 本质上

  • 主要内容:图像裁剪操作,图像拷贝和粘贴图像的剪裁、复制、粘贴是图像处理过程中经常使用的基本操作,Pillow Image 类提供了简单、易用的 API 接口,能够帮助您快速实现这些简单的图像处理操作。 图像裁剪操作 Image 类提供的 crop() 函数允许我们以矩形区域的方式对原图像进行裁剪,函数的语法格式如下: box:表示裁剪区域,默认为 None,表示拷贝原图像。 注意:box 是一个有四个数字的元组参数 (x_左上,y_左