Java 从图像中剥离Alpha通道

Java 从图像中剥离Alpha通道,java,bufferedimage,alpha,twelvemonkeys,Java,Bufferedimage,Alpha,Twelvemonkeys,我想从PNG中剥离alpha通道(透明背景),然后将其作为JPEG图像写入。更准确地说,我想把透明像素变成白色。我尝试了两种技术,两种技术都以不同的方式失败: 方法1: BufferedImage rgbCopy = new BufferedImage(inputImage.getWidth(), inputImage.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics2D graphics = rgbCopy.createGraphics();

我想从PNG中剥离alpha通道(透明背景),然后将其作为JPEG图像写入。更准确地说,我想把透明像素变成白色。我尝试了两种技术,两种技术都以不同的方式失败:

方法1:

BufferedImage rgbCopy = new BufferedImage(inputImage.getWidth(), inputImage.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = rgbCopy.createGraphics();
graphics.drawImage(inputImage, 0, 0, Color.WHITE, null);
graphics.dispose();
return rgbCopy;
final WritableRaster raster = inputImage.getRaster();
final WritableRaster newRaster = raster.createWritableChild(0, 0, inputImage.getWidth(), inputImage.getHeight(), 0, 0, new int[]{0, 1, 2});
ColorModel newCM = new ComponentColorModel(inputImage.getColorModel().getColorSpace(), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
return new BufferedImage(newCM, newRaster, false, null);
结果:图像具有粉红色背景

方法2:

BufferedImage rgbCopy = new BufferedImage(inputImage.getWidth(), inputImage.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D graphics = rgbCopy.createGraphics();
graphics.drawImage(inputImage, 0, 0, Color.WHITE, null);
graphics.dispose();
return rgbCopy;
final WritableRaster raster = inputImage.getRaster();
final WritableRaster newRaster = raster.createWritableChild(0, 0, inputImage.getWidth(), inputImage.getHeight(), 0, 0, new int[]{0, 1, 2});
ColorModel newCM = new ComponentColorModel(inputImage.getColorModel().getColorSpace(), false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
return new BufferedImage(newCM, newRaster, false, null);
结果:图像背景为黑色

在这两种情况下,输入图像都是PNG,输出图像以JPEG格式写入,如下所示:
ImageIO.write(bufferedImage,“jpg”,buffer)
。以防万一:这是在Java8上,我正在使用twelvemonkeys库调整图像大小,然后再将其作为JPEG格式写入


我已经尝试了上述代码的多种变体,但运气不佳。前面有许多问题建议使用上述代码,但在本例中似乎不起作用

haraldk是正确的,还有另一段代码导致了alpha通道问题。方法1中的代码正确且有效

但是,如果在剥离alpha通道后,使用twelvemonkeys Resample(十二猴子重采样)调整图像大小,如下所示:

BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_TRIANGLE);
BufferedImage resizedImg = resampler.filter(rgbImage, null);
这将导致alpha通道为粉红色


解决方案是在剥离alpha通道之前调整图像大小

多亏了罗布的回答,我们现在才知道为什么颜色会乱七八糟

问题有两方面:

  • ImageIO
    用于编写JPEG的默认
    JPEGImageWriter
    ,不会以其他软件可以理解的方式使用alpha编写JPEG(这是一个已知问题)
  • 当将
    null
    作为目标传递给
    ResampleOp.filter(src,dest)
    并且过滤方法是
    filter\u TRIANGLE
    时,将创建一个新的
    buffereImage
    ,其中包含alpha(实际上是
    buffereImage.TYPE\u INT\u ARGB
重新采样后剥离alpha将起作用。但是,还有另一种方法可能更快并节省一些内存。也就是说,不是传递一个
null
目标,而是传递一个大小和类型合适的
buffereImage

public static void main(String[] args) throws IOException {
    // Read input
    File input = new File(args[0]);
    BufferedImage inputImage = ImageIO.read(input);

    // Make any transparent parts white
    if (inputImage.getTransparency() == Transparency.TRANSLUCENT) {
        // NOTE: For BITMASK images, the color model is likely IndexColorModel,
        // and this model will contain the "real" color of the transparent parts
        // which is likely a better fit than unconditionally setting it to white.

        // Fill background  with white
        Graphics2D graphics = inputImage.createGraphics();
        try {
            graphics.setComposite(AlphaComposite.DstOver); // Set composite rules to paint "behind"
            graphics.setPaint(Color.WHITE);
            graphics.fillRect(0, 0, inputImage.getWidth(), inputImage.getHeight());
        }
        finally {
            graphics.dispose();
        }
    }

    // Resample to fixed size
    int width = 100;
    int height = 100;

    BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_TRIANGLE);

    // Using explicit destination, resizedImg will be of TYPE_INT_RGB
    BufferedImage resizedImg = resampler.filter(inputImage, new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB));

    // Write output as JPEG
    ImageIO.write(resizedImg, "JPEG", new File(input.getParent(), input.getName().replace('.', '_') + ".jpg"));
}

在将图像绘制到顶部之前,您可以将整个光栅绘制为白色吗?您是说您的图像在运行1中的代码后具有粉红色背景吗?还是涉及到更多的代码?我建议你创建一个完整的。不要忘了添加测试数据(PNG文件),以进行全面验证。我尝试了(通过重建缺失的部分)您的方法1,它应该可以工作。的确如此。对我来说,它工作得很好,使用了Java7和Java8。FWIW我在OSX上,10.11。3.最有可能的是,代码中有一些东西没有显示,这导致了这个问题。这比我建议的解决方案要干净得多,但是当我用PNG测试它时,输出图像是黑色的。我确信我遗漏了一些东西,因为从表面上看,它应该是有效的。你是对的,我不知怎么地忘记了,因为我的测试PNG被索引了,透明的颜色是白色。。。我将更新答案。@Rob更新了答案以添加白色背景。谢谢,这很有用。对于正在查看此解决方案的任何其他人,如果仅使用代码的一部分填充alpha通道(即,不调整其大小),则需要将图像复制到新的类型_INT_RGB,否则ImageIO将错误地写入。