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#

Symmetric Nearest Neighbor in C#

2/19/2009

I showed the Kuwahara filter and while it's rather cool, there's actually a filter that works a bit better: symmetric nearest neighbor. The SNN filter works in a similar fashion to k-nearest neighbor algorithms (which I'm sure is surprising since it more or less is a KNN algorithm...). All the algorithm does, is it takes symmetric pairs (although you can use quadruples as well) of pixels, compares each one to the center pixel, and the one that is closer (color wise, not distance) gets added to the total for that center pixel. At the end we divide by the number of pixels we added together and we end up with the new center pixel... Anyway, I'm sure that code would probably help you out more than my poor explanation:

   1: /// <summary>
   2: /// Does smoothing using a SNN blur
   3: /// </summary>
   4: /// <param name="OriginalImage">Image to manipulate</param>
   5: /// <param name="Size">Size of the aperture</param>
   6: public static Bitmap SNNBlur(Bitmap OriginalImage, int Size)
   7: {
   8:     Bitmap NewBitmap = new Bitmap(OriginalImage.Width, OriginalImage.Height);
   9:     BitmapData NewData = Image.LockImage(NewBitmap);
  10:     BitmapData OldData = Image.LockImage(OriginalImage);
  11:     int NewPixelSize = Image.GetPixelSize(NewData);
  12:     int OldPixelSize = Image.GetPixelSize(OldData);
  13:     int ApetureMinX = -(Size / 2);
  14:     int ApetureMaxX = (Size / 2);
  15:     int ApetureMinY = -(Size / 2);
  16:     int ApetureMaxY = (Size / 2);
  17:     for (int x = 0; x < NewBitmap.Width; ++x)
  18:     {
  19:         for (int y = 0; y < NewBitmap.Height; ++y)
  20:         {
  21:             int RValue = 0;
  22:             int GValue = 0;
  23:             int BValue = 0;
  24:             int NumPixels = 0;
  25:             for (int x2 = ApetureMinX; x2 < ApetureMaxX; ++x2)
  26:             {
  27:                 int TempX1 = x + x2;
  28:                 int TempX2 = x - x2;
  29:                 if (TempX1 >= 0 && TempX1 < NewBitmap.Width && TempX2 >= 0 && TempX2 < NewBitmap.Width)
  30:                 {
  31:                     for (int y2 = ApetureMinY; y2 < ApetureMaxY; ++y2)
  32:                     {
  33:                         int TempY1 = y + y2;
  34:                         int TempY2 = y - y2;
  35:                         if (TempY1 >= 0 && TempY1 < NewBitmap.Height && TempY2 >= 0 && TempY2 < NewBitmap.Height)
  36:                         {
  37:                             Color TempColor = Image.GetPixel(OldData, x, y, OldPixelSize);
  38:                             Color TempColor2 = Image.GetPixel(OldData, TempX1, TempY1, OldPixelSize);
  39:                             Color TempColor3 = Image.GetPixel(OldData, TempX2, TempY2, OldPixelSize);
  40:                             if (Distance(TempColor.R, TempColor2.R, TempColor.G, TempColor2.G, TempColor.B, TempColor2.B) <
  41:                                 Distance(TempColor.R, TempColor3.R, TempColor.G, TempColor3.G, TempColor.B, TempColor3.B))
  42:                             {
  43:                                 RValue += TempColor2.R;
  44:                                 GValue += TempColor2.G;
  45:                                 BValue += TempColor2.B;
  46:                             }
  47:                             else
  48:                             {
  49:                                 RValue += TempColor3.R;
  50:                                 GValue += TempColor3.G;
  51:                                 BValue += TempColor3.B;
  52:                             }
  53:                             ++NumPixels;
  54:                         }
  55:                     }
  56:                 }
  57:             }
  58:             Color MeanPixel = Color.FromArgb(RValue / NumPixels,
  59:                 GValue / NumPixels,
  60:                 BValue / NumPixels);
  61:             Image.SetPixel(NewData, x, y, MeanPixel, NewPixelSize);
  62:         }
  63:     }
  64:     Image.UnlockImage(NewBitmap, NewData);
  65:     Image.UnlockImage(OriginalImage, OldData);
  66:     return NewBitmap;
  67: }
   1: private static double Distance(int R1, int R2, int G1, int G2, int B1, int B2)
   2: {
   3:     return System.Math.Sqrt(((R1 - R2) * (R1 - R2)) + ((G1 - G2) * (G1 - G2)) + ((B1 - B2) * (B1 - B2)));
   4: }

Please note that the code uses a couple of functions from my utility library. Namely it uses an image locking/unlocking function to speed things up a bit, which can be removed and a SetPixel/GetPixel function which can be replaced with the built in .Net equivalents. Anyway, the code above has two functions. The first is the actual SNN function. It takes a bitmap and also the size of the box around each pixel that you want to look at. The second function is simply a helper, that calculates the Euclidean distance between two pixels. The code is rather straight forward. You look at each pixel and the pixels around it, taking the values that are closest in color. You add up the values and then divide it by the number of pixels that you added together. Very basic but it does a great job at smoothing an item and yet preserving borders. Anyway, hopefully this helps you out. So give it a try, leave feedback, and happy coding.



Comments