如何制作一个可以容纳1个以上数据的字典?

时间:2012-01-18 02:00:09

标签: c# neural-network ocr self-organizing-maps

我一直在尝试修改程序,以便它可以接受单个字母字符的多个数据,例如字母“A”。有某种ContainsKey函数只允许键盘中的一个键只保存一个数据。如何能够保存多个数据?

我要说得很清楚,这是一个使用无监督神经网络的在线OCR程序。当用户在绘图空间中绘制角色时,他们可以选择将角色添加到学习数据中以便稍后训练。当他们添加一个角色时,他们必须使用键盘上的键来定义他们刚输入的角色。例如,他们绘制字母'A',弹出窗口将显示要求用户从键盘输入该字母的密钥。

这里的问题是,当学习数据中已经有一个字母'A'时,我不能添加另一个字母'A',因为密钥A已经保留了之前的'A'。我想让钥匙A能够容纳多个字母'A'。

我要在这里发布该程序的整个代码,我希望你们能忍受我。这不是我的程序,它来自Heaton Research,我打算修改它。提前谢谢。

  public partial class Form1 : Form
  {
  /**
  * The downsample width for the application.
  */
    const int DOWNSAMPLE_WIDTH = 10;

    /**
     * The down sample height for the application.
     */
    const int DOWNSAMPLE_HEIGHT = 12;

    private Bitmap entryImage;
    private Graphics entryGraphics;
    private int entryLastX;
    private int entryLastY;
    private Pen blackPen;
    private bool[] downsampled;
    private Dictionary<char, List<bool[]>> letterData = new Dictionary<Char, List<bool[]>>();
    private double[][] trainingSet;
    private SelfOrganizingMap network;

    public Form1()
    {
        InitializeComponent();
        blackPen = new Pen(Color.Black);
        entryImage = new Bitmap(entry.Width, entry.Height);
        entryGraphics = Graphics.FromImage(entryImage);
        downsampled = new bool[Form1.DOWNSAMPLE_HEIGHT * Form1.DOWNSAMPLE_WIDTH];
        ClearEntry();
    }

    private void entry_Paint(object sender, PaintEventArgs e)
    {
        Graphics g = e.Graphics;
        g.DrawImage(entryImage, 0, 0);
        Pen blackPen = new Pen(Color.Black);
        g.DrawRectangle(blackPen, 0, 0, entry.Width - 1, entry.Height - 1);

    }

    private void btnDelete_Click(object sender, EventArgs e)
    {
        string str = (string)this.letters.Items[this.letters.SelectedIndex]; 
        char ch = str[0];
        this.letterData.Remove(ch);
        this.letters.Items.Remove(str);
        ClearEntry();
    }

    private void btnLoad_Click(object sender, EventArgs e)
    {
        try
        {

            OpenFileDialog openFileDialog1 = new OpenFileDialog();
            openFileDialog1.Filter = "Data File (*.dat)|*.dat";
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {

                TextReader f = new StreamReader(openFileDialog1.FileName);

                String line;

                this.letterData.Clear();
                this.letters.Items.Clear();

                while ((line = f.ReadLine()) != null)
                {
                    int sampleSize = Form1.DOWNSAMPLE_HEIGHT * Form1.DOWNSAMPLE_WIDTH;
                    char ch = char.ToUpper(line[0]);
                    bool[] sample = new bool[sampleSize];

                    int idx = 2;
                    for (int i = 0; i < sampleSize; i++)
                    {
                        if (line[idx++] == '1')
                            sample[i] = true;
                        else
                            sample[i] = false;
                    }

                    this.letterData.Add(ch, sample);
                    this.letters.Items.Add("" + ch);
                }

                f.Close();
            }
            MessageBox.Show(this, "File Loaded");

        }
        catch (Exception ex)
        {
            MessageBox.Show("Error: " + ex.Message);
        }
    }

    private void btnSave_Click(object sender, EventArgs e)
    {
        try
        {
             SaveFileDialog saveFileDialog1 = new SaveFileDialog();
            saveFileDialog1.Filter = "Data File (*.dat)|*.dat";
             if (saveFileDialog1.ShowDialog() == DialogResult.OK)
             {
                 TextWriter f = new StreamWriter(saveFileDialog1.FileName);
                 int size = Form1.DOWNSAMPLE_HEIGHT * Form1.DOWNSAMPLE_WIDTH;

                 for (int i = 0; i < this.letters.Items.Count; i++)
                 {
                     char ch = ((string)this.letters.Items[i])[0];
                     bool[] data = this.letterData[ch];

                     f.Write(ch + ":");
                     for (int j = 0; j < size; j++)
                     {
                         f.Write(data[j] ? "1" : "0");

                     }
                     f.WriteLine("");


                 }
                 f.Close();

                 MessageBox.Show("File Saved");

             }
        }
        catch (Exception e2)
        {
            MessageBox.Show("Error: " + e2.Message, "Training");
        }
    }

    private void btnBeginTraining_Click(object sender, EventArgs e)
    {
        int inputCount = Form1.DOWNSAMPLE_HEIGHT * Form1.DOWNSAMPLE_WIDTH;
        int letterCount = this.letters.Items.Count;
        this.trainingSet = new double[letterCount][];
        int index = 0;
        foreach (char ch in this.letterData.Keys)
        {
            this.trainingSet[index] = new double[inputCount];
            bool[] data = this.letterData[ch];
            for (int i = 0; i < inputCount; i++)
            {
                this.trainingSet[index][i] = data[i] ? 0.5 : -0.5;
            }
            index++;
        }

        network = new SelfOrganizingMap(inputCount, letterCount, NormalizationType.Z_AXIS);

        this.ThreadProc();

    }

    private void btnAdd_Click(object sender, EventArgs e)
    {
        DownSample ds = new DownSample(this.entryImage);
        this.downsampled = ds.downSample(Form1.DOWNSAMPLE_WIDTH, Form1.DOWNSAMPLE_HEIGHT);
        this.sample.Invalidate();
        String Prompt = "Enter the letter you just draw (from the keyboard)";
        String Title = "Letter definition Required";
        String Default = " ";
        Int32 XPos = ((SystemInformation.WorkingArea.Width / 2) - 200);
        Int32 YPos = ((SystemInformation.WorkingArea.Height / 2) - 100);

        bool valid = false;
        for (int i = 0; i < this.downsampled.Length; i++)
        {
            if (this.downsampled[i])
            {
                valid = true;
            }
         }


        if (!valid)
        {
            MessageBox.Show("Please draw a letter before adding it.");
            return;
        }



       String Result = Microsoft.VisualBasic.Interaction.InputBox(Prompt, Title, Default, XPos, YPos);
       if (Result != null)
       {
           Result = Result.ToUpper();
           if (Result.Length == 0)
           {
               MessageBox.Show("Please enter a character.");
           }
           else if (Result.Length < 1)
           {
               MessageBox.Show("Please enter only a single character.");
           }
           //else if (this.letterData.ContainsKey(Result[0]))
           //{
           //    MessageBox.Show("That letter is already defined, please delete first.");
           //}
           else
           {
               if (this.letterData.ContainsKey(Result[0]))
               {
                   this.letterData[Result[0]].Add(this.downsampled);
               }
               else
               {
                   this.letterData.Add(Result[0], new List<bool[]>() {this.downsampled});
               }

               this.letters.Items.Add(Result);
               //this.letterData.Add(Result[0], this.downsampled);
               this.ClearEntry();
           }
       }


    }

    private void btnRecognize_Click(object sender, EventArgs e)
    {
        DownSample ds = new DownSample(this.entryImage);
        this.downsampled = ds.downSample(Form1.DOWNSAMPLE_WIDTH, Form1.DOWNSAMPLE_HEIGHT);
        this.sample.Invalidate();

        if (this.network == null)
        {
            MessageBox.Show("The program needs to be trained first");
            return;
        }

        int sampleSize = Form1.DOWNSAMPLE_HEIGHT * Form1.DOWNSAMPLE_WIDTH;
        double[] input = new double[sampleSize];

        for (int i = 0; i < sampleSize; i++)
        {
            input[i] = this.downsampled[i] ? 0.5 : -0.5;
        }

        int best = this.network.Winner(input);
        char[] map = mapNeurons();
        this.result.Text = "  " + map[best];
        MessageBox.Show("  " + map[best] + "   (Neuron #"
                        + best + " fired)", "That Letter You Enter Is");
        //ClearEntry();
    }

    private void btnClear_Click(object sender, EventArgs e)
    {
        ClearEntry();
    }

    private void btnSample_Click(object sender, EventArgs e)
    {
        DownSample ds = new DownSample(this.entryImage);
        this.downsampled = ds.downSample(Form1.DOWNSAMPLE_WIDTH, Form1.DOWNSAMPLE_HEIGHT);
        this.sample.Invalidate();
    }
    public void ClearEntry()
    {
        Brush whiteBrush = new SolidBrush(Color.White);
        entryGraphics.FillRectangle(whiteBrush, 0, 0, entry.Width, entry.Height);
        entry.Invalidate();
        DownSample ds = new DownSample(this.entryImage);
        this.downsampled = ds.downSample(Form1.DOWNSAMPLE_WIDTH, Form1.DOWNSAMPLE_HEIGHT);
        this.sample.Invalidate();
    }

    private void entry_MouseDown(object sender, MouseEventArgs e)
    {
        entry.Capture = true;
        entryLastX = e.X;
        entryLastY = e.Y;
    }

    private void entry_MouseUp(object sender, MouseEventArgs e)
    {
        entryGraphics.DrawLine(blackPen, entryLastX, entryLastY, e.X, e.Y);
        entry.Invalidate();
        entry.Capture = false;
    }

    private void entry_MouseMove(object sender, MouseEventArgs e)
    {
        if (entry.Capture == true)
        {
            entryGraphics.DrawLine(blackPen, entryLastX, entryLastY, e.X, e.Y);
            entry.Invalidate();
            entryLastX = e.X;
            entryLastY = e.Y;
        }
    }

    private void sample_Paint(object sender, PaintEventArgs e)
    {
        Graphics g = e.Graphics;

        int x, y;
        int vcell = sample.Height / Form1.DOWNSAMPLE_HEIGHT;
        int hcell = sample.Width / Form1.DOWNSAMPLE_WIDTH;
        Brush whiteBrush = new SolidBrush(Color.White);
        Brush blackBrush = new SolidBrush(Color.Black);
        Pen blackPen = new Pen(Color.Black);

        g.FillRectangle(whiteBrush, 0, 0, sample.Width, sample.Height);



        for (y = 0; y < Form1.DOWNSAMPLE_HEIGHT; y++)
        {
            g.DrawLine(blackPen, 0, y * vcell, sample.Width, y * vcell);
        }
        for (x = 0; x < Form1.DOWNSAMPLE_WIDTH; x++)
        {
            g.DrawLine(blackPen, x * hcell, 0, x * hcell, sample.Height);
        }

        int index = 0;
        for (y = 0; y < Form1.DOWNSAMPLE_HEIGHT; y++)
        {
            for (x = 0; x < Form1.DOWNSAMPLE_WIDTH; x++)
            {
                if (this.downsampled[index++])
                {
                    g.FillRectangle(blackBrush, x * hcell, y * vcell, hcell, vcell);
                }
            }
        }

        g.DrawRectangle(blackPen, 0, 0, sample.Width - 1, sample.Height - 1);
    }

    private void letters_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (this.letters.SelectedIndex >= 0)
        {
            string str = (string)this.letters.Items[this.letters.SelectedIndex];
            char ch = str[0];
            this.downsampled = this.letterData[ch];
            this.sample.Invalidate();
        }
    }

    public void ThreadProc()
    {
        TrainSelfOrganizingMap train = new TrainSelfOrganizingMap(
            this.network, this.trainingSet, TrainSelfOrganizingMap.LearningMethod.SUBTRACTIVE, 0.5);

        int tries = 1;

        do
        {
            train.Iteration();
            this.txtTries.Text = "" + tries;
            this.txtBestError.Text = "" + train.BestError;
            this.txtLastError.Text = "" + train.TotalError;
            tries++;
            Application.DoEvents();
        } while (train.TotalError > 0.01 && (tries <= 100));
        MessageBox.Show("Training complete.");
    }

    /**
     * Used to map neurons to actual letters.
     * 
     * @return The current mapping between neurons and letters as an array.
     */
    public char[] mapNeurons()
    {
        char[] map = new char[this.letters.Items.Count];

        for (int i = 0; i < map.Length; i++)
        {
            map[i] = '?';
        }
        for (int i = 0; i < this.letters.Items.Count; i++)
        {
            double[] input = new double[Form1.DOWNSAMPLE_HEIGHT * Form1.DOWNSAMPLE_WIDTH];
            char ch = ((string)(this.letters.Items[i]))[0];
            bool[] data = this.letterData[ch];
            for (int j = 0; j < input.Length; j++)
            {
                input[j] = data[j] ? 0.5 : -0.5;
            }

            int best = this.network.Winner(input);
            map[best] = ch;
        }
        return map;
    }

}

2 个答案:

答案 0 :(得分:2)

Dictionary<>的创建方式使您可以以最有效的方式访问键值对。现在在Dictionary<>中,你不能拥有两对相同的密钥。为此, 你可以做什么,你创建了一个像Dictionary<char, List<bool[]>>这样的字典,现在在这个词典中你可以存储一个具有多个值的键。

<强>更新

如果您将字典更改为Dictionary<char, List<bool[]>>,那么要存储一个具有多个值的键,您必须按如下方式

private bool[] downsampled;
private Dictionary<char, List<bool[]>> letterData = new Dictionary<Char, List<bool[]>>();

//
//     Your Code
//

if (Result != null)
{
    Result = Result.ToUpper();
    if (Result.Length == 0)
    {
        MessageBox.Show("Please enter a character.");
    }
    else if (Result.Length < 1)
    {
        MessageBox.Show("Please enter only a single character.");
    }
    else
    {
        if (this.letterData.ContainsKey(Result[0]))
        {
            this.letterData[Result[0]].Add(this.downsampled);
        }
        else
        {
            this.letterData.Add(Result[0], new List<bool[]>() { this.downsampled });
        }
        this.letters.Items.Add(Result);            
        this.ClearEntry();
    }
}

如果您想string作为键,而不是char,请使用Dictionary<string, List<bool[]>>

希望这能回答你的问题。

答案 1 :(得分:0)

查看.Net中的Lookup课程。这使您可以多次使用相同的“键”值。