18
votes

I have an RGB bufferedImage bImg .
I want to convert bImg to gray image.

BufferedImage grayIm=new BufferedImage(bImg.getWidth(null), bImg.getHeight(null), BufferedImage.TYPE_BYTE_GRAY); 

I have tried this grayIm but i can not set the grayscale values for this grayIm.

3
Possible duplicate - stackoverflow.com/questions/6471340/…mre
What does "i can not set the grayscale values for this grayIm" mean?tim_yates

3 Answers

43
votes

One way could be to convert the color space (poor performance):

ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);  
ColorConvertOp op = new ColorConvertOp(cs, null);  
BufferedImage image = op.filter(bufferedImage, null);

Another would be to use a BufferedImage, as you do (better performance):

BufferedImage image = new BufferedImage(width, height,  
    BufferedImage.TYPE_BYTE_GRAY);  
Graphics g = image.getGraphics();  
g.drawImage(colorImage, 0, 0, null);  
g.dispose();  

Last but not least, the best performance is using a GrayFilter:

ImageFilter filter = new GrayFilter(true, 50);  
ImageProducer producer = new FilteredImageSource(colorImage.getSource(), filter);  
Image mage = Toolkit.getDefaultToolkit().createImage(producer);  

source: http://www.codebeach.com/2008/03/convert-color-image-to-gray-scale-image.html

edit: per Mark's comment.

26
votes

NOTE: This is not what the OP asked for (since it does not reduce the memory usage), but I'll leave it here, since people like this manual per-pixel approach. Instead I'll show how to accurately calculate a grayscale color.


This is quite simple. The idea is to iterate over each pixel of the image, and change it to its grayscale equivalent.

public static void makeGray(BufferedImage img)
{
    for (int x = 0; x < img.getWidth(); ++x)
    for (int y = 0; y < img.getHeight(); ++y)
    {
        int rgb = img.getRGB(x, y);
        int r = (rgb >> 16) & 0xFF;
        int g = (rgb >> 8) & 0xFF;
        int b = (rgb & 0xFF);

        // Normalize and gamma correct:
        float rr = Math.pow(r / 255.0, 2.2);
        float gg = Math.pow(g / 255.0, 2.2);
        float bb = Math.pow(b / 255.0, 2.2);

        // Calculate luminance:
        float lum = 0.2126 * rr + 0.7152 * gg + 0.0722 * bb;

        // Gamma compand and rescale to byte range:
        int grayLevel = (int) (255.0 * Math.pow(lum, 1.0 / 2.2));
        int gray = (grayLevel << 16) + (grayLevel << 8) + grayLevel; 
        img.setRGB(x, y, gray);
    }
}

However, this does not reduce memory. To effectively reduce the memory usage, do the same process but use a grayscale BufferedImage as output.

2
votes

I have had the same issue. The solution that you choose not only depends on the performance level. It is also necessary to understand whick image quality you strive for. Please look at these examples. They all maintain the source code. http://codehustler.org/blog/java-to-create-grayscale-images-icons/