How To Make Thickening In Image Processing Work In C#

Thickening is a morphological operation in image processing, which adds foreground or white pixels to objects in order to thicken them.


Andraz Krzisnik
How To Make Thickening In Image Processing...

Thickening is a morphological process we use to thicken the shape of objects in the image. This tutorial will be oriented to processing binary images, which hold only two possible intensities in their pixels.

Binary images consist only of white and black pixels, or in morphological terms foreground and background pixels.

When we’re using morphological operations on images, we divide them into sets. Therefore, when we’re working with binary images, we’re going to divide them into two sets. These kind of processes are non-linear.

If we compare morphological processing and convolution, we can see the similarity between structuring element and kernel. However, as we said before, operations in morphology are non-linear while convolution is a linear process.

This basically means that we’re not doing any computing between the structuring element and image data. What we actually do is compare values in structuring element and image data wether they match or not. Therefore, the result we get depends on shapes that foreground pixels represent.

How does thickening work?

Thickening is a morphological dual of thinning, which we can already tell by their names. However, the reason why I brought that up is that there is a trick we can make use of thinning process here.

Both of these processes use multiple structuring elements to detect where is the edge of an object and which pixels to change to background pixels – thinning or to foreground pixels – thickening.

If we were to create a separate algorithm to apply thickening, we would only need to interchange the values in structuring elements and add foreground pixels to image.

But in practice, we rarely do that. What we go after instead is inverting the pixel values in the image first and apply thinning to it. And finally, we just invert it back.

thickening process in image processing
Thickening process

Thickening can result in image with disconnected points, so it’s ofter accompanied by post processing techniques to remove them.

Code

Majority of code is from thinning operation, however there are a few modifications I’ve made to it for this operation. As I meantioned before, we need to invert pixel values twice, firstly before the start of the operation and secondly at the end of it.

And another change was that I’ve set a limit how many times it went through the sequence of structuring elements. If we would just leave it, it wouldn’t stop until almost all of the image was white. This way we can still get shapes that resemble objects it’s thickening.

public static Bitmap Thickening(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.Format8bppIndexed);

         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);

         int[][,] se =
         {
             Structuring_Element.left,
             Structuring_Element.topleft,
             Structuring_Element.top,
             Structuring_Element.topright,
             Structuring_Element.right,
             Structuring_Element.bottomright,
             Structuring_Element.bottom,
             Structuring_Element.bottomleft
         };

         for (int i = 0; i < bytes; i++)
         {
             buffer[i] = (byte)((buffer[i] == 0) ? 15 : 0);
         }

         for (int t = 0; t < 5; t++)
         {
             for (int i = 0; i < se.Length; i++)
             {
                 for (int x = 1; x < w - 1; x++)
                 {
                     for (int y = 1; y < h - 1; y++)
                     {
                         int position = x + y * image_data.Stride;
                         List<bool> change = new List<bool>();
                         for (int kx = -1; kx < 2; kx++)
                         {
                             for (int ky = -1; ky < 2; ky++)
                             {
                                 int se_pos = position + kx + ky * image_data.Stride;
                                 int se_opp = position + (kx * -1) + (ky * -1) * image_data.Stride;

                                 if (se_pos == position)
                                 {
                                     continue;
                                 }
                                 if (buffer[se_pos] > 0 && se[i][kx + 1, ky + 1] == 1 && buffer[se_opp] == 0 && se[i][(kx * -1) + 1, (ky * -1) + 1] == 0)
                                 {
                                     change.Add(true);
                                 }
                             }
                         }

                         if (change.Count == 3 && buffer[position] > 0)
                         {
                             buffer[position] = 0;
                         }
                     }
                 }
             }
         }

         for (int i = 0; i < bytes; i++)
         {
             buffer[i] = (byte)((buffer[i] == 0) ? 15 : 0);
         }

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

         return res_img;
     }

Conclusion

I hope this tutorial was helpful in understanding thickening morphological process.

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

Related Articles

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
Simple Lists

How To Use Generic Lists In C# – Made Easy

Generic lists in C# are a data structures that allow us to add and remove objects to store inside without declaring its size.

Posted on by Andraz Krzisnik