0

I am trying to create a program that applies a grayscale filter over a chosen image for my computer science class. I found the following code in a tutorial, it demonstrates the grayscale algorithm where the R, G, and B values of every pixel in the image is replaced with the average of the RGB value.

import java.io.File;
import java.io.IOException;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;

public class Grayscale{
public static void main(String args[])throws IOException{
BufferedImage img = null;
File f = null;

//read image
try{
  f = new File("D:\\Image\\Taj.jpg");
  img = ImageIO.read(f);
}catch(IOException e){
  System.out.println(e);
}

//get image width and height
int width = img.getWidth();
int height = img.getHeight();

//convert to grayscale
for(int y = 0; y < height; y++){
  for(int x = 0; x < width; x++){
    int p = img.getRGB(x,y);

    int a = (p>>24)&0xff;
    int r = (p>>16)&0xff;
    int g = (p>>8)&0xff;
    int b = p&0xff;

    //calculate average
    int avg = (r+g+b)/3;

    //replace RGB value with avg
    p = (a<<24) | (avg<<16) | (avg<<8) | avg;

    img.setRGB(x, y, p);
  }
}

//write image
try{
  f = new File("D:\\Image\\Output.jpg");
  ImageIO.write(img, "jpg", f);
}catch(IOException e){
  System.out.println(e);
}
}//main() ends here
}//class ends here

The problem is, the program does not properly apply the grayscale filter over certain images. For example, the code can properly apply a filter over this image, creating a grayscale image. But the following image of a rainbow looks like this with the grayscale filter applied to it.

Why are red, green, blue, and pink showing with the filter over it? My understanding is that when the R, G, and B values of a pixel are the same, a gray colour should be created?

  • I'm not sure what the problem is with the algorithm, but have consider using a `ColorConvertOp` instead? [As demonstrated here](https://stackoverflow.com/questions/21176754/how-can-i-convert-an-image-to-grayscale-without-losing-transparency/21176863#21176863) – MadProgrammer May 09 '18 at 01:48
  • Is it possible you are using an indexed color image instead of a rgb image? – Shadowzee May 09 '18 at 02:57
  • @MadProgrammer thank you for the suggestion, I'll see if I can use that! But I'm still curious about why the algorithm isn't working. – user9761831 May 09 '18 at 03:41
  • @Shadowzee how do I check if it's an indexed color image? (Sorry about the noob question) – user9761831 May 09 '18 at 03:41
  • @user9761831 I honestly have no Idea – Shadowzee May 09 '18 at 06:24
  • calling bufferedImage.getType() will return the type that Java has selected for your image. Outside of Java you can open your image in a tool such as Photoshop or Gimp and it will indicate if its RGB or indexed. All GIFs are indexed. PNGs can be indexed or non-indexed. All JPEGs are non-indexed – slipperyseal May 09 '18 at 06:27
  • @slipperyseal I called getType() and it returned 13. It's an indexed image. – user9761831 May 09 '18 at 14:33

1 Answers1

0

From the JavaDoc of BufferedImage.setRGB()

"Sets a pixel in this BufferedImage to the specified RGB value. The pixel is assumed to be in the default RGB color model, TYPE_INT_ARGB, and default sRGB color space. For images with an IndexColorModel, the index with the nearest color is chosen."

To solve this, create a new BufferedImage with the required color space, the same dimensions as the original image, and write the pixels to that, not back to the original BufferedImage.

BufferedImage targetImage = new BufferedImage(img.getWidth(),
        img.getHeight(),  BufferedImage.TYPE_3BYTE_BGR);

write the pixels to this image instead...

targetImage.setRGB(x, y, p);

then save this new image..

ImageIO.write(targetImage, "jpg", f);

As a note, the more accurate way to convert a colour image to grey scale is to convert the RGB pixels to YUV colour space and then use the luminance value, rather than the average of RGB. This is because the brightness of R G and B are weighted differently.

slipperyseal
  • 2,728
  • 1
  • 13
  • 15
  • it seems to work when I make the new image type TYPE_3BYTE_BGR (which is the same type created when loading a JPG). Jpegs don't have an alpha channel anyway. I would have expected it to work with other types though. Maybe its something to do with us using setRGB rather than using a drawing context. – slipperyseal May 09 '18 at 23:33