How To Make Morphological Smoothing Work With C#

Morphological smoothing is an image processing technique, which includes grayscale erosion and dilation, and grayscale opening and closing


Andraz Krzisnik
How To Make Morphological Smoothing Work...

Morphological smoothing is an image processing technique we can use for grayscale images. Additionally, it’s purpose is mainly to filter the image for removing noise and smoothing it.

It utilizes two fundamental grayscale morphological operations, which are grayscale erosion and dilation. Unlike operations we used to process binary images, these two work a little differently.

When we’re dealing with grayscale images, we don’t have just two sets of pixels like we had with binary images. However, in general it still follows the same rules when we’re applying it. What I mean by that is that we still compare structuring element values with image pixels they overlap.

The only difference here is that we have more intensities to work with. Therefore, when we’re applying grayscale erosion, we select the minimum intensity value in the neighborhood. On the other hand, when we’re working with dilation we select the maximum intensity.

We slide this structuring element over all the pixels in the input image and draw the resulting image by setting the value that overlaps in the center.

How does morphological smoothing work?

We’re going to use two different processes together, which are both a combination of erosion and dilation processes we described above. Furthermore, we call these processes grayscale opening and closing.

Opening is a composite operation, where we first apply erosion and follow up with dilation to that resulting image. Closing is the opposite, so we first apply dilation and then erosion.

We covered these two processes, when we were working with binary images. However, when we apply them to grayscale images, we don’t erase details, we suppress them. Additionally, with opening we supress bright details that are smaller than structuring element, while leaving dark ones unaffected.

Closing has the opposite effect.

Code

public static byte[] Erode(this byte[] buffer, BitmapData image_data)
     {
         byte[] result = new byte[buffer.Length];

         for (int x = 1; x < image_data.Width - 1; x++)
         {
             for (int y = 1; y < image_data.Height - 1; y++)
             {
                 int position = x * 3 + y * image_data.Stride;
                 byte val = 255;
                 for (int i = -1; i < 2; i++)
                 {
                     for (int j = -1; j < 2; j++)
                     {
                         int se_pos = position + i * 3 + j * image_data.Stride;
                         val = Math.Min(val, buffer[se_pos]);
                     }
                 }
                 for (int c = 0; c < 3; c++)
                 {
                     result[position + c] = val;
                 }
             }
         }

         return result;
     }

     public static byte[] Dilate(this byte[] buffer, BitmapData image_data)
     {
         byte[] result = new byte[buffer.Length];
         for (int x = 1; x < image_data.Width - 1; x++)
         {
             for (int y = 1; y < image_data.Height - 1; y++)
             {
                 int position = x * 3 + y * image_data.Stride;
                 byte val = 0;
                 for (int i = -1; i < 2; i++)
                 {
                     for (int j = -1; j < 2; j++)
                     {
                         int se_pos = position + i * 3 + j * image_data.Stride;
                         val = Math.Max(val, buffer[se_pos]);
                     }
                 }
                 for (int c = 0; c < 3; c++)
                 {
                     result[position + c] = val;
                 }
             }
         }

         return result;
     }

     public static Bitmap MorphologicalSmoothing(this Bitmap image)
     {
         int w = image.Width;
         int h = image.Height;

         BitmapData image_data = image.LockBits(
             new Rectangle(0, 0, w, h),
             ImageLockMode.ReadOnly,
             PixelFormat.Format24bppRgb);

         int bytes = image_data.Stride * image_data.Height;
         byte[] buffer = new byte[bytes];
         byte[] result = new byte[bytes];

         Marshal.Copy(image_data.Scan0, buffer, 0, bytes);
         image.UnlockBits(image_data);

         //opening
         result = buffer.Erode(image_data);
         result = result.Dilate(image_data);

         //closing
         result = result.Dilate(image_data);
         result = result.Erode(image_data);

         Bitmap res_img = new Bitmap(w, h);
         BitmapData res_data = res_img.LockBits(
             new Rectangle(0, 0, w, h),
             ImageLockMode.WriteOnly,
             PixelFormat.Format24bppRgb);
         Marshal.Copy(result, 0, res_data.Scan0, bytes);
         res_img.UnlockBits(res_data);

         return res_img;
     }

Conclusion

I hope this tutorial was helpful in giving you a better understanding on how grayscale morphological smoothing works. It was also a good opportunity to touch on fundamental operations like erosion and dilation and their combinations.

You can also download the demo project and try it out yourself.

Related Articles

C# Tutorial

C# Tutorial: How To Convert a Color Image To Grayscale

Grayscale images are basically black and white images. These images are good for programming, since their pixels hold only intensity values or in other words, shades of gray, they...

Posted on by Andraz Krzisnik
Image Noise

How To Add Salt And Pepper Noise On Image – C# Guide

Salt and pepper noise or impulse noise is one of the noise models we can use to simulate image data corruption in real life.

Posted on by Andraz Krzisnik