How To Make Opening And Closing – Morphology With C#
Opening and closing in image processing are morphological operations which are basically sequences of erosion and dilation operations.
Filter by Category
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#.
This tutorial shows how image watermarking works by implementing it with C# and we describe various purposes that watermarks have as well.
This tutorial shows how to use colors for image segmentation applied in C#. I explain the basics of applying it in different color spaces.
This color image smoothing and sharpening tutorial shows how to apply convolution for blurring and sharpening images with C#.
This guide shows how to apply color histogram equalization with iterative equalization of the image by using nth root or nth power.
Color balancing is one of the processes we use to adjust images, which are either on the weaker or heavier side for any of the color channels.
This guide shows how to apply image tone corrections for flat, dark and light images. The purpose of it is to adjust brightness and contrast.
Color slicing is a color image processing technique, which only shows colors in a certain color space making objects stand out.
Opening and closing are two morphological operations, each consisting of erosion and dilation processes in specific sequence. Morphological processes are nonlinear operations, because we’re dealing with set operations.
Therefore, we process image data, by putting it into sets. There is no limit, what kind of images we could work with with these techniques. However, for simplicity sake, we’ll demonstrate them on binary images where we separate pixels into two sets.
We’ll separate pixels into foreground or white pixel set and background or black pixel set. As for how erosion and dilation works, I suggest you check out my other posts where I talked about each more in depth.
This tutorial and every other morphological processing tutorial will use the principles you can learn from posts about erosion and dilation.
Opening is the process where we first apply erosion and after that we follow up with dilation. Where as with closing we apply dilation first and erosion second.
Resulting images if we applied it on the same image would be quite different to one another. Opening smoothes the contour of objects in the image, breaks lines that are thinner than the structuring element we use and it also eliminates thin protrusions.
Closing smoothes sections of contour but in comparison to opening, it fuses breaks that are narrower than the structuring element. It also eliminates small holes and fills gaps in the contour of the objects.
I’ve written functions that apply erosion and dilation and showed them on my other posts, but I’ll put them here as well. As you’ll be able to see, these are the simplest versions I could possibly make them.
For more complicated functions, I’d suggest you added the functionality to perform reflection for dilation. And another thing we could add was the option to use different shapes of structuring element and values held within.
public static Bitmap Dilation(this Bitmap image, int se_dim)
{
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);
int o = (se_dim - 1) / 2;
for (int i = o; i < w - o; i++)
{
for (int j = o; j < h - o; j++)
{
int position = i * 3 + j * image_data.Stride;
for (int k = -o; k <= o; k++)
{
for (int l = -o; l <= o; l++)
{
int se_pos = position + k * 3 + l * image_data.Stride;
for (int c = 0; c < 3; c++)
{
result[se_pos + c] = Math.Max(result[se_pos + c], buffer[position]);
}
}
}
}
}
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;
}
public static Bitmap Erosion(this Bitmap image, int se_dim)
{
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);
int o = (se_dim - 1) / 2;
for (int i = o; i < w - o; i++)
{
for (int j = o; j < h - o; j++)
{
int position = i * 3 + j * image_data.Stride;
byte val = 255;
for (int x = -o; x <= o; x++)
{
for (int y = -o; y <= o; y++)
{
int kposition = position + x * 3 + y * image_data.Stride;
val = Math.Min(val, buffer[kposition]);
}
}
for (int c = 0; c < 3; c++)
{
result[position + c] = val;
}
}
}
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;
}
I hope this tutorial was helpful in understanding opening and closing in image processing.
You can also download the whole demo project and try it out yourself.