HiveBrain v1.2.0
Get Started
← Back to all entries
snippetcsharpMinor

Applying a 'mean filter'

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
applyingmeanfilter

Problem

I am just trying to apply a "mean filter" with C#. The working code below gets window size (3,5,7,9,...) from textBoxWinSize and applies the mean filter.

I am new and I want your comments and suggestions about my code so I can improve myself.

Can I make it faster? How? Is there any unnecessary or absurd part in the code? Can it be improved in any other way?

I do not care about border pixels for now.

private void buttonApplyFilter_Click(object sender, EventArgs e)
{
    int R, G, B,aveR,aveG,aveB;
    double sumR,sumG,sumB;
    Color pixel_value;
    Bitmap bmp = new Bitmap(bmpOrig);
    int WinSize = Int32.Parse(textBoxWinSize.Text);
    int col,row,n,m;
    for (col = (WinSize-1); col < (bmp.Width-WinSize); col++)
        for (row = (WinSize-1); row < (bmp.Height-WinSize); row++)
        {
            sumB = 0;
            sumG = 0;
            sumR = 0;
            for (m=-(WinSize-1)/2; m<=(WinSize-1)/2;m++)
                for (n=-(WinSize-1)/2;n<=(WinSize-1)/2;n++)
                {
                    pixel_value = bmp.GetPixel(col+m, row+n);
                    R = (pixel_value.R);
                    G =(pixel_value.G);
                    B = (pixel_value.B);
                    sumR = sumR + R;
                    sumG = sumG + G;
                    sumB = sumB + B;
                }
            aveR = (int)(sumR / (WinSize * WinSize));
            aveG = (int)( sumG / (WinSize * WinSize)); 
            aveB = (int)( sumB / (WinSize * WinSize));
            bmp.SetPixel(col, row, Color.FromArgb(aveR, aveG, aveB));
        }
    pictureBox1.Image = bmp;

}

Solution

First up, I think you're doing too much in the button click handler. The logic for applying the mean filter should be moved to its own method, something like

private static Bitmap ApplyMeanFilter(Bitmap input, int windowSize)


Now it can be re-used and unit tested more easily.

I think this line is wrong

pixel_value = bmp.GetPixel(col+m, row+n)


in that bmp should be bmpOrig. The reasoning is that you're changing pixels in bmp as you go, which will have an effect on pixels that are processed later. That is, they won't be assigned the mean of their original surrounding pixels.

Assuming the bmp should be bmpOrig, we can optimise this quite a bit.

Say that instead of a bitmap, we have a 2D array of ints.

input

\begin{array}{c | c c c c}
& 0 & 1 & 2 & 3 \\
\hline
0 & 1 & 2 & 3 & -1 \\
1 & 4 & 1 & 6 & -3 \\
2 & 4 & 5 & 9 & -3 \\
3 & 4 & -2 & 6 & -3
\end{array}

We can compute another 2D array such that the \$(i, j)\$-th entry is the sum of entries in the submatrix \$(0, 0), (i, j)\$:

var sums = new int[height, width];
for (var i = 0; i  0 ? sums[i - 1, j] + sum : sum;
    }
}


sums

\begin{array}{c | c c c c}
& 0 & 1 & 2 & 3 \\
\hline
0 & 1 & 3 & 6 & 5 \\
1 & 5 & 8 & 17 & 13 \\
2 & 9 & 17 & 35 & 28 \\
3 & 13 & 19 & 43 & 33
\end{array}

Now suppose we want to find the sum of the values in the 3x3 square around entry \$(i, j) = (2, 2)\$:

\begin{array}{c | c c c c}
& 0 & 1 & 2 & 3 \\
\hline
0 & 1 & 2 & 3 & -1 \\
1 & 4 & 1 & 6 & -3 \\
2 & 4 & 5 & \textbf{9} & -3 \\
3 & 4 & -2 & 6 & -3
\end{array}

We can do this quickly using the matrix we just computed. The answer is given by

sums[3, 3] - sums[3, 0] - sums[0, 3] + sums[0, 0]
= 33 - 13 - 5 + 1
= 16


In general, if halfWindow = (windowSize - 1) / 2 then the sum of values in the windowSize * windowSize square around entry \$(i, j)\$ is given by

sums[i + halfWindow,     j + halfWindow] -
sums[i - halfWindow - 1, j + halfWindow] -
sums[i + halfWindow,     j - halfWindow - 1] +
sums[i - halfWindow - 1, j - halfWindow - 1]


The beauty is, computing the sum of the surrounding windowSize * windowSize values this way is independent of windowSize.

Getting back to the original problem of bitmaps, let's suppose we have a struct that will let us sum up the RGB values of the pixels. I named it Clr because I'm not very imaginative today; I'm sure you can think of a better name. Let's also assume we have operators +, - and / defined in the obvious way.

Putting it all together would look something like this

var sums = new Clr[input.Height, input.Width];
for (var i = 0; i  0 ? sums[i - 1, j] + sum : sum;
    }
}

var output = new Bitmap(input);
var windowArea = windowSize * windowSize;
var halfWindow = (windowSize - 1) / 2;
for (var i = windowSize - 1; i < input.Height - windowSize; i++)
{
    for (var j = windowSize - 1; j < input.Width - windowSize; j++)
    {
        var clr = sums[i + halfWindow,     j + halfWindow] -
                  sums[i - halfWindow - 1, j + halfWindow] -
                  sums[i + halfWindow,     j - halfWindow - 1] +
                  sums[i - halfWindow - 1, j - halfWindow - 1];

        output.SetPixel(j, i, (clr / windowArea).ToColor());
    }
}

return output;


Running this on a 256x256 image, I got the following timings

windowSize       this               original
 3          00:00:00.0910750    00:00:00.4720078
 5          00:00:00.0899366    00:00:01.2674758
 7          00:00:00.0853111    00:00:02.2569419
 9          00:00:00.0938911    00:00:03.4408123
11          00:00:00.0840854    00:00:04.9614386


Other things to try that might improve performance:

  • Use a Clr[][] instead of a Clr[,]. See Why are multi-dimensional


arrays in .NET slower than normal
arrays?

  • Doing the above will let you move sums[i + halfWindow] and sums[i - halfWindow - 1] into variables outside the inner loop.



  • Compute the first row of sums separately to remove the conditional in this line sums[i, j] = i > 0 ? sums[i - 1, j] + sum : sum;

Code Snippets

private static Bitmap ApplyMeanFilter(Bitmap input, int windowSize)
pixel_value = bmp.GetPixel(col+m, row+n)
var sums = new int[height, width];
for (var i = 0; i < height; i++)
{
    var sum = 0;
    for (var j = 0; j < width; j++)
    {
        sum += input[i, j];
        sums[i, j] = i > 0 ? sums[i - 1, j] + sum : sum;
    }
}
sums[3, 3] - sums[3, 0] - sums[0, 3] + sums[0, 0]
= 33 - 13 - 5 + 1
= 16
sums[i + halfWindow,     j + halfWindow] -
sums[i - halfWindow - 1, j + halfWindow] -
sums[i + halfWindow,     j - halfWindow - 1] +
sums[i - halfWindow - 1, j - halfWindow - 1]

Context

StackExchange Code Review Q#70124, answer score: 4

Revisions (0)

No revisions yet.