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

use*_*892 3 java bufferedimage image grayscale

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

我已经定义了一个方法"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);
}
}
Run Code Online (Sandbox Code Playgroud)

我从一个TYPE_4BYTE_ABGR名为"result" 的BufferedImage开始,然后:

 result= convertType (result, BufferedImage.TYPE_BYTE_GRAY);//transparency is lost
 result=convertType (result, BufferedImage.TYPE_INT_ARGB);//pixels will now support transparency
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

Mad*_*mer 6

你考虑过使用ColorConvertOp过滤器吗?

例如...

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

}
Run Code Online (Sandbox Code Playgroud)


Eli*_*nti 6

在我写这篇文章的时候,你得到了你的"怎么做"的答案,所以我只想对正在发生的事情以及你没有成功的原因加上一些解释.

您可能知道何时以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的详细答案显示了如何做到这一点.


har*_*ldK 6

重要提示ColorConvertOp:如果您想要正确的灰度转换,您可能应该使用@MadProgrammers解决方案( )。我对此投了赞成票。:-)

这个答案更加完整:

如果您确实想要BufferedImage具有CS_GRAY颜色空间和透明度的 ,则可以创建自定义(将导致TYPE_CUSTOMBufferedImage。下面的代码将生成类似“TYPE_2BYTE_GRAY_ALPHA”类型的内容(每个样本 8 位,每个像素 2 个样本)。TYPE_INT_ARGB它将使用或图像的一半内存TYPE_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);
}
Run Code Online (Sandbox Code Playgroud)