Now that we’ve finally manipulated a few images we’re starting to get somewhere. Today we are going to take some strides in classifying images. It would be nice if we had an objective way to say “that image is really bright”. It shouldn’t come as a surprise that this is actually pretty easy.
What is a Histogram?
Histogram is really just a fancy word for counting. In our case, we are counting how many pixels are at each intensity level between black and white. Let’s revisit our friendly orangutan.
The chart below the picture is his histogram. The horizontal-axis represents the intensity while the vertical-axis represents the number of pixels with that intensity.
Creating the Histogram
When you are converting an image to grayscale you can trivially create the histogram data with it. The process is as simple as looping through every pixel (which we have to do to convert to grayscale anyway) and adding up the intensities.
//we are going to use the
//average of the r, g, and b
//values for our grayscale conversion
WriteableBitmap ToAverageGrayscale(WriteableBitmap bmp)
{
WriteableBitmap grayscale =
new WriteableBitmap(bmp.PixelWidth, bmp.PixelHeight);
//there are 256 intensities
//so a simple array of the ints
//should suffice for holding the counts
int[] histogramData = new int[256];
//we will use this to determine
//what the number of the highest
//occurring intensity is
//we are simply going to use this value
//to scale our plot
int maxCount = 0;
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);
//this is not rocket science
byte intensity = (byte)((red + green + blue) / 3);
//once again we repeat the intensity
grayscale.Pixels[pixelIndex] = 255 << 24
| intensity << 16
| intensity << 8
| intensity;
//simply add another to the count
//for that intensity
histogramData[intensity]++;
if (histogramData[intensity] > maxCount)
{
maxCount = histogramData[intensity];
}
}
//this would typically be a completely
//separate function, but it’s easier
//in this case to perform the operation
//with the grayscale conversion
PlotHistogram(histogramData, maxCount);
return grayscale;
}
public void PlotHistogram(int[] counts, int maxCount)
{
//in MainPage we have a canvas that will
//hold the histogram rectangles
HistogramPlot.Children.Clear();
//our tallest rectangle will be the height
//of the canvas, so we want to scale the
//them down from there
double scale = HistogramPlot.Height / maxCount;
//in our case we will always have 256 counts
//but better safe than sorry
double rectWidth = HistogramPlot.Width / counts.Length;
for (int intensity = 0; intensity < counts.Length; intensity++)
{
//there is no magic going on here
//for each intensity we are going to build
//a rectangle with a height proportional to
//its count
Rectangle histRect = new Rectangle();
histRect.Width = rectWidth;
histRect.Height = counts[intensity] * scale;
histRect.SetValue
(Canvas.LeftProperty,
intensity * rectWidth);
histRect.SetValue
(Canvas.TopProperty,
HistogramPlot.Height – histRect.Height);
//the gradient isn’t necessary
//it just gives the fill a little
//softer look
LinearGradientBrush rectFill = new LinearGradientBrush();
rectFill.GradientStops.Add(
new GradientStop() {
Color =
Color.FromArgb(
255,
(byte)intensity,
(byte)intensity,
(byte)intensity),
Offset = 0 });
rectFill.GradientStops.Add(new GradientStop() {
Color = Color.FromArgb(
255,
(byte)(.25 * intensity),
(byte)(.25 * intensity),
(byte)(.25 * intensity)),
Offset = 1 });
histRect.Fill = rectFill;
//finally, let’s add it to the canvas
HistogramPlot.Children.Add(histRect);
}
}
It really is pretty simple.
What now?
The histogram offers us an objective method for classifying images. For instance, based on the histogram below we might say the crane is not too dark and not too light. Because it doesn’t use the entire range, however, we might classify this as having low contrast. Trust me, this will come in handy in future lessons.
Summary
Creating a histogram is really easy, and it gives us some basic statistics. It doesn’t take a wild imagination to come up with some ways to use this information. Over the next few lessons we will do just that.
Download Code
Up next: Contrast Stretching
[…] Up next: Intro to Histograms […]
[…] Vision – 09 Contrast Stretching 1 02 2011 Now that we have a histogram, it’s time to have some fun. Michael Reichmann at Luminous Landscape uses the following […]