20

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.

sayem siam
  • 1,281
  • 3
  • 13
  • 26
  • Possible duplicate - http://stackoverflow.com/questions/6471340/how-do-i-desaturate-a-bufferedimage-in-java/6471524#6471524 – mre Feb 03 '12 at 16:11
  • What does _"i can not set the grayscale values for this grayIm"_ mean? – tim_yates Feb 03 '12 at 16:12

3 Answers3

45

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.

vulkanino
  • 9,074
  • 7
  • 44
  • 71
29

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.

Martijn Courteaux
  • 67,591
  • 47
  • 198
  • 287
  • 2
    But does this reduce memory @Martijn Courteaux – sayem siam Feb 03 '12 at 16:26
  • 9
    Actually, a gray scale conversion is not a simple average of the R,G and B channels, as blue contributes the least to our brightness perception while green contributes the most. A weighted average is needed in order to have an accurate representation of light intensity in the grayscale image. Use 0.2126R + 0.7152G + 0.0722B – Mr.WorshipMe Dec 29 '15 at 13:11
  • @Mr.WorshipMe: Nice! I am aware of this phenomena. Any resource on your weights? – Martijn Courteaux Dec 29 '15 at 22:39
  • 1
    @MartijnCourteaux This is what OpenCV uses in cvtColor, and it also appears on the wikipedia article on grayscale. – Mr.WorshipMe Dec 30 '15 at 14:40
  • @MartijnCourteaux. How can you create a grayscale image frome only one channel like R. Is it enough to take only the rgb value of red for the new picture? – Jürgen K. Mar 09 '16 at 13:20
  • The coefficients Mr Worship used are the standards for Rec709 (HDTV) and also for sRGB (IEC standard). However, the RGB values need to be linearized first. The simple exponent method is shown in the answer, though the piecewise method in the IEC standard is usually recommended. If you are starting with three 8bit INT, and want one 8Bit INT in return, `//ANDY'S DOWN AND DIRTY GRAYSCALE™ gray=((Rs/255.0)**2.2*0.2126+(Gs/255.0)**2.2*0.7152+(Bs/255.0)**2.2*0.0722)**0.4545*255; ` – Myndex Dec 20 '21 at 03:17
2

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/

Jürgen K.
  • 3,427
  • 9
  • 30
  • 66