How To Make A Basic Global Thresholding Algorithm – C#
This tutorial demonstrates how to get optimal threshold value for basic global thresholding operation for segmentation in image processing.
Filter by Category
We used global thresholding process when we worked with edge detection segmentation operations. However, we’re going in a little deeper with this tutorial, where I’m going to introduce it a little more formally.
When I used thresholding in other guides, I basically just set the threshold value experimentally. Therefore, results that I got from those algorithms were more or less tailored for the example images I demonstrated with.
But you could still get good results using different images, as long their intensity distribution was similar to example ones. Thresholding in general is a process, where we split intensity values based on their distribution.
When we use global thresholding, we set a single intensity value as a threshold. Furthermore, we can get a more clear idea how we’re splitting pixels into two sets by looking at the image histogram. If we have a gap between object and background pixel intensities, we can split them pretty easily.
It get’s complicated when the image has considerable amount of noise which can cause intensity distribution to blend into one. At that point we need to do some preprocessing or different type of thresholding.
In order to find a suitable threshold for each image, we need to use an algorithm for estimating it. It’s basically an iterative process, where we set a condition which checks if the estimated value changed for less than or equal to some predefined amount before stoping the iterations.
In other words, it searches for most optimal value for each specific image.
I’m going to separate this iterative process into 4 steps. First of all, we need to set the initial threshold value. We can just set it by our choice or we can use the average intensity value of the whole image. In the example I made for this post I used the average.
Secondly, we split pixel values into two sets, to pixels with intensities below the threshold and above it.
For the third step, we’re going to compute mean intensity values for each of the set.
And lastly, we’re going to get the average intensity of the set means we calculated in the previous step. In other words, we’ll sum the means together and divide them by 2. Furthermore, this value will serve as our new initial thresholding value.
All we have to do now is just repeat this process until the change between the old and the new initial thershold value is lower than some predefined value. In case you’re dealing with images which have a distinct gap between intensity distributions, we can set it also to 0.
After we get our optimal value, we use it to segment the image. This part is more familiar as we used it before in other segmentation processes. Intensities below the threshold are set to 0 and those above it to 255.
public static Bitmap GlobalThresholding(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.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);
//Getting threshold intensity value
int[] converted = buffer.Select(x => (int)x).ToArray();
int init = converted.Sum() / bytes;
int delta = 1;
while (delta > 0)
{
int[] histogram = new int[255];
for (int i = 0; i < bytes; i += 3)
{
histogram[buffer[i]]++;
}
int mean1 = 0;
int mean2 = 0;
int sum1 = 0;
int sum2 = 0;
for (int i = 0; i < 255; i++)
{
if (i <= init)
{
mean1 += histogram[i] * i;
sum1 += histogram[i];
}
else
{
mean2 += histogram[i] * i;
sum2 += histogram[i];
}
}
mean1 /= sum1;
mean2 /= sum2;
delta = init;
init = (mean1 + mean2) / 2;
delta = Math.Abs(delta - init);
}
//Thresholding
for (int i = 0; i < bytes; i++)
{
result[i] = (byte)(buffer[i] >= init ? 255 : 0);
}
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 on global thresholding was helpful.
You can also download the demo project and try it out yourself.