Kuwahara Filter in C#

The Kuwahara filter is probably one of those things that you haven’t heard of much, if ever. It’s a noise reduction/blurring technique for images that preserves edges, similar to a median filter. It acts like a box blur, except we take that box around the center pixel and divide that into four smaller boxes (with some overlap since the number of items in the box is usually odd along the x and y axis). These smaller boxes are calculated in the same fashion as the box blur for the average value. While we’re doing that, we’re also performing another step: Finding the variance for the box. We want to know which box has the least amount of variance. The variance can be calculated in a number of ways but the easiest is simply finding the minimum and maximum values (for r, g and b) for a box and subtracting the min from the max. The bit of code below is based on my Visual Basic source used in RasterWave:

public static Bitmap KuwaharaBlur(Bitmap Image, int Size)
{
    System.Drawing.Bitmap TempBitmap = Image;
    System.Drawing.Bitmap NewBitmap = new System.Drawing.Bitmap(TempBitmap.Width, TempBitmap.Height);
    System.Drawing.Graphics NewGraphics = System.Drawing.Graphics.FromImage(NewBitmap);
    NewGraphics.DrawImage(TempBitmap, new System.Drawing.Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height), new System.Drawing.Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height), System.Drawing.GraphicsUnit.Pixel);
    NewGraphics.Dispose();
    Random TempRandom = new Random();
    int[] ApetureMinX = { -(Size / 2), 0, -(Size / 2), 0 };
    int[] ApetureMaxX = { 0, (Size / 2), 0, (Size / 2) };
    int[] ApetureMinY = { -(Size / 2), -(Size / 2), 0, 0 };
    int[] ApetureMaxY = { 0, 0, (Size / 2), (Size / 2) };
    for (int x = 0; x < NewBitmap.Width; ++x)
    {
        for (int y = 0; y < NewBitmap.Height; ++y)
        {
            int[] RValues = { 0, 0, 0, 0 };
            int[] GValues = { 0, 0, 0, 0 };
            int[] BValues = { 0, 0, 0, 0 };
            int[] NumPixels = { 0, 0, 0, 0 };
            int[] MaxRValue = { 0, 0, 0, 0 };
            int[] MaxGValue = { 0, 0, 0, 0 };
            int[] MaxBValue = { 0, 0, 0, 0 };
            int[] MinRValue = { 255, 255, 255, 255 };
            int[] MinGValue = { 255, 255, 255, 255 };
            int[] MinBValue = { 255, 255, 255, 255 };
            for (int i = 0; i < 4; ++i)
            {
                for (int x2 = ApetureMinX[i]; x2 < ApetureMaxX[i]; ++x2)
                {
                    int TempX = x + x2;
                    if (TempX >= 0 && TempX < NewBitmap.Width)
                    {
                        for (int y2 = ApetureMinY[i]; y2 < ApetureMaxY[i]; ++y2)
                        {
                            int TempY = y + y2;
                            if (TempY >= 0 && TempY < NewBitmap.Height)
                            {
                                Color TempColor = TempBitmap.GetPixel(TempX, TempY);
                                RValues[i] += TempColor.R;
                                GValues[i] += TempColor.G;
                                BValues[i] += TempColor.B;
                                if (TempColor.R > MaxRValue[i])
                                {
                                    MaxRValue[i] = TempColor.R;
                                }
                                else if (TempColor.R < MinRValue[i])
                                {
                                    MinRValue[i] = TempColor.R;
                                }
 
                                if (TempColor.G > MaxGValue[i])
                                {
                                    MaxGValue[i] = TempColor.G;
                                }
                                else if (TempColor.G < MinGValue[i])
                                {
                                    MinGValue[i] = TempColor.G;
                                }
 
                                if (TempColor.B > MaxBValue[i])
                                {
                                    MaxBValue[i] = TempColor.B;
                                }
                                else if (TempColor.B < MinBValue[i])
                                {
                                    MinBValue[i] = TempColor.B;
                                }
                                ++NumPixels[i];
                            }
                        }
                    }
                }
            }
            int j = 0;
            int MinDifference = 10000;
            for (int i = 0; i < 4; ++i)
            {
                int CurrentDifference = (MaxRValue[i] - MinRValue[i]) + (MaxGValue[i] - MinGValue[i]) + (MaxBValue[i] - MinBValue[i]);
                if (CurrentDifference < MinDifference && NumPixels[i] > 0)
                {
                    j = i;
                    MinDifference = CurrentDifference;
                }
            }
 
            Color MeanPixel = Color.FromArgb(RValues[j] / NumPixels[j],
                GValues[j] / NumPixels[j],
                BValues[j] / NumPixels[j]);
            NewBitmap.SetPixel(x, y, MeanPixel);
        }
    }
    return NewBitmap;
}

The code above takes in an image as well as the size that you want the aperture to be. In turn it gives you a smoothed image. This filter technique is quite different from other blur filters as it produces a lot more stylized look. Hopefully this helps you out in some way.

Leave a Reply

Your email address will not be published. Required fields are marked *