Home Course Index << Prev Next >> PDF Version of this Page C2 Code Comments

Course IPCis: Image Processing with C#
Chapter C2: The Complete Code of the Histogram Project


Copyright © by V. Miszalok, last update: 09-05-2010

Copy all this code into an empty Form1.cs of a new Windows Application C#-project histo1 and clear Form1.Designer.cs and Program.cs.

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() );   }
  Brush bbrush = SystemBrushes.ControlText;
  Brush wbrush = new SolidBrush( Color.White );
  Pen   bpen   = SystemPens.ControlText;
  Pen   rpen   = new Pen( Color.Red );
  Bitmap bmp, bmp_binary, bmp_histo;
  BitmapData binaryData; //for Versions 2 and 3
  Byte[,] grayarray;     //2D-Byte-Array
  Int32[] Histogram = new Int32[256];
  Rectangle histo_r = new Rectangle( 0,0,257,101 );
  Graphics g, g_histo;
  Byte[]  ORmask = { 128,  64,  32,  16,   8,   4,   2,   1 };// 1 bit  each //for Version 3
  Byte[] ANDmask = { 127, 191, 223, 239, 247, 251, 253, 254 };// 7 bits each //for Version 3

  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 = "Histo1";
    SetStyle( ControlStyles.ResizeRedraw, true );
    Width  = 800;
    Height = 600;
  }

  private void MenuFileRead( object obj, EventArgs ea )
  { OpenFileDialog dlg = new OpenFileDialog();
    if ( dlg.ShowDialog() != DialogResult.OK ) return;
    try
    { Cursor.Current = Cursors.WaitCursor;
      bmp = (Bitmap)Image.FromFile( dlg.FileName );
      GenerateTheHistogram();
      Cursor.Current = Cursors.Arrow;
      Invalidate();
    } catch {}
  }

  private void GenerateTheHistogram()
  { if ( bmp == null ) return;
    //bmp_binary = new Bitmap( bmp.Width, bmp.Height, PixelFormat.Format32bppRgb  ); //Version 1
    //bmp_binary = new Bitmap( bmp.Width, bmp.Height, PixelFormat.Format32bppRgb  ); //Version 2
    bmp_binary = new Bitmap( bmp.Width, bmp.Height, PixelFormat.Format1bppIndexed ); //Version 3
    grayarray = new Byte[bmp.Height, bmp.Width];
    Color color;
    for ( Int32 y=0; y < bmp.Height; y++ )
      for ( Int32 x=0; x < bmp.Width; x++ )
      { color = bmp.GetPixel( x, y );
        Int32 gray = ( color.R + color.G + color.B ) / 3;
        grayarray[y, x] = (Byte)gray;
        Histogram[gray]++;
      }
    Int32 hmax = 0;
    for ( Int32 i=0; i < 256; i++ )
      if ( Histogram[i] > hmax ) hmax = Histogram[i];
    for ( Int32 i=0; i < 256; i++ )
      Histogram[i] = (100*Histogram[i]) / hmax;
    bmp_histo = new Bitmap( histo_r.Width, histo_r.Height, PixelFormat.Format32bppRgb );
    g_histo = Graphics.FromImage( bmp_histo );
    g_histo.FillRectangle( wbrush, 0,0,256,100 );
    g_histo.DrawString( "click here and move !", Font, bbrush, 1, 1 );
    for ( Int32 i=0; i < 256; i++ ) g_histo.DrawLine( bpen, i, 100, i, 100 - Histogram[i] );
    g_histo.DrawRectangle( rpen, 0,0,256,100 );
  }

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

  protected override void OnMouseMove( MouseEventArgs e )
  { if ( e.Button == MouseButtons.None ) return;
    if ( !histo_r.Contains( e.X, e.Y ) ) return;
    if ( bmp == null ) return;
    Byte threshold = (Byte)(e.X - histo_r.X);

    /*
    //Version 1 (no pointers but slow)***************************************
    for ( Int32 y=0; y < bmp.Height; y++ )
      for ( Int32 x=0; x < bmp.Width; x++ )
      { if ( grayarray[y ,x] > threshold )
             bmp_binary.SetPixel( x, y, Color.White );
        else bmp_binary.SetPixel( x, y, Color.Black );
      }
    //End of Version 1 ******************************************************
    */
    /*
    //Version 2 (fast pointers creating a memory wasting 32-bit binary image)*
    unsafe
    { //lock bmp_binary from being shifted in memory by the garbage collector
      binaryData = bmp_binary.LockBits( new Rectangle( 0,0,bmp.Width,bmp.Height ),
                             ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb );
      //Byte* p1fix, p1run =  fixed + running pointers to grayarray  = input image
      UInt32* p2fix, p2run; //fixed + running pointers to binaryData = output image
      fixed ( Byte* p1fix = grayarray )    // lock grayarray in memory
      { Byte* p1run = p1fix;               // running pointer to grayarray
        p2fix = (UInt32*)binaryData.Scan0; // pointer to output image
        for ( int y=0; y < bmp.Height; y++ )
        { p2run = p2fix + y * bmp.Width; //p2run points to first byte in row y
          for ( int x=0; x < bmp.Width; x++ )
          { if ( *p1run++ > threshold ) *p2run++ = 0xFFFFFF; // white
            else                        *p2run++ = 0;        // black
          } // end of for x
        }   // end of for y
      }     // end of fixed, end of p1fix, unlock grayarray
      bmp_binary.UnlockBits( binaryData ); //end of p2fix, unlock bmp_binary
    } // end of unsafe
    //End of Version 2 ******************************************************
    */

    //Version 3 (fast pointers creating a 1-bit binary image)****************
    unsafe
    { //lock bmp_binary from being shifted in memory by the garbage collector
      binaryData = bmp_binary.LockBits( new Rectangle( 0,0,bmp.Width,bmp.Height ),
                             ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed );
      Byte* p2fix, p2row, p2run;         // pointers to binaryData = output image
      fixed ( Byte* p1fix = grayarray )  // lock grayarray = input image in memory
      { Byte* p1run = p1fix;             // running pointer to grayarray
        p2fix = (byte*)binaryData.Scan0; // pointer to output image
        for ( int y=0; y < bmp.Height; y++ )
        { p2row = p2fix + y * binaryData.Stride; //p2row points to first byte in row y
          for ( int x=0; x < bmp.Width; x++ )
          { p2run = p2row + x / 8;
            if ( *p1run++ > threshold ) *p2run |=  ORmask[ x % 8 ];//set    1 bit
            else                        *p2run &= ANDmask[ x % 8 ];//remove 1 bit
          } // end of for x
        }   // end of for y
      }     // end of fixed, end of p1fix, unlock grayarray
      bmp_binary.UnlockBits( binaryData ); // end of p2fix, unlock bmp_binary
    } // end of unsafe
    //End of Version 3 ******************************************************

    g = CreateGraphics();
    g.DrawImage( bmp_binary, ClientRectangle );
    g.DrawImage( bmp_histo, histo_r );
    g.DrawLine( rpen, histo_r.X+threshold, histo_r.Y,
                      histo_r.X+threshold, histo_r.Y+histo_r.Height-1 );
  }

  protected override void OnMouseUp(MouseEventArgs e)
  { Invalidate(); }

  protected override void OnPaint( PaintEventArgs e )
  { if ( bmp == null )
    { e.Graphics.DrawString( "Open an Image File !", Font, bbrush, 0, 0 ); return; }
    e.Graphics.DrawImage( bmp, ClientRectangle );
    histo_r.X  = ClientRectangle.Width  - histo_r.Width  - 10;
    histo_r.Y  = ClientRectangle.Height - histo_r.Height - 10;
    e.Graphics.DrawImage( bmp_histo, histo_r );
  }
}