how can I convert an image to grayscale without losing transparency?

一世执手 提交于 2019-12-30 10:36:49

问题


I am having problems converting a colored image with some transparent pixels to grayscale. I have searched and found related questions on this website but nothing I have been able to use to solve my problem.

I have defined a method "convertType" as follows:

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

I start with a BufferedImage of type TYPE_4BYTE_ABGR named "result", then:

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

For colored opaque pixels in the original image the above sequence works fine: for example, (32, 51, 81, 255)-> (49, 49, 49, 255)

However, transparent pixels in the original image are turned opaque: for example, (0, 0, 0, 0)-> (0, 0, 0, 255)

I understand what is happening and I can work around the problem if I can make use of the Java algorithm used to convert to grayscale. I have downloaded the source code and poked around but have not been able to locate the algorithm. I would greatly appreciate it if someone could either:

  1. tell me what class contains the grayscale conversion algorithm or
  2. suggest an alternative way to accomplish what I am trying to do.

回答1:


Have you considered using a ColorConvertOp filter?

For example...

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

}



回答2:


While I was writing this you got your "how it is done" answer, so I'll just add some explanations on what is happening and why you did not succeed.

You probably know when an image is represented in ARGB (TYPE_INT_ARGB) transparency is stored in the fourth byte (the A - Alpha channel). A = 0 transparent, A = 255 opaque.

The TYPE_BYTE_GRAY representation is just a single byte with the luminance component, there is no Alpha channel in this representation. When you draw the image into the new buffer, the luminance is computed from the RGB channels and stored. The Alpha channel is dropped.

The easiest way to achieve what you are doing is to compute the luminance yourself and store it in the three RGB channels of a new ARGB image copying the Alpha channel from the original to the now gray-scale image.

The luminance is the Y component in the YCbCr image representation. Wikipedia has details on how to compute it (it's just a weighted average of R, G and B).

The problem with this approach is that you lose hardware acceleration. Luckily Java provides classes to translate color spaces which (depending on platform) should be hardware accelerated, and more robust since they don't depend on the input image being ARGB. @MadProgrammer's detailed answer shows how to do this.




回答3:


Important: You should probably use @MadProgrammers solution (ColorConvertOp) if proper gray scale conversion is what you want. I voted for that. :-)

This answer is more for completeness:

If you really want a BufferedImage that has CS_GRAY color space and transparency, it's possible to create a custom (will result in TYPE_CUSTOM) BufferedImage. The code below will make something like a "TYPE_2BYTE_GRAY_ALPHA" type (8 bits per sample, 2 samples per pixel). It will use half the memory of a TYPE_INT_ARGB or TYPE_4BYTE_ABGR image, but it will most likely lose hardware acceleration, and possibly miss some optimized loops in the Java2D drawing pipelines (that said, my testing shows fully acceptable performance on 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);
}



回答4:


As only BITMASK support transparency, first convert the image to greyScale using TYPE_BYTE_GRAY, then use BITMARK to convert the image to have transparency. Hope this helps. I had the same problem I used this approach.

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


来源:https://stackoverflow.com/questions/21176754/how-can-i-convert-an-image-to-grayscale-without-losing-transparency

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!