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