Home Course Index << Prev PDF Version of this Page

Course CVCis: Computer Vision with C#
Chapter C6: The Complete Code of the Fourier Transform Project


Copyright © by V. Miszalok, last update: 2011-09-13


Main Menu after start of VC# 2010 Express: File -> New Project... -> Visual Studio installed templates: Windows Forms Application
Name: fourier_transform1 -> Location: C:\temp -> Create directory for solution
: switch off -> OK
Delete two superfluous files: Form1.Designer.cs and Program.cs.
Replace the preprogrammed code of Form1.cs by:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Collections;
using System.Text;
using System.IO;

public class Form1 : Form
{ [STAThread] static void Main() { Application.Run( new Form1() ); }
  const Int32 xSize = 16;
  const Int32 ySize = 16;
  Byte[,] i0       = new Byte[ySize, xSize];// raster matrix
  Byte threshold   = 1;
  Brush[] brush    = new Brush[10];//for 10 different gray values
  Brush redbrush   = new SolidBrush( Color.Red   );
  Brush bluebrush  = new SolidBrush( Color.Blue  );
  Brush graybrush  = new SolidBrush( Color.Gray  );
  Brush greenbrush = new SolidBrush( Color.Green );
  Pen redpen1      = new Pen( Color.Red,   1 );
  Pen redpen5      = new Pen( Color.Red,   5 );
  Pen greenpen     = new Pen( Color.Green, 5 );
  Pen bluepen      = new Pen( Color.Blue,  5 );
  Pen yellowpen    = new Pen( Color.Yellow,5 );
  Pen whitepen;
  Font arial10     = new Font( "Arial", 10 );
  Font arial20     = new Font( "Arial", 20 );

  Button[] button = new Button[ySize];
  Int32 i, x, y, dx, dy, backtransform;
  Point p0, p1;
  struct crackcode { public Point start; public String cracks; }
  struct FPoint    { public float x;     public float y; }
  crackcode cc;
  Graphics g;
  ArrayList pointArray = new ArrayList();
  ArrayList phi1;
  float[] phi2, phi3, fr, fi;

  public Form1()
  { BackColor  = Color.White;
    Text       = "Fourier Transform";
    SetStyle( ControlStyles.ResizeRedraw, true );
    Width  = 800;
    Height = 600;
    for ( i=0; i < 10; i++ )// 10 brushes with 10 different gray values
      brush[i] = new SolidBrush( Color.FromArgb( i*25, i*25, i*25 ) );
    for ( y=0; y < ySize; y++ )//a column of buttons
    { button[y] = new Button();
      Controls.Add( button[y] );
      button[y].BackColor = Color.Gray;
      button[y].Text = "nothing";
      button[y].Name = y.ToString();
      button[y].Click += new EventHandler( do_it );
    }
    button[0].Name = button[0].Text = "FourierTransf";
    button[1].Name =                  "Back";
    button[2].Name = button[2].Text = "Clear";
    button[3].Name =                  "Threshold";
    button[4].Name = button[4].Text = "Noise";
    button[1].Text = "Back=0";
    button[3].Text = "Threshold=1";
    cc.cracks = "";
  } // end of constructor

  protected override void OnPaint( PaintEventArgs e )
  { g = e.Graphics;
    String s;
    Rectangle r = ClientRectangle;
    dx = r.Width / (xSize+2);
    dy = (r.Height - 2 * FontHeight ) / ySize;
    for ( y=0; y < ySize; y++ )
    { button[y].Top = y*dy+1;
      button[y].Left = xSize*dx+1;
      button[y].Width = r.Width - button[y].Left - 2;
      button[y].Height = dy-2;
    }
    button[1].Text = "Back=" + backtransform.ToString();
    button[3].Text = "Threshold="  + threshold.ToString();
    //draw the raster matrix
    for ( y=0; y < ySize; y++ )
      for ( x=0; x < xSize; x++ )
        g.FillRectangle( brush[i0[y,x]], x*dx, y*dy, dx, dy );
    //draw the initial invitation
    if ( cc.cracks.Length == 0 )
    { s = "Draw something -> FourierTransf -> BackTransf";
      g.DrawString( s, arial10, redbrush, (xSize*dx)/4, 0 );
      return;
    }
    //draw the crack code
    x = cc.start.X;
    y = cc.start.Y;
    for ( i = 0; i < cc.cracks.Length; i++ )
      switch ( cc.cracks[i] )
      { case 'e': g.DrawLine( redpen5, x*dx, y*dy, (x+1)*dx, y*dy ); x++; break;
        case 's': g.DrawLine( redpen5, x*dx, y*dy, x*dx, (y+1)*dy ); y++; break;
        case 'w': g.DrawLine( redpen5, x*dx, y*dy, (x-1)*dx, y*dy ); x--; break;
        case 'n': g.DrawLine( redpen5, x*dx, y*dy, x*dx, (y-1)*dy ); y--; break;
      }
    g.FillRectangle( bluebrush, x*dx-5, y*dy-5, 11, 11 );
    //draw some info into the white space below the image
    try
    { s = " No. of cracks = " + cc.cracks.Length.ToString() + " ";
      s += " No. of Fourier steps = " + fr.Length.ToString();
      g.DrawString( s, arial10, redbrush, 0, ySize*dy );
    } catch { return; }
    //draw both Fourier coefficient curves
    g.DrawString( "fr", arial20, greenbrush, 10, 10 );
    g.DrawString( "fi", arial20, bluebrush , 10, 40 );
    float x0, y0, x1, y1, y0fr, y1fr, y0fi, y1fi, y_zoom = 3;
    for ( i=0; i < fr.Length; i++ )
    { x0 =  i    * xSize*dx / fr.Length;
      x1 = (i+1) * xSize*dx / fr.Length;
      y0fr = y_zoom * fr[i  ] + r.Height/2;
      y0fi = y_zoom * fi[i  ] + r.Height/2;
      //fr[i+1], fi[i+1] can be beyond the arrays. If so, set them to 0.
      try   { y1fr = y_zoom * fr[i+1] + r.Height/2;
              y1fi = y_zoom * fi[i+1] + r.Height/2; }
      catch { y1fr = y1fi = r.Height/2; }
      g.DrawLine( greenpen, x0, y0fr, x1, y1fr );
      g.DrawLine( bluepen , x0, y0fi, x1, y1fi );
    }
    //draw the no.s of coefficents on the x-axis
    Int32 step = fr.Length < (ySize*dy / 10) ? 1 : 2;
    for ( i=0; i <= fr.Length; i+=step )
    { x0 =  i * xSize*dx / fr.Length;
      if ( i <= backtransform ) g.FillRectangle( graybrush, x0-4, r.Height/2-5, 17, 17 );
      g.DrawString( i.ToString(), arial10, redbrush, x0-4, r.Height/2-5 );
    }
    //create a polygon (with float x, y) from the back-transformed phi3
    const double angle_to_arcus = 2 * Math.PI / 360;
    double circular_angle_per_crack = 360.0f / cc.cracks.Length;
    double angle = 0;
    FPoint[] polygon_f = new FPoint[phi3.Length];
    float xmin, ymin, xmax, ymax;
    xmin = xmax = polygon_f[0].x = cc.start.X;
    ymin = ymax = polygon_f[0].y = cc.start.Y;
    for ( i=0; i < phi3.Length-1; i++ )
    { angle = angle + angle_to_arcus * (phi3[i] - circular_angle_per_crack);
      x1 = polygon_f[i+1].x = polygon_f[i].x - (float)Math.Cos( angle );
      y1 = polygon_f[i+1].y = polygon_f[i].y - (float)Math.Sin( angle );
      if ( x1 > xmax ) xmax = x1;
      if ( y1 > ymax ) ymax = y1;
      if ( x1 < xmin ) xmin = x1;
      if ( y1 < ymin ) ymin = y1;
    }
    //The following 6 lines keep the complete polygon inside the window
    float w = xmax - xmin + cc.start.X;
    float h = ymax - ymin + cc.start.Y;
    if (xmin < 0 ) for ( i=0; i < phi3.Length; i++ ) polygon_f[i].x -= xmin;
    if (ymin < 0 ) for ( i=0; i < phi3.Length; i++ ) polygon_f[i].y -= ymin;
    if (w > xSize) for ( i=0; i < phi3.Length; i++ ) polygon_f[i].x *= xSize/w;
    if (h > ySize) for ( i=0; i < phi3.Length; i++ ) polygon_f[i].y *= ySize/h;
    //draw the polygon
    for ( i=0; i < phi3.Length-1; i++ )
    { x0 = polygon_f[i].x; x1 = polygon_f[i+1].x;
      y0 = polygon_f[i].y; y1 = polygon_f[i+1].y;
      g.DrawLine( yellowpen, x0*dx, y0*dy, x1*dx, y1*dy );
    }
    //close the polygon
    x0 = polygon_f[phi3.Length-1].x; x1 = polygon_f[0].x;
    y0 = polygon_f[phi3.Length-1].y; y1 = polygon_f[0].y;
    g.DrawLine( yellowpen, x0*dx, y0*dy, x1*dx, y1*dy );
  } // end of OnPaint(...)

  protected override void OnMouseDown( MouseEventArgs e )
  { p0.X = e.X;
    p0.Y = e.Y;
    pointArray.Add( p0 );
    whitepen = new Pen( Color.White, dx < dy ? dx : dy );
      whitepen.StartCap = whitepen.EndCap = System.Drawing.Drawing2D.LineCap.Round;
  }

  protected override void OnMouseMove( MouseEventArgs e )
  { if ( e.Button == MouseButtons.None ) return;
    p1.X = e.X;
    p1.Y = e.Y;
    //Insert an additional point if the distance is too far
    if ( Math.Abs( p1.X - p0.X ) >= dx || Math.Abs( p1.Y - p0.Y ) >= dy )
      pointArray.Add( new Point( (p1.X + p0.X)/2, (p1.Y + p0.Y)/2 ) );
    pointArray.Add( p1 );
    g = this.CreateGraphics();
    g.DrawLine( whitepen, p0, p1 );
    p0 = p1;
  }

  private void do_it( object sender, System.EventArgs e )
  { switch( ((Button)sender).Name)
    { case "Threshold"://*************************************
        if ( ++threshold > 9 ) threshold = 1;
        button[3].Text = "Threshold=" + threshold.ToString();
        cc.cracks =""; break;
      case "Noise"://****************************************
        Random random = new Random();
        for ( y=0; y < ySize; y++ )
          for ( x=0; x < xSize; x++ )
          { Int32 noise =  random.Next() % 3 - 1;//gives -1 or 0 or +1
            noise += i0[y,x];//add former gray value
            if (noise < 0)      i0[y,x] = 0;
            else if (noise > 9) i0[y,x] = 9;
            else                i0[y,x] = (Byte)noise;
          }
        cc.cracks =""; break;
      case "Clear"://*******************************************
        for ( y=0; y < ySize; y++ )
          for ( x=0; x < xSize; x++ ) i0[y,x] = 0;
        backtransform = threshold = 1;
        cc.cracks =""; pointArray.Clear(); break;
      case "FourierTransf"://***********************************
        //Digitize*****************************************
        for ( i = 0; i < pointArray.Count; i++ )
        {  Point vertex = (Point)pointArray[i];
           x = vertex.X / dx;
           y = vertex.Y / dy;
           try { if ( i0[y,x] < 9 ) i0[y,x]++; } catch{};
        }
        //Crack Code 8*****************************************
        //Search for a vertical start crack
        Byte leftpix;
        for ( y=0; y < ySize; y++ )
          for ( x=0; x < xSize; x++ )
          { if ( x > 0 ) leftpix = i0[y,x-1]; else leftpix = 0;
            if ( leftpix < threshold && i0[y,x] >= threshold )
            { cc.start.X = x; cc.start.Y = y; goto start_crack_found; }
          }
        return; //nothing to start with
        start_crack_found:
        Int32 xmin = x, xmax = x, ymin = y, ymax = ++y;
        phi1 = new ArrayList(); phi1.Add(-90);
        System.Text.StringBuilder cracks = new System.Text.StringBuilder();
        cracks.Append('s');
        Char last_crack = 's';
        do
        { switch ( last_crack )
          { case 'e': if ( x == xSize )                            { phi1.Add(-90); goto n; }
                      if ( y <  ySize && i0[y  ,x  ] >= threshold) { phi1.Add(+90); goto s; }
                      if (               i0[y-1,x  ] >= threshold) { phi1.Add(  0); goto e; }
                                                                   { phi1.Add(-90); goto n; }

            case 's': if ( y == ySize )                            { phi1.Add(-90); goto e; }
                      if ( x >  0     && i0[y  ,x-1] >= threshold) { phi1.Add(+90); goto w; }
                      if (               i0[y  ,x  ] >= threshold) { phi1.Add(  0); goto s; }
                                                                   { phi1.Add(-90); goto e; }

            case 'w': if ( x == 0 )                                { phi1.Add(-90); goto s; }
                      if ( y >  0     && i0[y-1,x-1] >= threshold) { phi1.Add(+90); goto n; }
                      if (               i0[y  ,x-1] >= threshold) { phi1.Add(  0); goto w; }
                                                                   { phi1.Add(-90); goto s; }

            case 'n': if ( y == 0 )                                { phi1.Add(-90); goto w; }
                      if ( x <  xSize && i0[y-1,x  ] >= threshold) { phi1.Add(+90); goto e; }
                      if (               i0[y-1,x-1] >= threshold) { phi1.Add(  0); goto n; }
                                                                   { phi1.Add(-90); goto w; }
          }
          e: last_crack = 'e'; cracks.Append( 'e' ); x++; continue;
          s: last_crack = 's'; cracks.Append( 's' ); y++; continue;
          w: last_crack = 'w'; cracks.Append( 'w' ); x--; continue;
          n: last_crack = 'n'; cracks.Append( 'n' ); y--; continue;
        } while ( x != cc.start.X || y != cc.start.Y ); //end of do
        cc.cracks = cracks.ToString();
        Int32 N = cc.cracks.Length;
        //Bad property of function phi1: All phi1[i] sum up to -360 degrees
        phi2 = new float[N]; //Fourier transform needs float values
        phi3 = new float[N]; //Back transform creates float values
        float circular_angle_per_crack = 360.0f / N;
        for ( i=0; i < N; i++ )//Construction of a phi2 with a sum of 0
          phi2[i] = (Int32)phi1[i] + circular_angle_per_crack;
        //Fourier Transform*****************************************************************
        fr = new float[N/2+1];
        fi = new float[N/2+1];
        FourierTransform( phi2, fr, fi ); //output = fr, fi
        //just for fun: immediate back transform
        backtransform = fr.Length;
        FourierBackTransform( fr, fi, phi3, backtransform ); //output = phi3
        break;
      case "Back"://*************************************
        if ( cc.cracks.Length == 0 ) return;
        if ( ++backtransform > fr.Length ) backtransform = 0;
        FourierBackTransform( fr, fi, phi3, backtransform ); //output = phi3
        break;
    } // end of switch, end of all cases
    Invalidate();
  } // end of protected void do_it( object sender, System.EventArgs e )

  private void FourierTransform( float[] a, float[] fr, float[] fi )
  { int Na = a.Length, Nf = fr.Length;
    float sum_r, sum_i; fr[0] = fi[0] = 0;
    for ( i=0; i < Na; i++ ) fr[0] += a[i]; fr[0] /= Na;//fr[0] is the average = 0.
    double dpi_div_N, x1_dpi_div_N, x2_x1_dpi_div_N;
    dpi_div_N = 2 * Math.PI / Na;
    for ( int x1 = 1; x1 < Nf; x1++ )
    { sum_r = sum_i = 0;
      x1_dpi_div_N = (double)x1 * dpi_div_N;
      for ( int x2 = 0; x2 < Na; x2++ )
      { x2_x1_dpi_div_N = (double)x2 * x1_dpi_div_N;
        sum_r += a[x2] * (float)Math.Cos( -x2_x1_dpi_div_N );
        sum_i += a[x2] * (float)Math.Sin( -x2_x1_dpi_div_N );
      }
      fr[x1] = 2 * sum_r / (float)Na;
      fi[x1] = 2 * sum_i / (float)Na;
    }
    fr[Nf-1] /= 2; fi[Nf-1] /= 2;
  } // end of private void FourierTransform(...)

  private void FourierBackTransform( float[] fr, float[] fi, float[] a, int Nf )
  { if ( Nf > fr.Length ) return;
    int Na = a.Length;
    Array.Clear( a, 0, a.Length );
    double dpi_div_N, x1_dpi_div_N, x2_x1_dpi_div_N;
    dpi_div_N = 2 * Math.PI / Na;
    for ( int x1 = 0; x1 < Na; x1++ )
    { x1_dpi_div_N = (double)x1 * dpi_div_N;
      for ( int x2 = 0; x2 < Nf; x2++ )
      { x2_x1_dpi_div_N = (double)x2 * x1_dpi_div_N;
         a[x1] += fr[x2] * (float)Math.Cos( x2_x1_dpi_div_N ) -
                  fi[x2] * (float)Math.Sin( x2_x1_dpi_div_N );
      }
    }
  } // end of private void FourierBackTransform(...)
}