Navigation

Related Articles

Back to Latest Articles

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

RGB to HSI Color Model

How To – RGB To HSI And HSI To RGB Color Model In C#

This guide shows how to convert RGB to HSI image data and also how to convert it back from HSI to RGB to make it displayable on screen.

Posted on by Andraz Krzisnik
C# Tutorial

C# Tutorial: How To Create Gaussian Blur

Gaussian blur is an image processing operation, that reduces noise in images. It does so by a convolution process, using a matrix that contains values calculated by a Gaussian...

Posted on by Andraz Krzisnik