Java 如何将图像转换为灰度而不丢失透明度?
我在将带有一些透明像素的彩色图像转换为灰度时遇到问题。我已经在这个网站上搜索并找到了相关的问题,但是我没有能够用它来解决我的问题 我定义了一个方法“convertType”,如下所示:Java 如何将图像转换为灰度而不丢失透明度?,java,image,bufferedimage,grayscale,Java,Image,Bufferedimage,Grayscale,我在将带有一些透明像素的彩色图像转换为灰度时遇到问题。我已经在这个网站上搜索并找到了相关的问题,但是我没有能够用它来解决我的问题 我定义了一个方法“convertType”,如下所示: /*------------------------------ attempts to convert the type of the image argument to the chosen type for example: BufferedImage newImage= ImageUtilities.co
/*------------------------------
attempts to convert the type of the image argument to the chosen type
for example: BufferedImage newImage= ImageUtilities.convertType(oldImage, BufferedImage.TYPE_3BYTE_BGR);
11/28/2013 WARNING-it remains to be seen how well this method will work for various type conversions
------------------------------*/
public static BufferedImage convertType (BufferedImage image,int newType){
if (image.getType() == newType){
return (image);
}else {
int w= image.getWidth();
int h= image.getHeight ();
BufferedImage modifiedImage = new BufferedImage( w,h,newType);
Graphics2D g = ( Graphics2D)modifiedImage.getGraphics();
g.drawImage( image, null, 0,0);
g.dispose();
return (modifiedImage);
}
}
我从名为“result”的类型为type\u 4BYTE\u ABGR
的BuffereImage开始,然后:
对于原始图像中的彩色不透明像素,上述序列工作正常:
例如,(32,51,81255)->(49,49,49255)
但是,原始图像中的透明像素变为不透明:
例如,(0,0,0,0)->(0,0,0,255)
我理解正在发生的事情,如果我能利用用于转换为灰度的Java算法,我可以解决这个问题。我已经下载了源代码并四处搜索,但还没有找到算法。如果有人能:
您是否考虑过使用
ColorConvertOp
过滤器
例如
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorConvertOp;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestAlphaGrayScale {
public static void main(String[] args) {
new TestAlphaGrayScale();
}
public TestAlphaGrayScale() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage master;
private BufferedImage gray;
public TestPane() {
setBackground(Color.RED);
try {
master = ImageIO.read(new File("C:\\hold\\thumbnails\\Miho_Alpha.png"));
gray = new BufferedImage(master.getWidth(), master.getHeight(), BufferedImage.TYPE_INT_ARGB);
// Automatic converstion....
ColorConvertOp op = new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
op.filter(master, gray);
setLayout(new GridLayout(1, 2));
add(new JLabel(new ImageIcon(master)));
add(new JLabel(new ImageIcon(gray)));
} catch (IOException exp) {
exp.printStackTrace();
}
}
}
}
当我写这篇文章的时候,你得到了你的“怎么做到的”答案,所以我将补充一些关于发生了什么以及为什么你没有成功的解释 您可能知道,当图像以ARGB(TYPE_INT_ARGB)表示时,透明度存储在第四个字节(A-Alpha通道)中。A=0透明,A=255不透明 TYPE_BYTE_GRAY表示法只是带有亮度分量的单个字节,该表示法中没有Alpha通道。 将图像绘制到新缓冲区时,将从RGB通道计算亮度并存储。Alpha通道被丢弃 实现此操作的最简单方法是自己计算亮度,并将其存储在新ARGB图像的三个RGB通道中,将Alpha通道从原始图像复制到现在的灰度图像 亮度是YCbCr图像表示中的Y分量。详细介绍了如何计算它(它只是R、G和B的加权平均值)
这种方法的问题是你失去了硬件加速。幸运的是,Java提供了转换颜色空间的类(取决于平台),这些颜色空间应该是硬件加速的,而且更健壮,因为它们不依赖于输入图像是ARGB@MadProgrammer的详细回答说明了如何做到这一点。重要信息:如果您需要适当的灰度转换,您可能应该使用@MadProgrammers solution(
ColorConvertOp
)。我投了赞成票。:-)
这个答案更完整:
如果您确实想要一个具有CS\u灰色
颜色空间和透明度的buffereImage
,可以创建一个自定义(将导致TYPE\u custom
)buffereImage
。下面的代码将生成类似于“type2byte_GRAY_ALPHA”的类型(每个采样8位,每个像素2个采样)。它将使用TYPE_INT_ARGB
或TYPE_4BYTE_ABGR
映像的一半内存,但它很可能会失去硬件加速,并且可能会错过Java2D绘图管道中的一些优化循环(也就是说,我的测试显示在OS X上的性能完全可以接受)
由于只有位掩码支持透明度,请首先使用类型_BYTE_GRAY将图像转换为灰度,然后使用位标记将图像转换为具有透明度。希望这有帮助。我也有同样的问题,我用这种方法
//grey scale image
BufferedImage b = new BufferedImage(
colorImage.getWidth(),
colorImage.getHeight(),
BufferedImage.TYPE_BYTE_GRAY);
File lostFGFile = new File(lostFgFileName);
Graphics g = b.createGraphics();
g.drawImage(colorImage, 0, 0, null);
g.setColor(Color.gray);
ImageIO.write(b, "png", lostFGFile);
g.dispose();
//convert the lost image as transparent
BufferedImage greyImage = ImageIO.read(new FileInputStream(lostFgFileName));
b = new BufferedImage(
colorImage.getWidth(),
colorImage.getHeight(),
BufferedImage.BITMASK);
g = b.createGraphics();
g.drawImage(greyImage, 0, 0, null);
ImageIO.write(b, "png", lostFGFile);
g.dispose();
我之所以选择+1,是因为信息很有趣,而且通常很有用。如果整数是8字节长,那么前4个字节应该是0@Bionix1441你能解释一下你指的是什么吗。ARGB32中常用的ARGB表示(即每个通道一个字节的32位)。使用此表示法时,图像中的每个像素被压缩为32位。还有一个ARGB64表示,它使用8个字节,但每个通道是16位。我不明白你说的“前四个字节应该是0”是什么意思你好,ColorConvertOp过滤器工作得很好。我也非常感谢其他海报的评论。它将在我进一步的图像处理中非常有用。非常感谢,Joe PS-我想检查MADProgrammer的回答是否为“已回答”,但我不知道怎么做!?在“选票”下面应该有一个小的“半透明”勾号。只需单击使其变为绿色…请描述您的答案
/** Color Model usesd for raw GRAY + ALPHA */
private static final ColorModel CM_GRAY_ALPHA =
new ComponentColorModel(
ColorSpace.getInstance(ColorSpace.CS_GRAY),
true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE
);
public static BufferedImage create2ByteGrayAlphaImage(int width, int height) {
int[] bandOffsets = new int[] {1, 0}; // gray + alpha
int bands = bandOffsets.length; // 2, that is
// Init data buffer of type byte
DataBuffer buffer = new DataBufferByte(width * height * bands);
// Wrap the data buffer in a raster
WritableRaster raster =
Raster.createInterleavedRaster(buffer, width, height,
width * bands, bands, bandOffsets, new Point(0, 0));
// Create a custom BufferedImage with the raster and a suitable color model
return new BufferedImage(CM_GRAY_ALPHA, raster, false, null);
}
//grey scale image
BufferedImage b = new BufferedImage(
colorImage.getWidth(),
colorImage.getHeight(),
BufferedImage.TYPE_BYTE_GRAY);
File lostFGFile = new File(lostFgFileName);
Graphics g = b.createGraphics();
g.drawImage(colorImage, 0, 0, null);
g.setColor(Color.gray);
ImageIO.write(b, "png", lostFGFile);
g.dispose();
//convert the lost image as transparent
BufferedImage greyImage = ImageIO.read(new FileInputStream(lostFgFileName));
b = new BufferedImage(
colorImage.getWidth(),
colorImage.getHeight(),
BufferedImage.BITMASK);
g = b.createGraphics();
g.drawImage(greyImage, 0, 0, null);
ImageIO.write(b, "png", lostFGFile);
g.dispose();