从图像中剥离 Alpha 通道
Stripping Alpha Channel from Images
我想从 PNG 中去除 alpha 通道(透明背景),然后将它们写入 JPEG 图像。更准确地说,我想让透明像素变成白色。我尝试了两种技术,但都以不同的方式失败:
方法一:
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)
。如果它是相关的:这是在 Java 8 上,我正在使用 twelvemonkeys 库调整图像大小,然后再将其写入 JPEG。
我尝试了上述代码的多种变体,但没有成功。之前有很多问题建议使用上述代码,但在这种情况下似乎不起作用。
haraldk 是正确的,还有一段代码导致了 alpha 通道问题。方法 1 中的代码正确且有效。
但是,如果在剥离 alpha 通道后,您使用 twelvemonkeys ResampleOp 调整图像大小,如下所示:
BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_TRIANGLE);
BufferedImage resizedImg = resampler.filter(rgbImage, null);
这会导致 alpha 通道变成粉红色。
解决方案是在剥离 Alpha 通道之前调整图像大小。
感谢 Rob 的回答,我们现在知道为什么颜色会乱七八糟了。
问题是双重的:
ImageIO
用于写入 JPEG 的默认 JPEGImageWriter
不会以其他软件理解的方式写入带有 alpha 的 JPEG(这是一个已知问题)。
- 当将
null
作为目标传递给 ResampleOp.filter(src, dest)
且过滤方法为 FILTER_TRIANGLE
时,将创建一个新的 BufferedImage
,alpha(实际上,BufferedImage.TYPE_INT_ARGB
).
重采样后剥离 alpha 将起作用。但是,还有另一种方法可能会更快并节省一些内存。也就是说,不是传递 null
目的地,而是传递适当大小和类型的 BufferedImage
:
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"));
}
我想从 PNG 中去除 alpha 通道(透明背景),然后将它们写入 JPEG 图像。更准确地说,我想让透明像素变成白色。我尝试了两种技术,但都以不同的方式失败:
方法一:
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)
。如果它是相关的:这是在 Java 8 上,我正在使用 twelvemonkeys 库调整图像大小,然后再将其写入 JPEG。
我尝试了上述代码的多种变体,但没有成功。之前有很多问题建议使用上述代码,但在这种情况下似乎不起作用。
haraldk 是正确的,还有一段代码导致了 alpha 通道问题。方法 1 中的代码正确且有效。
但是,如果在剥离 alpha 通道后,您使用 twelvemonkeys ResampleOp 调整图像大小,如下所示:
BufferedImageOp resampler = new ResampleOp(width, height, ResampleOp.FILTER_TRIANGLE);
BufferedImage resizedImg = resampler.filter(rgbImage, null);
这会导致 alpha 通道变成粉红色。
解决方案是在剥离 Alpha 通道之前调整图像大小。
感谢 Rob 的回答,我们现在知道为什么颜色会乱七八糟了。
问题是双重的:
ImageIO
用于写入 JPEG 的默认JPEGImageWriter
不会以其他软件理解的方式写入带有 alpha 的 JPEG(这是一个已知问题)。- 当将
null
作为目标传递给ResampleOp.filter(src, dest)
且过滤方法为FILTER_TRIANGLE
时,将创建一个新的BufferedImage
,alpha(实际上,BufferedImage.TYPE_INT_ARGB
).
重采样后剥离 alpha 将起作用。但是,还有另一种方法可能会更快并节省一些内存。也就是说,不是传递 null
目的地,而是传递适当大小和类型的 BufferedImage
:
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"));
}