Filters, filters, filters… Tired of them yet? Me too. After this we’ll leave them alone for a while. I promise. Until then, we have work to do.
What is a median filter? If you understand the average filter (or mean filter) then the median filter should give you no troubles at all – assuming you know what the median is. We’ll start there.
What is the Median?
The median is the middle number of a sample. To find it, you sort your list and pick the middle item. Yes, it really is that simple.
Here is a random set of 9 numbers:
Sort them:
Pick the middle value:
Like I said, it’s simple.
Why use median instead of mean?
The median is particularly effective against certain types of noise – specifically “salt and pepper” noise (black and white specks). Let’s assume this is the neighborhood we’re dealing with:
Performing the mean gives:
While the median returns:
If we expect that the 0 values are noise then the median is probably a much closer estimate.
Code
private WriteableBitmap MedianFilter(WriteableBitmap grayscale, int radius)
{
// we are still going to create a new image
// because we don’t want to modify the
// old image as we are processing it
WriteableBitmap filtered =
new WriteableBitmap(
grayscale.PixelWidth,
grayscale.PixelHeight);
// boiler plate code for our
// histogram stuff
int[] histogram = new int[256];
int maxIntensity = 0;
// the math is still easier if we create two loops
// instead of one
for (int y = 0; y < grayscale.PixelHeight; y++)
{
for (int x = 0; x < grayscale.PixelWidth; x++)
{
//here’s the pixel we’re centered on
int pixel = x + y * grayscale.PixelWidth;
byte intensity = (byte)grayscale.Pixels[pixel];
// if we are on an edge we are going to leave it
// as the original intensity. you will see the
// edges increasingly unsmoothed as the window
// size increases. here we are using the radius
// to determine our bounds
if (y <= radius – 1 ||
x <= radius – 1 ||
y >= grayscale.PixelHeight – radius ||
x >= grayscale.PixelWidth – radius)
{
histogram[intensity]++;
if (histogram[intensity] > maxIntensity)
{
maxIntensity = histogram[intensity];
}
continue;
}
/////////////////////////////////////////////////////////
// IMPORTANT PART ///////////////////////////////////////
/////////////////////////////////////////////////////////
// this list is the key
// it contains all of the neighboring pixels
List<byte> localIntensities = new List<byte>();
for (int yoffset = -radius; yoffset <= radius; yoffset++)
{
for (int xoffset = -radius;
xoffset <= radius;
xoffset++)
{
localIntensities.Add((
(byte)grayscale.Pixels[(x + xoffset)
+ (y + yoffset) * grayscale.PixelWidth]));
}
}
//sort the intensities
localIntensities.Sort();
//pick the middle value
int medianLocalIntensity =
localIntensities[(int)(localIntensities.Count/2.0+.5)];
/////////////////////////////////////////////////////////
// END IMPORTANT PART ///////////////////////////////////
/////////////////////////////////////////////////////////
// and now just set the color
filtered.Pixels[pixel] = (255 << 24)
| (byte)medianLocalIntensity << 16
| (byte)medianLocalIntensity << 8
| (byte)medianLocalIntensity;
histogram[(byte)medianLocalIntensity]++;
if (histogram[(byte)medianLocalIntensity] > maxIntensity)
{
maxIntensity = histogram[(byte)medianLocalIntensity];
}
}
}
PlotHistogram(histogram, maxIntensity);
return filtered;
}
Results
Taking a simple gray image I added salt and pepper noise to the image then performed a median filter and an average filter to it. The results are stunning.
flat gray image 10% salt and pepper noise
after median filtering after mean filtering
There are a few specks after applying the median filter, but the noise is removed pretty well. The average filter performed dismally to say the list.
Summary
If you expect salt and pepper noise then the median filter is great tool to have in your toolbox. If you want you can explore max, min, and mode filters. I don’t think we’ll cover them here unless we have a specific application for it.
Download Code
(code includes salt and pepper noise generation)
Up Next: Binary Images
Feel free to leave a comment or follow @azzlsoft on Twitter if you have any questions.