如何在Java中将图像转换为透明图像

3
如何将图像的白色背景转换为透明背景?有人可以告诉我该如何操作吗?
4个回答

4

嗨Tim,谢谢你的回复。我已经尝试了使用白色代替蓝色的代码,但是我没有得到透明的图像,图像背景颜色被填充为黑色。顺便说一下,我没有使用小程序,而是使用了"ImageIO.write"并将其写成image.png,也尝试过gif格式。 - SWDeveloper
也发现了这个问题:https://dev59.com/AHRB5IYBdhLWcg3wUFrB - tim_yates
我认为你的“黑色背景”问题是没有创建一个新的BufferedImage,使用TYPE_INT_ARGB将过滤后的图像复制到其中(即:你正在将图像复制到不支持透明度的BufferedImage中)。 - tim_yates
哇,你说得对,它能工作了...但有一个小问题,它不是完全透明的,图像轮廓附近的一些像素仍然是白色的。 - SWDeveloper
它们不是100%纯白... :-/ - tim_yates
显示剩余2条评论

1

这种方法可以使背景透明。您需要传入想要修改的图像、颜色和容差。

final int color = ret.getRGB(0, 0);
final Image imageWithTransparency = makeColorTransparent(ret, new Color(color), 10);
final BufferedImage transparentImage = imageToBufferedImage(imageWithTransparency);

private static BufferedImage imageToBufferedImage(final Image image) {
        final BufferedImage bufferedImage =
            new BufferedImage(image.getWidth(null), image.getHeight(null), BufferedImage.TYPE_INT_ARGB);
        final Graphics2D g2 = bufferedImage.createGraphics();
        g2.drawImage(image, 0, 0, null);
        g2.dispose();
        return bufferedImage;
}


private static Image makeColorTransparent(final BufferedImage im, final Color color, int tolerance) {
    int temp = 0;

    if (tolerance < 0 || tolerance > 100) {

        System.err.println("The tolerance is a percentage, so the value has to be between 0 and 100.");

        temp = 0;

    } else {

        temp = tolerance * (0xFF000000 | 0xFF000000) / 100;

    }

    final int toleranceRGB = Math.abs(temp);



    final ImageFilter filter = new RGBImageFilter() {

        // The color we are looking for (white)... Alpha bits are set to opaque

        public int markerRGBFrom = (color.getRGB() | 0xFF000000) - toleranceRGB;

        public int markerRGBTo = (color.getRGB() | 0xFF000000) + toleranceRGB;



        public final int filterRGB(final int x, final int y, final int rgb) {

            if ((rgb | 0xFF000000) >= markerRGBFrom && (rgb | 0xFF000000) <= markerRGBTo) {

                // Mark the alpha bits as zero - transparent

                return 0x00FFFFFF & rgb;

            } else {

                // Nothing to do

                return rgb;

            }

        }

    }; 

    final ImageProducer ip = new FilteredImageSource(im.getSource(), filter);

    return Toolkit.getDefaultToolkit().createImage(ip);
}

0

我知道这个问题已经超过十年了,并且已经给出了一些答案。然而,如果图像内部的像素颜色与背景相同,则它们都不令人满意。让我们举一个实际的例子。 假设有以下这些图像:

enter image description here enter image description here

两个都有白色背景,但是白色也在要被剪切的图像内部。换句话说,两个旗帜外面的白色像素必须变成透明,内部的保持不变。加上这个背景白色并不完全是白色的(由于JPEG压缩),所以需要一个容差。如果图形不仅是凸出的而且是凹陷的,则问题可以变得更加复杂。

我用Java创建了一个非常好的算法来解决这个问题,我用这里显示的两个图形进行了测试。以下代码涉及Codename One的Java API(https://www.codenameone.com/javadoc/),但可以重新用于Java SE API或实现其他语言。重要的是要理解原理。

    /**
     * Given an image with no transparency, it makes the white background
     * transparent, provided that the entire image outline has a different color
     * from the background; the internal pixels of the image, even if they have
     * the same color as the background, are not changed.
     *
     * @param source image with a white background; the image must have an
     * outline of a different color from background.
     * @return a new image with a transparent background
     */
    public static Image makeBackgroundTransparent(Image source) {
        /*
         * Algorithm
         *
         * Pixels must be iterated in the four possible directions: (1) left to
         * right, for each row (top to bottom); (2) from right to left, for each
         * row (from top to bottom); (3) from top to bottom, for each column
         * (from left to right); (4) from bottom to top, for each column (from
         * left to right).
         *
         * In each iteration, each white pixel is replaced with a transparent
         * one. Each iteration ends when a pixel of color other than white (or
         * a transparent pixel) is encountered.
         */
        if (source == null) {
            throw new IllegalArgumentException("ImageUtilities.makeBackgroundTransparent -> null source image");
        }
        if (source instanceof FontImage) {
            source = ((FontImage) source).toImage();
        }
        int[] pixels = source.getRGB(); // array instance containing the ARGB data within this image
        int width = source.getWidth();
        int height = source.getHeight();
        int tolerance = 1000000; // value chosen through several attempts

        // check if the first pixel is transparent
        if ((pixels[0] >> 24) == 0x00) {
            return source; // nothing to do, the image already has a transparent background
        }
        
        Log.p("Converting white background to transparent...", Log.DEBUG);

        // 1. Left to right, for each row (top to bottom)
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                int color = pixels[y * width + x];
                if ((color >> 24) != 0x00 && color >= ColorUtil.WHITE - tolerance && color <= ColorUtil.WHITE + tolerance) { // means white with tolerance and no transparency
                    pixels[y * width + x] = 0x00; // means full transparency
                } else {
                    break;
                }
            }
        }

        // 2. Right to left, for each row (top to bottom)
        for (int y = 0; y < height; y++) {
            for (int x = width - 1; x >= 0; x--) {
                int color = pixels[y * width + x];
                if ((color >> 24) != 0x00 && color >= ColorUtil.WHITE - tolerance && color <= ColorUtil.WHITE + tolerance) { // means white with tolerance and no transparency
                    pixels[y * width + x] = 0x00; // means full transparency
                } else {
                    break;
                }
            }
        }
        
        // 3. Top to bottom, for each column (from left to right)
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                int color = pixels[y * width + x];
                if ((color >> 24) != 0x00 && color >= ColorUtil.WHITE - tolerance && color <= ColorUtil.WHITE + tolerance) { // means white with tolerance and no transparency
                    pixels[y * width + x] = 0x00; // means full transparency
                } else {
                    break;
                }
            }
        }
        
        // 4. Bottom to top, for each column (from left to right)
        for (int x = 0; x < width; x++) {
            for (int y = height - 1; y >= 0; y--) {
                int color = pixels[y * width + x];
                if ((color >> 24) != 0x00 && color >= ColorUtil.WHITE - tolerance && color <= ColorUtil.WHITE + tolerance) { // means white with tolerance and no transparency
                    pixels[y * width + x] = 0x00; // means full transparency
                } else {
                    break;
                }
            }
        }
        
        return EncodedImage.createFromRGB(pixels, width, height, false);
    }

0

这是我的解决方案。只要背景图像颜色在左上角,此过滤器将从任何图像中删除背景。

private static class BackgroundFilter extends RGBImageFilter{

    boolean setUp = false;
    int bgColor;

    @Override
    public int filterRGB(int x, int y, int rgb) {
        int colorWOAlpha = rgb & 0xFFFFFF;
        if( ! setUp && x == 0 && y == 0 ){
            bgColor = colorWOAlpha;
            setUp = true;
        }
        else if( colorWOAlpha == bgColor )
            return colorWOAlpha;
        return rgb;
    }
}

在其他地方...

ImageFilter bgFilter = new BackgroundFilter();
ImageProducer ip = new FilteredImageSource(image.getSource(), bgFilter);
image = Toolkit.getDefaultToolkit().createImage(ip);

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