Home Index of Samples PDF Version of this Page

Code Samples:
The Fog Filter = Weighted Fast Average Filter in C#


Copyright © by V. Miszalok, last update: 2011-01-02

This code has been developed with Visual C# 2010 Express.
The algorithm accepts any rectangular filter sizes M≠N or M==N where M, N must be positive odd integers M < b0.Width, N < b0.Height.
It works with any image format that can be read and written with the GetPixel(..), SetPixel(..) methods of the Bitmap-class.
Adjust the desired blur effect by choosing 3 const-values in lines 9 - 11:
a) Blur increases with increasing filter sizes M and N.
b) Blur decreases with an extra weight extraCentralVotes≥0 which rises the voting power of the central pixel compared to its neighbors.

Create a new Windows-project "fog_filter". Delete the files Form1.Designer.cs and Program.cs from the project. Clear any prefabricated content from Form1.cs and replace it by the following code:

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;

public class Form1 : Form
{ [STAThread] static void Main() { Application.Run( new Form1() );   }
  Bitmap b0, b1;
  const Int32 M = 21, Mh = M/2;          //M must be an odd integer < b0.Width
  const Int32 N = 21, Nh = N/2, MN=M*N;  //N must be an odd integer < b0.Height
  const Int32 extraCentralVotes = MN/2;   //0 -> maximal fog, MN -> CentralVotes==PeripheralVotes
  float eCVRatio = (float)extraCentralVotes/(M*N);
  Byte [,] R0, G0, B0;
  String s;

  public Form1()
  { MenuItem miRead = new MenuItem( "&Read", new EventHandler( MenuFileRead ) );
    MenuItem miExit = new MenuItem( "&Exit", new EventHandler( MenuFileExit ) );
    MenuItem miFile = new MenuItem( "&File", new MenuItem[] { miRead, miExit } );
    Menu = new System.Windows.Forms.MainMenu( new MenuItem[] { miFile } );
    Text = "Fog Filter = Weighted Fast Average Filter";
    SetStyle( ControlStyles.ResizeRedraw, true );
    try { b0 = new Bitmap( typeof( Form1 ), "fog_filter.Butterfly.jpg" );
          byte_arrays_and_b1_image();
          fast_average_filter();
        } catch {}
    Width  = 800;
    Height = 600;
  }

  void MenuFileRead( object obj, EventArgs ea )
  { OpenFileDialog dlg = new OpenFileDialog();
    if ( dlg.ShowDialog() != DialogResult.OK ) return;
    b0 = (Bitmap)Image.FromFile( dlg.FileName );
    byte_arrays_and_b1_image();
    fast_average_filter();
    Invalidate();
  }

  void MenuFileExit( object obj, EventArgs ea )
  { Application.Exit(); }

  protected override void OnPaint( PaintEventArgs e )
  { Graphics g = e.Graphics;
    g.Clear( BackColor );
    try
    { g.DrawImage( b0, 0, 0, b0.Width, b0.Height );
      g.DrawImage( b1, b0.Width+10, 0 );
      g.DrawString( s, new Font( "Arial", 16 ), new SolidBrush( Color.Red ),
                    0, ClientRectangle.Height-120 );
    } catch{}
  }

    private void byte_arrays_and_b1_image()
  { R0 = new Byte [b0.Height, b0.Width];
    G0 = new Byte [b0.Height, b0.Width];
    B0 = new Byte [b0.Height, b0.Width];
    if ( b1 != null ) b1.Dispose();
    b1 = new Bitmap( b0 );
    for ( int y=0; y < b0.Height; y++ )
      for ( int x=0; x < b0.Width; x++ )
      { Color c = b0.GetPixel( x, y );
        R0[y,x] = (Byte)c.R;
        G0[y,x] = (Byte)c.G;
        B0[y,x] = (Byte)c.B;
        b1.SetPixel( x, y, Color.Black );
      }
  }

  private void fast_average_filter()
  { Int32 x, x0, x1, x2, x3;
    Int32 y, y0, y1, y2, y3;
    Int64 t0, t1;
    float Rf, Gf, Bf;
    //Intermediary arrays
    Int32[,] R1 = new Int32[b0.Height, b0.Width];
    Int32[,] G1 = new Int32[b0.Height, b0.Width];
    Int32[,] B1 = new Int32[b0.Height, b0.Width];
    Int32[,] R2 = new Int32[b0.Height, b0.Width];
    Int32[,] G2 = new Int32[b0.Height, b0.Width];
    Int32[,] B2 = new Int32[b0.Height, b0.Width];
    Cursor.Current = Cursors.WaitCursor;
    t0 = DateTime.Now.Ticks;
    //Horizontal integration Matrix0 -> Matrix1
    for ( y=0; y < b0.Height; y++ )
    { for ( x=0; x < M; x++ ) //first column = column[Mh]
      { R1[y,Mh] += R0[y,x];
        G1[y,Mh] += G0[y,x];
        B1[y,Mh] += B0[y,x];
      }
      for ( x0=0, x1=Mh, x2=Mh+1, x3=M; x3 < b0.Width; x0++, x1++, x2++, x3++ )
      { R1[y,x2] = R1[y,x1] - R0[y,x0] + R0[y,x3];
        G1[y,x2] = G1[y,x1] - G0[y,x0] + G0[y,x3];
        B1[y,x2] = B1[y,x1] - B0[y,x0] + B0[y,x3];
      }
    }
    //Vertical integration Matrix1 -> Matrix2
    for ( x=Mh; x < b0.Width-Mh; x++ )
    { for ( y=0; y < N; y++ ) //first row = row[Nh]
      { R2[Nh,x] += R1[y,x];
        G2[Nh,x] += G1[y,x];
        B2[Nh,x] += B1[y,x];
      }
      for ( y0=0, y1=Nh, y2=Nh+1, y3=N; y3 < b0.Height; y0++, y1++, y2++, y3++ )
      { R2[y2,x] = R2[y1,x] - R1[y0,x] + R1[y3,x];
        G2[y2,x] = G2[y1,x] - G1[y0,x] + G1[y3,x];
        B2[y2,x] = B2[y1,x] - B1[y0,x] + B1[y3,x];
      }

    }
    //reduction of filter strength by a single extra weight at the center
    if ( extraCentralVotes > 0 ) //if ( center_weight > 0 )
    { for ( y=0; y < b0.Height; y++ )
        for ( x=0; x < b0.Width; x++ )
        { R2[y,x] += extraCentralVotes * R0[y,x];
          G2[y,x] += extraCentralVotes * G0[y,x];
          B2[y,x] += extraCentralVotes * B0[y,x];
        }
    }
    //final output
    Int32 divisor = MN + extraCentralVotes;
    for ( y=Nh; y < b0.Height-Nh; y++ )
      for ( x=Mh; x < b0.Width-Mh; x++ )
      { Rf = R2[y,x]/divisor; Gf = G2[y,x]/divisor; Bf = B2[y,x]/divisor;
        b1.SetPixel( x, y, Color.FromArgb( Convert.ToInt32(Rf),
                                           Convert.ToInt32(Gf),
                                           Convert.ToInt32(Bf) ) );
      }
    t1 = DateTime.Now.Ticks;
    s = "Fast average filter with middle weight = Fog filter\r\n" +
        "Image:  " + b0.Width.ToString() + " x " + b0.Height.ToString() + "\r\n" +
        "Filter: " + M.ToString() + " x " + N.ToString() + "\r\n" +
        "Vote Ratio center/periphery = " +  String.Format( "{0:F1}", eCVRatio ) + " / 1\r\n" +
        "Filter Time: " + String.Format( "{0:F1}", (t1 - t0)/1000000f ) + " MegaTicks";
    Cursor.Current = Cursors.Arrow;
  }
}

Recommended experiments:
1) In line 9 of the code change const Int32 M = 21 to any odd integer 1 ≤ M < image.Width.
2) In line 10 of the code change const Int32 N = 21 to any odd integer 1 ≤ N < image.Height.
3) In line 11 of the code change const Int32 extraCentralVotes = MN/2 to any integer 0 ≤ extraCentralVotes < 2*MN.
4) Load different images via the File menu.

How to embed an arbitrary sample image into the code:
Copy all the code into an empty Form1.cs of a new Windows Application C#-project fog_filter and delete Form1.Designer.cs and Program.cs.
1) In the Solution Explorer window right click on fog_filter. A context menu opens.
2) Click Add. Click Existing Item. A file dialog box opens.
3) At the bottom of the box is a drop-down menu: Files of type:
4) Select Image Files (*.gif;*.jpg;...)
5) Choose an arbitrary image from your computer and leave the file dialog box with button Add.
6) The file name should now appear in the Solution Explorer tree. Right click this name.
7) A context menu opens. Click Properties. A Properties-window opens below the Solution Explorer window.
8) Click its first property: Build Action and set it to Embedded Resource.
9) Line 23 of the program below is: b0 = new Bitmap( typeof( Form1 ), "fog_filter.Butterfly.jpg" );
Replace Butterfly.jpg by the name of your image.