How To Make Pruning In Image Processing Work With C#

Pruning in image processing is a morphological operation for removing spurs. It serves mainly as a post processing technique for cleaning up.


Andraz Krzisnik
How To Make Pruning In Image Processing Work...

Pruning is a combination of morphological operations in image processing, which we commonly use for processing handwriting. However, there are a few modifications we need to make to processes we will include.

In case you’re not familiar with morphological processes we’ve worked with so far, let’s mention what’s essential. First of all, we will deal with binary images, which means that they have either black or white pixels. Or if we phrase it in morphological terms, these images have background and foreground pixels.

Secondly, we divide images into sets, on which we will later apply morphological operations. Furthermore, we use structuring elements with each operation.

We can view structuring elements as kernels in convolution. However, we don’t compute image data with structuring element, we compare wether certain position has a foreground or background pixel.

How does pruning in image processing work?

Firstly, we’re going to apply a few steps of thinning with modified structuring elements. These structuring elements are meant for detecting endpoints.

We will have 8 different structuring elements, that will be able to detect an endpoint of the handwritten letters in any direction.

Thinning part of this process will remove any spurs or parasitic components left behind by skeletonization. Therefore this process is essential complement to these kind of processes.

However, we do remove good part of the letters as well, so we will also need to apply dilation. Furthermore, we’re going to limit the growth by dilating only those pixels that were foreground in the input image. This way, we’ll get those good pixels back.

Pruning process in image processing
Pruning process in image processing

Code

I’ve written a few functions, which all together apply the entire process of pruning. We use this image processing operation for automated recognition of handwriting.

public static byte[] Thin(this byte[] buffer, int[][,] se, int w, int h)
     {
         byte[] result = buffer;

         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 * w;
                     List<bool> change = new List<bool>();
                     int match = 8;

                     for (int kx = -1; kx < 2; kx++)
                     {
                         for (int ky = -1; ky < 2; ky++)
                         {
                             int se_pos = position + kx + ky * w;

                             if (se_pos == position)
                             {
                                 continue;
                             }

                             if (se[i][kx + 1, ky + 1] == -1)
                             {
                                 match--;
                             }

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

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

         return result;
     }

     public static byte[] Dilate(this byte[] buffer, byte[] limiter, int w, int h)
     {
         byte[] result = buffer;

         for (int x = 1; x < w - 1; x++)
         {
             for (int y = 1; y < h - 1; y++)
             {
                 int position = x + y * w;
                 byte val = 15;
                 for (int kx = -1; kx < 2; kx++)
                 {
                     for (int ky = -1; ky < 2; ky++)
                     {
                         int se_pos = position + kx + ky * w;
                         val = Math.Max(val, buffer[se_pos]);
                     }
                 }
                 if (limiter[position] > 0)
                 {
                     result[position] = val;
                 }
             }
         }

         return result;
     }

     public static Bitmap Prune(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);

         result = buffer;
         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 < 3; i++)
         {
             result = result.Thin(se, w, h);
         }
         for (int i = 0; i < 3; i++)
         {
             result = result.Dilate(buffer, w, h);
         }

         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(result, 0, res_data.Scan0, bytes);
         res_img.UnlockBits(res_data);
         return res_img;
     }

Conclusion

This post should serve as an example, how we can combine various morphological operations together. They can tackle problems we face in practice.

I hope this guide was helpful in understanding pruning in image processing. You can also download the demo project and try it out yourself.

Related Articles

C# Tutorial

Intensity Level Slicing With C# – Explore Image Processing

In this tutorial we will be talking about intensity level slicing in image processing. What is Intensity Level Slicing It’s a process that highlights pixels in an arbitrary...

Posted on by Andraz Krzisnik
Mean Filters

How To Use Arithmetic Mean Filter On Images – C# Guide

Arithmetic mean filter is one of the simplest mean filters we could use to reduce noise from an image. Learn more about spatial filtering.

Posted on by Andraz Krzisnik