Java 如何将图像转换为灰度而不丢失透明度?

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

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

我定义了一个方法“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\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();