ImageIO read()和write()操作后,GIF图像变得错误 [英] GIF image becomes wrong after ImageIO read() and write() operations

查看:303
本文介绍了ImageIO read()和write()操作后,GIF图像变得错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

解决方案

这里有两个独立的问题.

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

另一个(实际"问题)是getImageType(..)的代码没有BufferedImage.TYPE_BYTE_INDEXED没有特殊分支.因此,图像类型将按原样返回.并且,当使用BufferedImage.TYPE_BYTE_INDEXED类型创建BufferedImage时,它将具有带有固定的默认调色板的颜色模型(实际上,它是旧的256色网络安全"调色板). .原始色中的粉红色与该调色板中的粉红色不完全匹配,因此使用粉红色和白色来抖动.

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

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

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

I have this code. It simply reads a GIF file, redraws it with background and output to a new GIF file.

The problem is the result file becomes strange. I have no idea why it becomes poor quality. The problem doesn't happen on JPG files. How to fix it?

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

Input image (pinkHeart.gif):

Output image (pinkHeartOut.gif):

Update case 2

Input image (example.gif):

Output image (exampleOut.gif): The output's yellow totally disappears!

解决方案

There are two separate problems here.

The first is the assumption that your input images have transparency. They do not, as far as I can see. Because of this, the background will not change to gray, but stay solid white in both cases. There's nothing wrong with this, but perhaps not what you intended/expected.

The other (the "real" problem) is that the code for getImageType(..) has no special branch for BufferedImage.TYPE_BYTE_INDEXED without alpha. Because of this, the image type will just be returned as is. And when a BufferedImage is created with the BufferedImage.TYPE_BYTE_INDEXED type, it will have a color model with a fixed, default palette (in fact, it's the good old 256 color "web safe" palette). The pink color in your origininal does not exactly fit the pink in this palette, and is thus dithered using pink and white.

The "issue" with your second input image, is that it is not TYPE_BYTE_INDEXED at all, it is TYPE_BYTE_BINARY. This type is used for images that have 1-4 bits per pixel, and multiple pixels "packed" into one byte. As above, when a BufferedImage is created with the BufferedImage.TYPE_BYTE_BINARY type, it will have a color model with a fixed, default 2 color black/white palette (that's why the yellow disappear).

By adding branches for the above types in the getImageType(..) method that returns TYPE_INT_RGB instead, I get the same output as your original (which is what I expect, as long as your image has no transparent background):

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: Here's an alternate approach that avoids the problem of creating a copy of the original image altogether, and is both faster and saves memory as a bonus. It should do exactly the same as your code above intends:

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

这篇关于ImageIO read()和write()操作后,GIF图像变得错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆