I'm trying to figure out whether an image is colored or not. On this StackOverflow question, there's a reply that says that I should check the PixelFormat
enum of the Image
. Unfortunately, the reply isn't very clear to me. Is it safe to check whether the image.PixelFormat
is different from PixelFormat.Format16bppGrayScale
to consider that it is a colored image? What about the other values of the enumeration? The MSDN documentation isn't very clear...
-
Would you like to check if it supports colors, or if it contains colors? It can be Format32bppArgb and still contain only black, white and gray. – SimpleVar Apr 26 '12 at 13:01
-
@YoryeNathan: I want to know whether it contains colors. – Kassem Apr 26 '12 at 13:01
-
That's a different thing, then. Do you want it to work for every PixelFormat? – SimpleVar Apr 26 '12 at 13:02
-
@YoryeNathan: I'm not really sure to be honest. The thing is that the user can upload an image to the site, then I need to check whether this image is colored or not. Accordingly, I need to do different things... – Kassem Apr 26 '12 at 13:04
-
The user uploads an image of any type, then? There's a file dialog box, etc? No extension filter? – SimpleVar Apr 26 '12 at 13:08
-
There is a GetPixel method on Bitmap. http://msdn.microsoft.com/en-us/library/system.drawing.bitmap.getpixel.aspx you could then check the RGB values of every pixel but that kind of sucks. – Andrew Jones Apr 26 '12 at 13:08
-
@YoryeNathan: No, I will restrict it to jpg, png and gif images. – Kassem Apr 26 '12 at 13:11
-
@AndrewJones: Yeh well this is what I'm trying to avoid... – Kassem Apr 26 '12 at 13:12
3 Answers
You can improve this by avoiding Color.FromArgb, and iterating over bytes instead of ints, but I thought this would be more readable for you, and easier to understand as an approach.
The general idea is draw the image into a bitmap of known format (32bpp ARGB), and then check whether that bitmap contains any colors.
Locking the bitmap's bits allows you to iterate through it's color-data many times faster than using GetPixel, using unsafe code.
If a pixel's alpha is 0, then it is obviously GrayScale, because alpha 0 means it's completely opaque. Other than that - if R = G = B, then it is gray (and if they = 255, it is black).
private static unsafe bool IsGrayScale(Image image)
{
using (var bmp = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppArgb))
{
using (var g = Graphics.FromImage(bmp))
{
g.DrawImage(image, 0, 0);
}
var data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
var pt = (int*)data.Scan0;
var res = true;
for (var i = 0; i < data.Height * data.Width; i++)
{
var color = Color.FromArgb(pt[i]);
if (color.A != 0 && (color.R != color.G || color.G != color.B))
{
res = false;
break;
}
}
bmp.UnlockBits(data);
return res;
}
}

- 14,044
- 4
- 38
- 60
-
Glad I helped. Just make sure I didn't confuse with the alpha (0 or 255). Other than that it should go smooth :) – SimpleVar Apr 27 '12 at 14:52
private bool isGrayScale(Bitmap processedBitmap)
{
bool res = true;
unsafe
{
System.Drawing.Imaging.BitmapData bitmapData = processedBitmap.LockBits(new Rectangle(0, 0, processedBitmap.Width, processedBitmap.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, processedBitmap.PixelFormat);
int bytesPerPixel = System.Drawing.Bitmap.GetPixelFormatSize(processedBitmap.PixelFormat) / 8;
int heightInPixels = bitmapData.Height;
int widthInBytes = bitmapData.Width * bytesPerPixel;
byte* PtrFirstPixel = (byte*)bitmapData.Scan0;
Parallel.For(0, heightInPixels, y =>
{
byte* currentLine = PtrFirstPixel + (y * bitmapData.Stride);
for (int x = 0; x < widthInBytes; x = x + bytesPerPixel)
{
int b = currentLine[x];
int g = currentLine[x + 1];
int r = currentLine[x + 2];
if (b != g || r != g)
{
res = false;
break;
}
}
});
processedBitmap.UnlockBits(bitmapData);
}
return res;
}

- 23
- 4
-
2Please add some context to your answer and explain what the code does. https://stackoverflow.com/help/how-to-answer – jmdon Jul 19 '18 at 12:22
SimpleVar's answer is mostly correct: that code doesn't properly handle when the source image has an indexed color format.
To solve this, simply replace the outer using
block with:
using (var bmp = new Bitmap(image)) {
and remove the inner using
entirely, as the Graphics
object is no longer needed. This will create a perfect copy of the image in a non-indexed format, regardless of the original image's pixel format.

- 31
- 4