Reputation: 16928
In the Aforge.net/Accord.net library, the following test is performed to determine whether an image is grayscale:
public static bool IsGrayscale (Bitmap image)
{
bool ret = false;
// check pixel format
if (image.PixelFormat == PixelFormat.Format8bppIndexed)
{
ret = true;
// check palette
ColorPalette cp = image.Palette;
Color c;
// init palette
for ( int i = 0; i < 256; i++ )
{
c = cp.Entries[i];
if ((c.R != i) || (c.G != i) || (c.B != i))
{
ret = false;
break;
}
}
}
return ret;
}
Isn't it fallacious?
As far as the definition goes, a Grayscale image can be of any color depth except 1-bit pp. For instance, the following is a 32-bit grayscale image:
So, my question is, what is the proper way to test a Grayscale image?
Upvotes: 1
Views: 1306
Reputation: 5629
That code is checking for standard 8-bit grayscale where the pixel values correspond to their brightness. This is more or less a standard for grayscale, but it will indeed not match optimised palettes or anything like that.
I'm not sure why you would exclude 1bpp though. It's an indexed format like any other, and in fact has a colour palette just like 8bpp, meaning it is not even limited to pure black and white. This is a 1bpp grayscale version of that parrot with two gray values in its palette:
The simplest way to check indexed images is indeed to go over the palette and do the R=G=B test, but technically, even then, you could argue that the image is grayscale as long as any non-gray colours on the palette are not actually used on the image.
A sure-fire way is probably just to make LockBits convert the image to 32bppARGB, and then check the R, G and B on that. But even there you'll have to make choices... do 100% transparent pixels that don't match R=G=B make the image "not grayscale"?
Anyway, here would be the method I'd use:
public static Boolean IsGrayscale(Bitmap cur)
{
// Indexed format, and no non-gray colours in the images palette: immediate pass.
if ((cur.PixelFormat & PixelFormat.Indexed) == PixelFormat.Indexed
&& cur.Palette.Entries.All(c => c.R == c.G && c.R == c.B))
return true;
// Quick indexed check failed; actually check image data.
// Get bytes out of the image, converted to 32bpp ARGB
BitmapData curBitmapData = cur.LockBits(new Rectangle(0, 0, cur.Width, cur.Height),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
Int32 stride = curBitmapData.Stride;
Byte[] data = new Byte[stride * cur.Height];
Marshal.Copy(curBitmapData.Scan0, data, 0, data.Length);
cur.UnlockBits(curBitmapData);
// Go over all bytes per block of four.
Int32 curRowOffs = 0;
for (Int32 y = 0; y < cur.Height; y++)
{
// Set offset to start of current row
Int32 curOffs = curRowOffs;
for (Int32 x = 0; x < cur.Width; x++)
{
Byte b = data[curOffs];
Byte g = data[curOffs + 1];
Byte r = data[curOffs + 2];
Byte a = data[curOffs + 3];
// Increase offset to next colour
curOffs += 4;
if (a == 0)
continue;
if (r != g || r != b)
return false;
}
// Increase row offset
curRowOffs += stride;
}
return true;
}
Upvotes: 1
Reputation: 16928
Looks like I got my answer in this link.
If the image is a Gray Scale image then
if(R=G=B) //Grayscale
For more accurate results you can introduce some thresholds values. i.e
if((abs(R-G)< Threshold))// Threshold-> can be greater than zero. eg 0.006 //Grayscale
By this way you can get pretty good results.
But, I suspect, this procedure would be as slow as hell.
So, anyone with a better idea is welcome to answer.
Upvotes: 2