How To Make Region Growing Algorithm With C#

Region growing segmentation is a process, with which we can extract regions from image based on the properties of pixels inside them.


Andraz Krzisnik
How To Make Region Growing Algorithm With C#

Region growing is a segmentation operation, which groups pixels into larger areas following certain criteria. Therefore, operations within the whole sequence will vary depending on the problem you’re trying to solve.

However, first step is to get a set of seed points, which we’re going to grow into a region. Furthermore, these seed pixels will hold the information that we’re going to use as a criteria for which pixels belong to the region and which don’t.

We can also call this a similarity criteria, since we’re measuring which pixels are similar enough. However, it doesn’t depend just on a problem we’re trying to solve here. But we also need to consider what type of image data is available to us, like color for example.

We also need to stop the region growth, which we can do by using a mask. In case you already know of my tutorials on morphological processing, I covered similar thing at automatic hole filling.

However, since we’re working with grayscale and color images with this process, it’s a little more complicated. Therefore, our mask here is going to determine, which pixels we want to include in the growing process and when to stop it.

How does region growing segmentation work?

Firstly, we need to extract seed pixels of the regions we want to segment. In our case here, we’re going to extract cracks and porosities from an x-ray image of a weld.

X-ray image of a weld
X-ray image of a weld

As, we’ll be able to see from the image, these defects will show up much brighter than everything else in the image. Therefore, we’re going to use a high threshold to extract seed points of these regions.

In order to get our seed points from these regions, we need to use morphological erosion to erode them to single points.

Secondly, we need to make a mask, which will limit the growth to these specific regions. Therefore, we need to specify a predicate, which we can compute with the following formula.

region growing predicate formula
Predicate formula

After we compute the predicate image, we need to threshold it. There’s various thresholding algorithms we can use here. However, I’ll use hysteresis thresholding and use the lower threshold for thresholding the image.

This process will produce an image where defects and around the weld appear black and the weld itself will appear white. We’re going to use this as a mask to limit the growth of defects.

Furthermore, we’re going to limit the growth inside the black spots in the image. And since we used a high enough threshold earlier, we didn’t get any seed pixels outside the defect regions.

And lastly, we grow these seed pixels by using morphological dilation until we fill all those defect areas.

Region growing result
Region growing result

Code

I modified the algorithm for hysteresis thresholding, so it only returns the first optimal threshold. You’ll also be able to download the whole project and check the entire code yourself.

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

         double[] histogram = new double[256];
         for (int i = 0; i < bytes; i += 3)
         {
             histogram[buffer[i]]++;
         }
         histogram = histogram.Select(x => x / (w * h)).ToArray();

         double sum = 1;
         int threshold = new int();
         for (int i = 255; i >= 0; i--)
         {
             if (sum > 0.999)
             {
                 sum -= histogram[i];
                 threshold = i;
             }
             else
             {
                 break;
             }
         }

         for (int i = 0; i < bytes; i++)
         {
             result[i] = (byte)((buffer[i] > threshold) ? 255 : 0);
         }

         result = result.ErodeToPoints(image_data);

         byte[] mask = buffer.Select(x => (byte)Math.Abs(threshold + 1 - x)).ToArray();
         threshold = mask.HysteresisThreshold(image_data);
         for (int i = 0; i < bytes; i++)
         {
             mask[i] = (byte)((mask[i] > threshold) ? 255 : 0);
         }
         result = result.Dilate(mask, image_data);

         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;
     }

Conclusion

I hope this tutorial was helpful in giving you a better understanding on how region growing segmentation process works.

As I mentioned above, you can also download the demo project and try it out yourself.

Related Articles

Tone and Color Corrections

How To Color Balancing In Images With C#

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.

Posted on by Andraz Krzisnik
Region Segmentation Using Superpixels

How To Make SLIC Superpixel Algorithm With C#

SLIC superpixel segmentation is a modern operation for reducing irrelevant detail for shortening computational time in further processing.

Posted on by Andraz Krzisnik