5

I am currently working on one use case where i need to determine if uploaded image is Grey Scale or RGB. I found couple of ways to identify this, but not sure if they are reliable and can be used collectively to confirm image is grey scale or not.

Part 1: Read Image and get NumberDataElements using Raster.

BufferedImage image = ImageIO.read(file);
        Raster ras = image.getRaster();
        int elem = ras.getNumDataElements();

I observed value of elem is "1" in some cases, but not in all.

Part 2: Check RGB value of each pixel. If R , G, B value is same of given pixel.

BufferedImage image = ImageIO.read(file);
        Raster ras = image.getRaster();

        //Number of Color elements
        int elem = ras.getNumDataElements();

        int width = image.getWidth();
        int height = image.getHeight();

        int pixel,red, green, blue;

        for (int i = 0; i < width; i++)
            for (int j = 0; j < height; j++) {
                //scan through each pixel
                pixel = image.getRGB(i, j);
                red = (pixel >> 16) & 0xff;
                green = (pixel >> 8) & 0xff;
                blue = (pixel) & 0xff;

                //check if R=G=B
                if (red != green || green != blue ) {
                    flag = true;
                    break;
                }


            }

Here i check R, G,B values are same for any given pixel and this behavior is consistent across all pixels.

I am using these 2 approaches, but not sure how accurate they are. Kindly suggest..

Dark Knight
  • 8,218
  • 4
  • 39
  • 58
  • 2
    `red != green || green != blue || red != blue` Only two comparisons should be necessary. Furthermore, you are calling "break" twice, the second is redundant. – leonbloy Mar 22 '16 at 14:06
  • Although "gray" in RGB means R = G = B, there can be some variations, e. g. if an image has an embedded color profile, or is restored from jpeg. I believe the more reliable, but more slow way is to convert pixels to HSV and check if S (saturation) is close to 0. – Alex Salauyou Mar 22 '16 at 14:08
  • @SashaSalauyou as I understand it even if the image WAS jpeg and used a color table this code will still work as the image has been decoded and getRGB is independent of the source format. – Elemental Mar 22 '16 at 14:14
  • @Elemental yes, sure. Treat my comment as a way to determine the image "looking greyscale", not "greyscale" – Alex Salauyou Mar 22 '16 at 14:16
  • @leonbloy They have two loops, but their breaks are not correctly placed. Dark Knight it seems odd that an image would be stored as RGB but actually be a greyscale image. Did you consider checking BufferedImage#getType? – matt Mar 22 '16 at 14:21
  • How will the image be created, and how much control will you have over that? If a grayscale sheet of paper is scanned as 24-bit color, you might get color borders at transitions between dark and light gray, even though the image will look grayscale. Because of this, can you instead convert the image to grayscale rather than test for it, or do you need to know whether it is grayscale rather than just provide a guarantee that it is grayscale? – Matt Jordan Mar 22 '16 at 15:25

6 Answers6

2

Move your if (flag) { break; } line outside of the inner for loop.

And you only need to check that (red != green || green != blue). Breaking any one of these two equalities ensures that the third MUST be broken, so you only require two checks.

I'd also possibly just set an isGrayscale variable of boolean to true and then set it to false, when the equality logic breaks, rather than setting a flag to true. It should be assumed to be grayscale, until it breaks and becomes false. No problem with what you have here with flag, but this is a little more meaningful and intuitive.

If you want to get really clever, you could allow for a delta of variance to allow for images that are SUFFICIENTLY grayscale for purpose i.e. their deviance from equality is lower than a set barrier. But this works as it is :)

ManoDestra
  • 6,325
  • 6
  • 26
  • 50
1

Below approach is worked for me. Thanks guys for help.

BufferedImage image = ImageIO.read(file);
        Raster ras = image.getRaster();

        //Number of Color elements
        int elem = ras.getNumDataElements();

        int width = image.getWidth();
        int height = image.getHeight();

        int pixel,red, green, blue;

        for (int i = 0; i < width; i++)
            for (int j = 0; j < height; j++) {
                //scan through each pixel
                pixel = image.getRGB(i, j);
                red = (pixel >> 16) & 0xff;
                green = (pixel >> 8) & 0xff;
                blue = (pixel) & 0xff;

                //check if R=G=B
                if (red != green || green != blue ) {
                    flag = true;
                    break;
                }


            }
Dark Knight
  • 8,218
  • 4
  • 39
  • 58
0

I think your second option is a reliable and correct method of proving an image is greyscale. Your code has some problems: * you don't break out of the outer loop the way you intended to (look carefully at the second break - I think it should be in the outer loop but NOT the inner loop). * as leonbloy explains in his comment your comparison could be simpler

But if you fix these small problems it should work reliably.

Elemental
  • 7,365
  • 2
  • 28
  • 33
0

Checking R=G=B will tell you if an image is greyscale, thats for sure. But i would be very careful with that approach. You dont know where the images came from. They could be saved with lossy compression or some other strange format. I dont know if formats like jpg actually colorshift greyscale pixels, but that might also be dependant on the compression algorithm (and thus the program used to save the image). Anyway i would suggest you convert images to greyscale by yourself just to be sure. At least for those images that fail the R=G=B test.

For your algorithm i strongly suggest you create a new function for checking R=G=B. This way, if you found a pixel that fails the test you can return false immediately.

public static boolean isGreyscale(BufferedImage image)
{
    int pixel,red, green, blue;
    for (int i = 0; i < width; i++)
    {
        for (int j = 0; j < height; j++) 
        {
            pixel = image.getRGB(i, j);
            red = (pixel >> 16) & 0xff;
            green = (pixel >> 8) & 0xff;
            blue = (pixel) & 0xff;
            if (red != green || green != blue ) return false;
        }
    }
    return true;
}

PS: I just checked the compression colorshift thing. I cant archieve color shifting with pohotohop and jpg format. But its possible to save a greyscale image as gif in such a way, that its not exactly greyscale anymore.

Mario Dekena
  • 843
  • 6
  • 20
0

The question is : do you want the image itself to be grayscale, or the encoding ?

Your second solution tells you whether the image is grayscale or not, regardless of the encoding (i.e, it returns true even the image could potentially have colors, but just doesn't). It isn't perfect however, one could perfectly imagine a situation where the image is grayscale in some color space different than RGB, and a rounding error makes your test fail. Or a lossy encoding. You should add a margin of error and convert to proper grayscale any image that is close enough.

Your first solution is an imperfect attempt at finding out whether the encoding is grayscale. An image with a color palette of size 255 would also give you elem=1, and a grayscale image can have elem=2 if it has an alpha channel.

In order to check if your encoding is grayscale, I suggest the following test :

int type = image.getColorModel().getColorSpace().getType();
boolean grayscale = (type==ColorSpace.TYPE_GRAY || type==ColorSpace.CS_GRAY);

To do this, you will need to import the classes ColorModel and ColorSpace from java.awt.image and java.awt.color.

You could also investigate whether image.getType() has value BufferedImage.TYPE_BYTE_GRAY or BufferedImage.TYPE_USHORT_GRAY.

A.N.
  • 320
  • 1
  • 8
0

Here is a very simple way:

  1. Test the image type
  2. Test the image number of channels
  3. Test the pixels values.

Here is the code

boolean isGrayScale(BufferedImage image)
    {
    // Test the type
    if ( image.getType() == BufferedImage.TYPE_BYTE_GRAY ) return true ;
    if ( image.getType() == BufferedImage.TYPE_USHORT_GRAY ) return true ;
    // Test the number of channels / bands
    if ( image.getRaster().getNumBands() == 1 ) return true ; // Single channel => gray scale

    // Multi-channels image; then you have to test the color for each pixel.
    for (int y=0 ; y < image.getHeight() ; y++)
    for (int x=0 ; x < image.getWidth() ; x++)
        for (int c=1 ; c < image.getRaster().getNumBands() ; c++)
            if ( image.getRaster().getSample(x, y, c-1) != image.getRaster().getSample(x, y, c) ) return false ;

    return true ;
    }
FiReTiTi
  • 5,597
  • 12
  • 30
  • 58