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.
Filter by Category
Thickening is a morphological operation in image processing, which adds foreground or white pixels to objects in order to thicken them.
Thinning is a morphological operation in image processing, which eats away at the foregorund pixels and leaves only lines shaping objects.
Convex hull in image processing is a morphological operation, where we encapsulate a shape or and object in an image into a convex shape.
Hit or miss transform is a morphological process for shape detection. We need to use two different structuring elements to find the shapes.
Extraction of connected components in image processing is a morphological process, where we isolate object to a separate image.
Hole filling in image processing is a morphological operation that fills in shapes of black pixels surrounded by white pixels.
Boundary extraction in image processing is one of the basic morphological algorithms with which we extract only the outline of shown objects.
Opening and closing in image processing are morphological operations which are basically sequences of erosion and dilation operations.
Image dilation is one of the fundamental morphological processes, which we demonstrate it here in C# programming language.
Image erosion is one of the fundamental morphological operations and this tutorial explains how it works and demonstrates it in C#.
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.
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 can result in image with disconnected points, so it’s ofter accompanied by post processing techniques to remove them.
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;
}
I hope this tutorial was helpful in understanding thickening morphological process.
You can also download the demo project and try it out yourself.