Many useful image processing concepts are simpler and faster without color information, so now that you have a solid grasp of color, let’s remove it and take a look at the world in grayscale.
Conversion
Grayscale is a measure of the intensity of the total light at a given point. Because the ranges for red, green, and blue in the WriteableBitmap are from 0 to 255 it is convenient for us to set the grayscale ranges similarly where the absence of light (black) is 0 and maximum intensity (white) is 255. We just need a method to extract this intensity from the color values.
I always go with my gut and it says we should be able to average the color intensities. Time to put on the coding gloves.
WriteableBitmap ToAverageGrayscale(WriteableBitmap bmp)
{
WriteableBitmap grayscale =
new WriteableBitmap(bmp.PixelWidth, bmp.PixelHeight);
for (int pixelIndex = 0;
pixelIndex < bmp.Pixels.Length;
pixelIndex++)
{
int pixelColor = bmp.Pixels[pixelIndex];
byte red = (byte)((pixelColor & 0xFF0000) >> 16);
byte green = (byte)((pixelColor & 0xFF00) >> 8);
byte blue = (byte)(pixelColor & 0xFF);
byte intensity = (byte)((red + green + blue) / 3);
//notice how we repeat the intensity
//across each color channel
//this makes the output "color neutral"
//i.e. gray
grayscale.Pixels[pixelIndex] = 255 << 24
| intensity << 16
| intensity << 8
| intensity;
}
return grayscale;
}
Easy peasy lemon squeezy.
This looks like mission accomplished to me.
However, we are going from over 16 million colors to 256 intensities. Obviously we are going to lose information. How do we know that we are keeping the best information?
Any three numbers can represent six separate colors, but only one intensity. the following example uses the numbers 255, 128, and 64:
Simply averaging the numbers will work fine in many situations, but in this situation it breaks down.
If we are trying to approximate the brightness a human perceives, mimicking human physiology is a better approach. In the last lesson, the Bayer filter had twice as many green filters as red or blue because humans are more sensitive to green frequencies. Green colors should then appear brighter than other colors right?
We can modify our code to be slightly more intelligent by weighting the average toward the green colors.
WriteableBitmap ToWeightedGrayscale(
WriteableBitmap bmp,
double redWeight,
double greenWeight,
double blueWeight)
{
WriteableBitmap grayscale =
new WriteableBitmap(bmp.PixelWidth, bmp.PixelHeight);
for (int pixelIndex = 0;
pixelIndex < bmp.Pixels.Length;
pixelIndex++)
{
int pixelColor = bmp.Pixels[pixelIndex];
byte red = (byte)((pixelColor & 0xFF0000) >> 16);
byte green = (byte)((pixelColor & 0xFF00) >> 8);
byte blue = (byte)(pixelColor & 0xFF);
//notice that we are dividing by the sum of the weights
//this can be avoided by ensuring the weights add to 1
byte intensity = (byte)((red * redWeight
+ green * greenWeight
+ blue * blueWeight)
/ (redWeight + greenWeight + blueWeight));
grayscale.Pixels[pixelIndex] =
255 << 24
| intensity << 16
| intensity << 8
| intensity;
}
return grayscale;
}
Weights that work well for human vision are red = .2, green = .7, and blue =.1 (rounded from Frequently Asked Questions about Color by Charles Poynton).
As you should expect, the green colors are brighter. If you want a grayscale image to match the perceived brightness of the color image, this technique does the job. Is it better than the simple average? In our contrived example, yes. In many cases, the simple average will suffice. Here is the grayscale orangutan using both techniques. Can you guess which is simple and which is weighted?
(hint: the bottom one is weighted)
Summary
Bottom line: there is no “right” answer. If you’re trying to make your grayscale image “look” right, the weights used above may be a good path. If speed is your game, you might try just using the green channel alone. No technique is right for every situation. Remember, our end goal is for the phone, not a person, to understand the image. Have fun and play around with the numbers. Think about the advantages and disadvantages of different techniques.
I highly recommend the Frequently Asked Questions about Color by Charles Poynton. Warning: some of it is pretty math intensive.
Download Code
Up next: Intro to Histograms