Navigation

Related Articles

Back to Latest Articles

How To Make Adaptive Filters For Local Noise In C#

Adaptive local noise reduction filters are useful for processing images that have too much noise to deal with with other simpler filters.


Andraz Krzisnik
How To Make Adaptive Filters For Local Noise...

I’m going to talk about adaptive filters in this post. More specifically, I’m going to talk about adaptive local noise reduction filter. Basically, it’s a spatial filter and we mainly use it to filter out noise from the images.

Spatial filter or kernel is like a small window on our image which encompasses a local set of intensity values. After we get this set, we use it to calculate an output intensity value. In order to render the output image, we need to calculate for each pixel separately.

There are a few reasons why are adaptive filters better than other filters. Firstly, other filters disregard that image characteristics may vary from one point to the other across it, these do not. And secondly, their performance is superior.

However, there are also a few downsides. For one, adaptive filters are more complicated. Therefore, they need more processing power to compute each output intensity value. And for another point, they’re still good for images which have only noise degradation.

Adaptive local noise reduction filters

These have the simplest statistical measures for mean and variance. When we calculate new ouput values, the mean gives us the average intensity inside the filter. While variance gives us contrast information from these intensities.

Let’s take a look at the following formula and break it down.

adaptive local noise reduction filter formula
Adaptive local noise reduction filter formula

So, first things first, g(x, y) represents the intensity value, which has a position in the center of the filter. Then, before the brackets, we have ratio between noise variance on top of the fraction and intensity variance below.

We calculate intensity variance from intensities inside the filter and we have to do it for each output pixel. And lastly, mL is the mean of intensities inside the filter region.

Despite how complex formula is, we can compute almost all values from filter set. The only unknown that remains is noise variance. We need to estimate it to be as close to variance of the noise with which our image is corrupted.

Another pointer for using this filter is to limit maximum noise variance to be less or equal to intensity variance. This way we can prevent some unwanted things happening, like negative intensity values and such.

C# implementation

public static Bitmap Filter(this Bitmap image, double noise_variance)
    {

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

        Marshal.Copy(image_data.Scan0, buffer, 0, bytes);
        image.UnlockBits(image_data);

        int r = 3;

        int wres = w - 2 * r;
        int hres = h - 2 * r;

        Bitmap result_image = new Bitmap(wres, hres);
        BitmapData result_data = result_image.LockBits(
            new Rectangle(0, 0, wres, hres),
            ImageLockMode.WriteOnly,
            PixelFormat.Format24bppRgb);
        int res_bytes = result_data.Stride * result_data.Height;
        byte[] result = new byte[res_bytes];

        for (int x = r; x < w - r; x++)
        {
            for (int y = r; y < h - r; y++)
            {

                int pixel_location = x * 3 + y * image_data.Stride;
                int res_pixel_loc = (x - r) * 3 + (y - r) * result_data.Stride;
                double[] mean = new double[3];
                int[][] neighborhood = new int[3][];

                for (int c = 0; c < 3; c++)
                {
                    neighborhood[c] = new int[(int)Math.Pow(2 * r + 1, 2)];
                    int added = 0;
                    for (int kx = -r; kx <= r; kx++)
                    {
                        for (int ky = -r; ky <= r; ky++)
                        {
                            int kernel_pixel = pixel_location + kx * 3 + ky * image_data.Stride;
                            neighborhood[c][added] = buffer[kernel_pixel + c];
                            added++;
                        }
                    }
                }

                for (int c = 0; c < 3; c++)
                {
                    mean[c] = neighborhood[c].Sum() / Math.Pow(2 * r + 1, 2);
                    double variance = 0d;
                    for (int i = 0; i < neighborhood[c].Length; i++)
                    {
                        variance += Math.Pow(neighborhood[c][i] - mean[c], 2);
                    }
                    variance /= Math.Pow(2 * r + 1, 2);

                    if (noise_variance > variance)
                    {
                        noise_variance = variance;
                    }

                    result[res_pixel_loc + c] = (byte)(buffer[pixel_location + c] - (noise_variance / variance) * (buffer[pixel_location + c] - mean[c]));
                }
            }
        }

        Marshal.Copy(result, 0, result_data.Scan0, res_bytes);
        result_image.UnlockBits(result_data);

        return result_image;
    }

Conclusion

We can use adaptive filters when we have significant noise corruption in our images. They give us a little more umpf than the rest of spatial filters.

I hope this guide was helpful. You can also download the project and try it out on your own images.

Related Articles

Sorting Algorithms

How To Make Quicksort Algorithm With C# – Made Easy

Quicksort algorithm or divide and conquer algorithm is one of the most used sorting algorithms, because of its supperior efficiency.

Posted on by Andraz Krzisnik
Edge Detection

How To Make Basic Edge Detection Algorithm With C#

Edge detection is a segmentation technique in image processing for extracting object boundaries based on abrupt intensity changes.

Posted on by Andraz Krzisnik