How to create grayscale images on .NET
In this example we will use simple color recalculation method to grayscale images. For each pixel on image we will calculate "average color" that we write back on image. Average color is sum of current color’s red, green and blue channels integer value divided by three. This value is assigned to all three channels for current pixel.
Colored picture of rabbit commiting
the crime.
Although grayscaled she is still on action.
The cool thing about this rabbit is her colors – black, white and gray. As you can see rabbit colors are almost the same on both pictures. Okay, let’s see the code.
public Bitmap GrayScale(Bitmap Bmp)
{
int rgb;
Color c;
for (int y = 0; y < Bmp.Height; y++)
for (int x = 0; x < Bmp.Width; x++)
{
c = Bmp.GetPixel(x, y);
rgb = (int)((c.R + c.G + c.B) / 3);
Bmp.SetPixel(x, y, Color.FromArgb(rgb, rgb, rgb));
}
return Bmp;
}
Some notes about this code. It is also possible to calculate grayscale colors for channels differently. Instead of average of channels colors we can also use:
- maximum of channel values,
- minimum of channel values,
- custom divider.
It depends purely on context what one may need. Channel values maximum is good for pictures that are little bit too dark. Minimum is good for pictures that are little bit too bright. Custom divider may find usage in applications where user may want to change brightness manually. Current example by itself is goof for original pictures that are okay enough to publish them.
I know there are faster methods available for grayscaling. These methods are usually based on unmanaged code. This example here is slower but it is managed code you can use safely in your applications.
The fastest method in pure managed code is to use a ColorMatrix:
http://www.codeproject.com/…/colormatrix.asp
Or use unsafe code by locking the bitmap object and retrieving the pointer in memory. The colormatrix through gdi is the fastest as this also can be done accellerated.
By the way… you are doing a very rude color to grey conversion. You should weight the color components differently.
how to 8-bit bmp to 8-bit jpg??thanks!
There’s a problem in not weighting the different colours, since neither the human eye nor display tech works that way. This works well in the example image, but not as well as in others.
Changing
rgb = (int)((c.R + c.G + c.B) / 3);
to:
rgb = (int)(sqrt( 0.241 * c.R * c.R + 0.691 * c.G * c.G + 0.068 * c.B * c.B ));
Will give a much more accurate version, though we can do pretty well much faster with:
rgb = (int)(0.299 * c.R + 0.587 * c.G + 0.114 * c.B);
Perhaps the most commonly seen is the slight rounding of this:
rgb = (int)(0.3 * c.R + 0.59 * c.G + 0.11 * c.B);
Which in practice is going to come out much the same.