Other Posts in Image Editing

  1. Perlin Noise
  2. Fault Formation
  3. Cellular Textures
  4. Resizing an Image in C#
  5. Box Blur and Gaussian Blur... Sort of...
  6. Thermal Erosion
  7. Using Mid Point Displacement to Create Cracks
  8. Fluvial Erosion
  9. Creating Marble Like Textures Procedurally
  10. Procedural Textures and Dilation
  11. Converting Image to Black and White in C#
  12. Getting an HTML Based Color Palette from an Image in C#
  13. Adding Noise/Jitter to an Image in C#
  14. Creating Pixelated Images in C#
  15. Edge detection in C#
  16. Using Sin to Get What You Want... In C#...
  17. Noise Reduction of an Image in C# using Median Filters
  18. Image Dilation in C#
  19. Sepia Tone in C#
  20. Kuwahara Filter in C#
  21. Matrix Convolution Filters in C#
  22. Symmetric Nearest Neighbor in C#
  23. Bump Map Creation Using C#
  24. Normal Map Creation Using C#
  25. Creating Negative Images using C#
  26. Red, Blue, and Green Filters in C#
  27. Converting an Image to ASCII Art in C#
  28. Adjusting Brightness of an Image in C#
  29. Adding Noise to an Image in C#
  30. Adjusting the Gamma of an Image Using C#
  31. Adjusting Contrast of an Image in C#
  32. Drawing a Box With Rounded Corners in C#
  33. Anding Two Images Together Using C#
  34. Motion Detection in C#
  35. Creating Thermometer Chart in C#
  36. Colorizing a Black and White Image in C#
  37. Extracting an Icon From a File
  38. Setting the Pixel Format and Image Format of an Image in .Net
  39. Using Unsafe Code for Faster Image Manipulation
  40. Sobel Edge Detection and Laplace Edge Detection in C#

Noise Reduction of an Image in C# using Median Filters


One of the main issues when trying to do image processing is the simple fact that images usually contain some degree of noise. This noise can in turn cause issues. For instance if you're doing edge detection, a spot on the image may cause the algorithm to detect edges that it shouldn't. One of the easiest ways to fix this issue is to use a median filter on an image. Unlike box blurs and gaussian blurs, we're not looking for the average of the pixels. In the case of a median filter, we're looking for the median (sort the values, take the one in the middle). We're still looking at a set of pixels around each pixel but we're simply taking the median instead of the mean.  The advantage of this approach is that it keeps the edges of an image but simply degrades the details a bit, unlike a box blur which will slowly bleed the edges.

Unfortunately in C# there is no built in function to do this (or at least none that I'm aware of) and as such I wrote code to accomplish it and as always I'm sharing it with you:

   1: public static Bitmap MedianFilter(Bitmap Image, int Size)
   2: {
   3:     System.Drawing.Bitmap TempBitmap = Image;
   4:     System.Drawing.Bitmap NewBitmap = new System.Drawing.Bitmap(TempBitmap.Width, TempBitmap.Height);
   5:     System.Drawing.Graphics NewGraphics = System.Drawing.Graphics.FromImage(NewBitmap);
   6:     NewGraphics.DrawImage(TempBitmap, new System.Drawing.Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height), new System.Drawing.Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height), System.Drawing.GraphicsUnit.Pixel);
   7:     NewGraphics.Dispose();
   8:     Random TempRandom = new Random();
   9:     int ApetureMin = -(Size / 2);
  10:     int ApetureMax = (Size / 2);
  11:     for (int x = 0; x < NewBitmap.Width; ++x)
  12:     {
  13:         for (int y = 0; y < NewBitmap.Height; ++y)
  14:         {
  15:             List<int> RValues = new List<int>();
  16:             List<int> GValues = new List<int>();
  17:             List<int> BValues = new List<int>();
  18:             for (int x2 = ApetureMin; x2 < ApetureMax; ++x2)
  19:             {
  20:                 int TempX = x + x2;
  21:                 if (TempX >= 0 && TempX < NewBitmap.Width)
  22:                 {
  23:                     for (int y2 = ApetureMin; y2 < ApetureMax; ++y2)
  24:                     {
  25:                         int TempY = y + y2;
  26:                         if (TempY >= 0 && TempY < NewBitmap.Height)
  27:                         {
  28:                             Color TempColor = TempBitmap.GetPixel(TempX, TempY);
  29:                             RValues.Add(TempColor.R);
  30:                             GValues.Add(TempColor.G);
  31:                             BValues.Add(TempColor.B);
  32:                         }
  33:                     }
  34:                 }
  35:             }
  36:             RValues.Sort();
  37:             GValues.Sort();
  38:             BValues.Sort();
  39:             Color MedianPixel = Color.FromArgb(RValues[RValues.Count / 2],
  40:                 GValues[GValues.Count / 2], 
  41:                 BValues[BValues.Count / 2]);
  42:             NewBitmap.SetPixel(x, y, MedianPixel);
  43:         }
  44:     }
  45:     return NewBitmap;
  46: }

The code above takes in an image and the size of the aperture. The aperture is the number of pixels around that pixel that you want it to look at. The bigger the aperture, the more "blurred" the image will be. There is one issue with the code that I should bring up and it's one that many implementations have to deal with. The issue is what do we do with the edges? Edges have less pixels and therefore are going to have more detail than everything else. In my case, I'm ignoring this fact. If you wanted you could modify it, I just felt you should know prior to using the code. Anyway, I hope this helps someone out. So give it a try, leave feedback, and happy coding.


James Craig
September 23, 2010 8:37 AM

Very cool and thank you. I've also updated the code in the post in my utility library to use LockBits: http://cul.codeplex.com/SourceControl/changeset/view/61004#707831Note that the functions are in alphabetical order, so just look for the 'M's... Your version will actually run faster though as I encapsulated the GetPixel/SetPixel in their own functions and thus have to calculate my position each time. You're doing straight pointer arithmetic (and thus wouldn't have to do as many multiplications, etc. in the long run). So very cool and thank you for the post.

Jakub Milik
September 23, 2010 5:14 AM

Forgot to add that it assumes that your input image is in ARGB format. If it's not just change:var srcImage = image;to:var srcImage = new Bitmap(image);and dispose srcImage at the end of the function:srcImage.Dispose();

Jakub Milik
September 23, 2010 5:10 AM

Hi. Here's a version with LockBits.[quote]public static Bitmap MedianFilter(Bitmap image, int size){unsafe{var width = image.Width;var height = image.Height;var imgSize = new Rectangle(0, 0, width, height);var srcImage = image;var srcBitmapData = srcImage.LockBits(imgSize, ImageLockMode.ReadWrite, srcImage.PixelFormat);var dstImage = new Bitmap(width, height);var dstBitmapData = dstImage.LockBits(imgSize, ImageLockMode.ReadWrite, dstImage.PixelFormat);var apetureMin = -(size / 2);var apetureMax = (size / 2);for (var y = 0; y < height; y++){var dstRow = (byte*)dstBitmapData.Scan0 + (y * dstBitmapData.Stride);for (var x = 0; x < width * 4; x += 4){

James Craig
August 17, 2010 5:35 PM

ContextSwitch deadlock usually means that your app's UI thread isn't responding. This can be because a median filter takes a while on large images. To mitigate this you might want to look into LockBits (http://msdn.microsoft.com/en-us/library/5ey6h79d.aspx) and unsafe code. The basic concept is the same though (I'm working on rewriting all of my image classes/functions to use this in the future but for now I don't have any code that I could show you). You should be able to search and find some examples. As far as the size of the image, it's the Width and Height properties of the Bitmap object. Whatever the size of the image you send in is the size of the image that you get out.

August 17, 2010 12:55 PM

I set '2' as size.. The system response for small images. when i upload more colorful images the system gives an error called "ContextSwitch deadlock was detected..." .. What is the reason for the error??? If u don't mind please describe me about the size little bit further and how can i recognize the size of the image... thank you....

James Craig
August 11, 2010 8:43 AM

Size is the size of the aperture in pixels that the median filter uses around each individual pixel (basically how far it looks left/right and up/down).

August 11, 2010 1:24 AM

wts size here???

James Craig
June 22, 2010 3:08 PM

I completely agree, but I felt that some people might get confused as to what is going on if I went the unsafe route (especially considering most C# developers have never even seen the keyword actually used). This was more to show the concept than anything as switching it to use an array isn't too difficult really.

June 22, 2010 11:43 AM

Cool function but waaay too slow for large images. You should consider stepping into unsafe code and then cycle through the image using a double array altering the byte values of pixels directly. This is a lot faster than GetPixel & SetPixel.Cheers,Nick

June 11, 2010 2:42 AM

Got it sorry

June 11, 2010 2:39 AM

Excusmi what is the size ?is it the size of the original image