Click here to Skip to main content
15,895,142 members
Articles / Programming Languages / C#

Image Recognition with Neural Networks

Rate me:
Please Sign up or sign in to vote.
4.82/5 (89 votes)
30 Oct 2007CPOL4 min read 1M   46.2K   286  
This article contains a brief description of BackPropagation Artificial Neural Network and its implementation for Image Recognition
��/*##########################################################################

 * 

 * FormANN.cs

 * -------------------------------------------------------------------------

 * By

 * Murat FIRAT, June 2007

 * muratti24@gmail.com

 * 

 * -------------------------------------------------------------------------

 * Description:

 * FormANN.cs implements the interface that uses backpropagation classes.

 * 

 * -------------------------------------------------------------------------

 * Notes:

 * To train the B.P.N. Network there must be a folder [in the same directory

 * of the .exe ] named "PATTERNS" which contains one folder[containing images] 

 * for each pattern.(For example, for english alfhabet, in "PATTERNS" directory 

 * there must be folders, namely "0","1","2"...."Z" each of which contains 

 * that letter's images). You can change this folder from settings tab.

 * 

 * -------------------------------------------------------------------------

 ###########################################################################*/





using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using System.IO;

using System.Collections;

using System.Threading;

using ANN;



//BPNN: BackPropagation Neural Network

namespace BPNN

{

    public partial class FormANN : Form

    {

        private BP1Layer Classifier1;

        private BP2Layer Classifier2;

        private BP3Layer Classifier3;



        private double[] InputFromUser;

        private List<Point> PointsOnPanel;



        private int AvImageHeigh;

        private int AvImageWidth;

        //private int PreInputNumber = AvImageHeigh * AvImageWidth;//not needed

        private int InputNumber = 250;

        private int HiddenNumber = 100;

        private int OutputNumber;

        private double MaxError = 1.1;

        private int LayerNum=1;

        private string PatternsDirectory = "PATTERNS";



        ArrayList TrainingSet;

        ArrayList TrainingValues;        



        //to draw characters on panel

        private bool MouseMoving = false;

        private int PanelImgeHeight;

        private int PanelImgeWidth;



        //for training process

        Thread ThreadTrain;

        private bool StopTraining = false;



        public FormANN()

        {

            InitializeComponent();

            InitializeDataMembers();

            InitializeSettings();

        }



        //clean (any) threads

        private void FormANN_FormClosing(object sender, FormClosingEventArgs e)

        {

            System.Environment.Exit(System.Environment.ExitCode);

        }



        //Create Instances of Data Members

        private void InitializeDataMembers()

        {

            try

            {

                textBoxState.AppendText("Initializing..");



                string[] Dirs = Directory.GetDirectories(PatternsDirectory);

                OutputNumber = Dirs.Length;

                int AvHeight = 0, AvWidth = 0, PNum = 0;

                foreach (string Dir in Dirs)

                {

                    foreach (string file in Directory.GetFiles(Dir))

                    {

                        if (Path.GetExtension(file) == ".bmp" )

                        {

                            Bitmap BM = new Bitmap(file);

                            AvHeight += BM.Height;

                            AvWidth += BM.Width;

                            PNum++;

                            BM.Dispose();

                        }

                    }

                }



                AvHeight /= PNum;

                AvWidth /= PNum;



                AvImageHeigh = AvHeight;

                AvImageWidth = AvWidth;



                TrainingSet = new ArrayList(PNum);

                TrainingValues = new ArrayList(PNum);

                                

                foreach (string Dir in Dirs)

                {

                    foreach (string file in Directory.GetFiles(Dir))

                    {

                        if (Path.GetExtension(file) == ".bmp")

                        {

                            Bitmap BM = new Bitmap(file);                           

                            TrainingSet.Add(IMGToDoubleArr(BM, AvWidth, AvHeight));

                            string[] Folders = Dir.Split('\\');

                            TrainingValues.Add(Folders[Folders.Length-1]);//the last folder                           

                            BM.Dispose();

                        }

                    }

                }



                PanelImgeWidth = TrackBarWidth.Value;

                PanelImgeHeight = 30 - TrackBarHeight.Value;



                pictureBoxInput.Height = AvImageHeigh;

                pictureBoxInput.Width = AvImageWidth;

                

                InputFromUser = new double[AvImageHeigh * AvImageWidth];

                PointsOnPanel = new List<Point>();



                Classifier1 = new BP1Layer(AvImageHeigh * AvImageWidth, OutputNumber);

                Classifier2 = new BP2Layer(AvImageHeigh * AvImageWidth, InputNumber, OutputNumber);

                Classifier3 = new BP3Layer(AvImageHeigh * AvImageWidth, InputNumber, HiddenNumber, OutputNumber);



                textBoxState.AppendText("Done!\r\n");

            }

            catch (Exception Ex)

            {                

                MessageBox.Show("Error Initializing Network Defaults: " + Ex.Message);



                textBoxState.AppendText("Error Initializing..\r\n");

            }

        }



        //Fill Settings Panel

        private void InitializeSettings()

        {

            textBoxState.AppendText("Initializing Settings..");



            comboBoxLayers.Items.Clear();

            comboBoxLayers.Items.Add(1);

            comboBoxLayers.Items.Add(2);

            comboBoxLayers.Items.Add(3);

            comboBoxLayers.SelectedIndex = 0;



            textBoxInputUnit.Text = InputNumber.ToString();

            textBoxHiddenUnit.Text = HiddenNumber.ToString();

            textBoxOutputUnit.Text = OutputNumber.ToString();

            textBoxMaxError.Text = MaxError.ToString();



            textBoxAvHeight.Text = AvImageHeigh.ToString();

            textBoxAvWidth.Text = AvImageWidth.ToString();



            PatternsDirectory = Path.GetFullPath(PatternsDirectory);

            textBoxTrainingBrowse.Text=PatternsDirectory ;



            textBoxState.AppendText("Done!\r\n");

        }



        /*

         * Convert Image To Matrix of Double.

         * Note: 

         * I Preferred Scaling Images to Average Height and Width 

         * of Training Images to Minimize Loss of Data. Trying to 

         * Recognize Smaller Images (Smaller Than Training Images)

         * May Not Give Correct Result in This Way.        

         */

        private double[] IMGToDoubleArr(Bitmap BM, int Col, int Row)

        {

            double HRate = ((Double)Row / BM.Height);

            double WRate = ((Double)Col / BM.Width);

            double[] Result = new double[Col * Row];

            for (int r = 0; r < Row; r++)

            {

                for (int c = 0; c < Col; c++)

                {

                    Color color = BM.GetPixel((int)(c / WRate), (int)(r / HRate));

                    Result[r * Col + c] =1- (color.R * .3 + color.G * .59 + color.B * .11) / 255;

                }

            }

            return Result;

        }                



        /*

         * Convert Matrix of Double to Image.

         * The note above is also valid for this one.

         */

        private Bitmap DoubleArrToIMG(double[] input,int DHeight,int DWidth,

                                                     int BHeight, int BWidth)

        {

            double HRate = ((double)BHeight / DHeight);

            double WRate = ((double)BWidth / DWidth);

            Bitmap BM = new Bitmap(BWidth, BHeight);



            for (int i = 0; i < BHeight; i++)

            {

                for (int j = 0; j < BWidth; j++)

                {

                    int x =(int)((double) j / WRate);

                    int y =(int)((double) i / HRate);



                    double temp = input[y * DWidth + x];

                    BM.SetPixel(j, i, Color.FromArgb((int)((1 - temp) * 255), (int)((1 - temp) * 255), (int)((1 - temp) * 255)));

                                    

                }

            }

            return BM;

        }



        private void radioButtonDraw_CheckedChanged(object sender, EventArgs e)

        {

            if (radioButtonDraw.Checked)

            {

                textBoxBrowse.Enabled = false;

                buttonBrowse.Enabled = false;



                radioButtonDraw.Checked = true;

                groupBoxDrawing.Enabled = true;

                buttonClear.Enabled = true;



                InputFromUser = new double[AvImageHeigh * AvImageWidth];

                

            }

        }



        private void radioButtonBrowse_CheckedChanged(object sender, EventArgs e)

        {

            if (radioButtonBrowse.Checked)

            {

                textBoxBrowse.Enabled = true;

                buttonBrowse.Enabled = true;



                radioButtonDraw.Checked = false;

                groupBoxDrawing.Enabled = false;

                buttonClear.Enabled = false;



                InputFromUser = new double[AvImageHeigh * AvImageWidth];

                PointsOnPanel.Clear();

                PanelDrawing.Invalidate();

            }

        }



       

        private void TrackBarWidth_Scroll(object sender, EventArgs e)

        {

            PanelImgeWidth = TrackBarWidth.Value;

            if (PanelImgeWidth < 10)

            {

                TrackBarWidth.Value = 10;

                PanelImgeWidth = 10;

            }

            InputFromUser = new double[AvImageHeigh * AvImageWidth];

            PointsOnPanel.Clear();



            PanelDrawing.Invalidate();



        }



        private void TrackBarHeight_Scroll(object sender, EventArgs e)

        {

            PanelImgeHeight = TrackBarHeight.Maximum - TrackBarHeight.Value;

            if (PanelImgeHeight < 15)

            {

                TrackBarHeight.Value = TrackBarHeight.Maximum - 15;

                PanelImgeHeight = 15;

            }

            InputFromUser = new double[AvImageHeigh * AvImageWidth];

            PointsOnPanel.Clear();          



            PanelDrawing.Invalidate();



        }



        private void DrawingPanel_Paint(object sender, PaintEventArgs e)

        {

            Graphics graphicsObject = e.Graphics;



            Brush BR1 = new SolidBrush(Color.Black);

            Brush BR2 = new SolidBrush(Color.White);



            graphicsObject.FillRectangle(BR2, 0, 0, PanelImgeWidth * (PanelDrawing.Width / TrackBarWidth.Maximum),

                                                    PanelImgeHeight * (PanelDrawing.Height / TrackBarHeight.Maximum));



            foreach (Point p in PointsOnPanel)

            {

                graphicsObject.FillRectangle(BR1, p.X, p.Y, (PanelDrawing.Width / TrackBarWidth.Maximum),

                                                            (PanelDrawing.Height / TrackBarHeight.Maximum));

            }

            graphicsObject.Dispose();

        }



        private void DrawingPanel_MouseMove(object sender, MouseEventArgs e)

        {

            if (MouseMoving)

            {

                //map to actual input(20x30)

                int X = (int)(e.X * ((double)TrackBarWidth.Maximum / PanelDrawing.Width));

                int Y = (int)(e.Y * ((double)TrackBarHeight.Maximum / PanelDrawing.Height));



                //select as to trackbar(PanelImgeHeightxPanelImgeWidth)

                if (X < 0 || PanelImgeHeight <= Y || Y < 0 || PanelImgeWidth<= X)

                {

                    return;

                }



                //map to average dimensions(AvImageHeighxAvImageWidth)

                int y = (int)(Y * ((double)AvImageHeigh / PanelImgeHeight));

                int x = (int)(X * ((double)AvImageWidth / PanelImgeWidth));



                InputFromUser[y * AvImageWidth + x] = 1;



                //add the point as to previous dimensions(PanelDrawing.WidthxPanelDrawing.Height)

                PointsOnPanel.Add(new Point(X * (PanelDrawing.Width / TrackBarWidth.Maximum),

                                     Y * (PanelDrawing.Height / TrackBarHeight.Maximum)));

                PanelDrawing.Invalidate();

            }

        }



        private void DrawingPanel_MouseUp(object sender, MouseEventArgs e)

        {

            MouseMoving = false;

        }



        private void DrawingPanel_MouseDown(object sender, MouseEventArgs e)

        {

            MouseMoving = true;

        }

        

        private void buttonClear_Click(object sender, EventArgs e)

        {

            InputFromUser = new double[AvImageHeigh * AvImageWidth];

            PointsOnPanel.Clear();

            PanelDrawing.Invalidate();

        }



        private void buttonTrainingBrowse_Click(object sender, EventArgs e)

        {

            FolderBrowserDialog FD = new FolderBrowserDialog();

            FD.SelectedPath = PatternsDirectory;

            if (FD.ShowDialog() == DialogResult.OK)

            {

                textBoxTrainingBrowse.Text = FD.SelectedPath;

            }

        }

        



        private void comboBoxLayers_SelectedIndexChanged(object sender, EventArgs e)

        {

            if (comboBoxLayers.SelectedIndex == 0)

            {

                textBoxInputUnit.Enabled = false;

                textBoxHiddenUnit.Enabled = false;

            }

            else if (comboBoxLayers.SelectedIndex == 1)

            {

                textBoxInputUnit.Enabled = true;

                textBoxHiddenUnit.Enabled = false;

            }

            else if (comboBoxLayers.SelectedIndex == 2)

            {

                textBoxInputUnit.Enabled = true;

                textBoxHiddenUnit.Enabled = true;

            }

        }



        private void buttonSaveSettings_Click(object sender, EventArgs e)

        {

            try

            {

                LayerNum = comboBoxLayers.SelectedIndex + 1;

                MaxError = Double.Parse(textBoxMaxError.Text);

                if (MaxError <= 0)

                {

                    throw new Exception("Maximum Error Must Be Positive");

                }



                InputNumber = Int32.Parse(textBoxInputUnit.Text);

                if (InputNumber <= 0)

                {

                    throw new Exception("Input Unit Number Must Be Positive");

                }



                HiddenNumber = Int32.Parse(textBoxHiddenUnit.Text);

                if (HiddenNumber <= 0)

                {

                    throw new Exception("Hidden Unit Number Must Be Positive");

                }



                OutputNumber = Int32.Parse(textBoxOutputUnit.Text);

                if (OutputNumber <= 0)

                {

                    throw new Exception("Output Unit Number Must Be Positive");

                }



                PatternsDirectory = textBoxTrainingBrowse.Text;

                if (!Directory.Exists(PatternsDirectory))

                {

                    throw new Exception("Directory Does Not Exist: " + PatternsDirectory);

                }



                AvImageHeigh = Int32.Parse(textBoxAvHeight.Text);

                if (AvImageHeigh <= 0)

                {

                    throw new Exception("Average Image Height Must Be Positive");

                }



                AvImageWidth = Int32.Parse(textBoxAvWidth.Text);

                if (AvImageWidth <= 0)

                {

                    throw new Exception("Average Image Width Must Be Positive");

                }





                InitializeDataMembers();



                textBoxOutputUnit.Text = OutputNumber.ToString();               

                textBoxAvHeight.Text = AvImageHeigh.ToString();

                textBoxAvWidth.Text = AvImageWidth.ToString();



                buttonRecognize.Enabled = false;

                buttonSave.Enabled = false;



                textBoxState.AppendText("Saved Settings. (Re)Train Network To Apply New Settings.\r\n");

            }

            catch (Exception Ex)

            {

                MessageBox.Show("Error Saving Settings: " + Ex.Message);

            }

        }

        

        

        

        private void buttonTrain_Click(object sender, EventArgs e)

        {

            buttonStop.Visible = true;



            ThreadTrain = new Thread(new ThreadStart(TrainNetwork));

            ThreadTrain.Start();            

        }



        private void TrainNetwork()

        {

            double CurrentError = 999;

            int iter = 0;

            do

            {

                switch (LayerNum)

                {

                    case 1:

                        Classifier1.Train(TrainingSet, TrainingValues);

                        CurrentError = Classifier1.GetTotalError(TrainingSet, TrainingValues);

                        break;

                    case 2:

                        Classifier2.Train(TrainingSet, TrainingValues);

                        CurrentError = Classifier2.GetTotalError(TrainingSet, TrainingValues);

                        break;

                    case 3:

                        Classifier3.Train(TrainingSet, TrainingValues);

                        CurrentError = Classifier3.GetTotalError(TrainingSet, TrainingValues);

                        break;

                    default:

                        break;

                }

                textBoxState.AppendText("Training..Current Error: " + CurrentError.ToString("###.##") + ", Expected: " + MaxError.ToString("###.##") + ", Iteration: " + (++iter).ToString() + "\r\n");

                

            } while (CurrentError > MaxError && !StopTraining);



            buttonRecognize.Enabled = true;

            buttonSave.Enabled = true;



            textBoxState.AppendText("Completed Training The Network..\r\n ");



            buttonStop.Visible = false;

            StopTraining = false;

        }



        private void buttonRecognize_Click(object sender, EventArgs e)

        {

            string SHight = "?", SLow = "?";

            double DHight = 0, DLow = 0;

            if (LayerNum == 1)

            {

                Classifier1.Recognize(InputFromUser, ref SHight, ref DHight, ref SLow, ref DLow);

            }

            else if (LayerNum == 2)

            {

                Classifier2.Recognize(InputFromUser, ref SHight, ref DHight, ref SLow, ref DLow);

            }

            else if (LayerNum == 3)

            {

                Classifier3.Recognize(InputFromUser, ref SHight, ref DHight, ref SLow, ref DLow);

            }



            pictureBoxInput.Image = DoubleArrToIMG(InputFromUser, AvImageHeigh, AvImageWidth,

                                                   pictureBoxInput.Height, pictureBoxInput.Width);

                  

            labelMatchedHigh.Text = SHight + "(%" + (DHight * 100).ToString("##.#") + ")";



            string[] Files;

            if (SHight != "?" && Directory.Exists(PatternsDirectory + "\\" + SHight))

            {

                Files = Directory.GetFiles(PatternsDirectory + "\\" + SHight);

                //thumbs.db may appear

                if (Path.GetExtension(Files[0]) == ".bmp" )

                {

                    if(File.Exists(Files[0]))

                        pictureBoxMatchedHigh.ImageLocation = Files[0];

                }

                else

                {

                    if (File.Exists(Files[1]))

                        pictureBoxMatchedHigh.ImageLocation = Files[1];

                }

            }





            labelMatchedLow.Text = SLow + "(%" + (DLow * 100).ToString("##.#") + ")";



            if (SLow != "?" && Directory.Exists(PatternsDirectory + "\\" + SLow))

            {

                Files = Directory.GetFiles(PatternsDirectory + "\\" + SLow);

                //thumbs.db may appear

                if (Path.GetExtension(Files[0]) == ".bmp")

                {

                    if (File.Exists(Files[0]))

                        pictureBoxMatchedLow.ImageLocation = Files[0];

                }

                else

                {

                    if (File.Exists(Files[1]))

                        pictureBoxMatchedLow.ImageLocation = Files[1];

                }

            }



        }





        private void buttonBrowse_Click(object sender, EventArgs e)

        {

            OpenFileDialog FD = new OpenFileDialog();

            FD.Filter = "Bitmap Image(*.bmp)|*.bmp";

            if (FD.ShowDialog() == DialogResult.OK)

            {

                string FileName = FD.FileName;

                if (Path.GetExtension(FileName) == ".bmp")

                {

                    Bitmap BM = new Bitmap(FileName);

                    InputFromUser = IMGToDoubleArr(BM, AvImageWidth, AvImageHeigh);

                    BM.Dispose();



                    textBoxBrowse.Text = FileName;

                }

            }



        }



        private void buttonStop_Click(object sender, EventArgs e)

        {

            StopTraining = true;

            textBoxState.AppendText("Stopping Training Process..\r\n");

        }



        private void buttonSave_Click(object sender, EventArgs e)

        {

            try

            {

                SaveFileDialog FD = new SaveFileDialog();

                FD.Filter = "Network File(*.net)|*.net";

                if (FD.ShowDialog() == DialogResult.OK)

                {

                    if (LayerNum == 1)

                    {

                        Classifier1.SaveNetwork(FD.FileName, AvImageHeigh, AvImageWidth);

                    }

                    else if (LayerNum == 2)

                    {

                        Classifier2.SaveNetwork(FD.FileName, AvImageHeigh, AvImageWidth);

                    }

                    else if (LayerNum == 3)

                    {

                        Classifier3.SaveNetwork(FD.FileName, AvImageHeigh, AvImageWidth);

                    }

                }



                textBoxState.AppendText("Saved The Network Successfully..\r\n");

            }

            catch (Exception Ex)

            {

                MessageBox.Show("Unable To Save The File Due To The Reason: " + Ex.Message,"Error");

            }

            

        }



        private void buttonLoad_Click(object sender, EventArgs e)

        {

            try

            {

                OpenFileDialog FD = new OpenFileDialog();

                FD.Filter = "Network File(*.net)|*.net";

                if (FD.ShowDialog() == DialogResult.OK)

                {

                    if (LayerNum == 1)

                    {

                        Classifier1.LoadNetwork(FD.FileName, ref AvImageHeigh, ref AvImageWidth);

                    }

                    else if (LayerNum == 2)

                    {

                        Classifier2.LoadNetwork(FD.FileName, ref AvImageHeigh, ref AvImageWidth);

                    }

                    else if (LayerNum == 3)

                    {

                        Classifier3.LoadNetwork(FD.FileName, ref AvImageHeigh, ref AvImageWidth);

                    }

                }

                buttonRecognize.Enabled = true;

                buttonSave.Enabled = true;



                textBoxAvHeight.Text = AvImageHeigh.ToString();

                textBoxAvWidth.Text = AvImageWidth.ToString();



                textBoxState.AppendText("Loaded The Network Successfully..\r\n");

            }

            catch (Exception Ex)

            {

                MessageBox.Show("Unable To Load The File Due To The Reason: " + Ex.Message,"Error");

            }

        }

        

    }

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Turkey Turkey
Has BS degree on computer science, working as software engineer in istanbul.

Comments and Discussions