How To Make Skeletonization With C#
Skeletonization is a morphological process in image processing, which extracts the center lines of all shapes, which look like their skeleton
Filter by Category
Skeletonization is a morphological process in image processing, which extracts the center lines of all shapes, which look like their skeleton
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.
Skeletonization is a morphological operation in image processing with which we can extract the skeleton of shapes in binary images. Furthermore, skeletons are basically lines that lie in the middle of shapes.
Binary images have pixels with only two possible intensities, meaning they can be either black or white. Or if we put it in morphological terms, background and foreground pixels.
Furthermore, we divide image data into sets when we’re working with morphological processes. So in our case, we’re going to divide them into two sets, one for each color.
We can define the skeleton of a shape by placing a circle inside it. Therefore, if this circle is as large as it can be while still being inside the shape, its center would be a part of the skeleton. However, the size of this circle may vary depending on its position.
When this circle is of maximum size on any position, it touches boundary of the shape at two or more different places.
Unfortunately, it’s not that simple when it comes to processing pixels. However, we’re going to use morphological processes, we’ve covered before. We can skeletonize an image by using erosion and opening operations.
The trick here is to erode an image, and subtract the results of opening from it to get one part of the skeleton. Furthermore, we need to repeat this process, by further eroding and opening the image. And finally, we stop this iteration when an opening results to an empty image.
We get the skeleton of the image by combining the results of all steps where we subtracted opening from erosion. This may be difficult to grasp at first, but don’t worry, the function for this process if fairly simple.
And in case you’re not familiar with opening operation, we essentially apply erosion and then dilation to the result.
We can also reconstruct the skeletonized image, but we need to have each of the step stored separately. So we can do this by applying dilation at each of the steps and combining them to form the image.
I will include the function for skeletonization only. Therefore, I’d like to recommend that you check out the entire project. However, functions that aren’t going to be included in this post are for erosion and dilation.
public static Bitmap Skeletonize(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[] temp = new byte[bytes];
byte[] result = new byte[bytes];
Marshal.Copy(image_data.Scan0, buffer, 0, bytes);
image.UnlockBits(image_data);
while (true)
{
temp = buffer.Erode(w, h);
int sum = temp.Sum(x => (int)x);
if (sum == 0)
{
break;
}
temp = temp.Dilate(w, h);
for (int i = 0; i < bytes; i++)
{
result[i] += (byte)(buffer[i] - temp[i]);
}
buffer = buffer.Erode(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;
}
I hope this guide was helpful in understanding skeletonization process. As I said before, I’m including a download link to this demo project.