如何在不失透明度的情况下将图像转换为灰度?

我在将带有透明像素的彩色图像转换为灰度时遇到问题。 我在这个网站上搜索并发现了相关的问题,但我没有能够用来解决我的问题。

我已经定义了一个方法“convertType”,如下所示:

/*------------------------------ 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_4BYTE_ABGR类型的BufferedImage开始,然后:

  result= convertType (result, BufferedImage.TYPE_BYTE_GRAY);//transparency is lost result=convertType (result, BufferedImage.TYPE_INT_ARGB);//pixels will now support transparency 

对于原始图像中的彩色不透明像素,上述序列工作正常:例如,(32,51,81,255) – >(49,49,49,255)

但是,原始图像中的透明像素变为不透明:例如,(0,0,0,0) – >(0,0,0,255)

我理解发生了什么,如果我可以利用用于转换为灰度的Java算法,我可以解决这个问题。 我已经下载了源代码并且四处寻找,但是无法找到算法。 如果有人可以,我将非常感激:

  1. 告诉我哪个类包含灰度转换算法或
  2. 建议另一种方法来完成我想要做的事情。

您是否考虑过使用ColorConvertOpfilter?

例如…

AlphaGray

 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解决方案( ColorConvertOp )。 我投了赞成票。 🙂

这个答案更完整:

如果你真的想要一个具有CS_GRAY颜色空间和透明度的BufferedImage ,可以创建一个自定义(将导致TYPE_CUSTOMBufferedImage 。 下面的代码将类似于“TYPE_2BYTE_GRAY_ALPHA”类型(每个样本8位,每个像素2个样本)。 它将使用TYPE_INT_ARGBTYPE_4BYTE_ABGR图像的一半内存,但它很可能会丢失硬件加速,并且可能会错过Java2D绘图管道中的一些优化循环(也就是说,我的测试显示OS X上的完全可接受的性能)。

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

由于只有BITMASK支持透明度,首先使用TYPE_BYTE_GRAY将图像转换为greyScale,然后使用BITMARK将图像转换为透明度。 希望这可以帮助。 我使用这种方法遇到了同样的问题。

 //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();