Click here to Skip to main content
15,867,568 members
Articles / Artificial Intelligence / Neural Networks

Multiple convolution neural networks approach for online handwriting recognition

Rate me:
Please Sign up or sign in to vote.
4.95/5 (37 votes)
9 Apr 2013CPOL8 min read 74.6K   25.1K   74  
The research focuses on the presentation of word recognition technique for an online handwriting recognition system which uses multiple component neural networks (MCNN) as the exchangeable parts of the classifier.
using System;
using System.Diagnostics;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using System.IO;
using System.IO.IsolatedStorage;
using System.Threading;
using System.ComponentModel;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Drawing;
using System.Drawing.Design;
using System.Globalization;

using SpellChecker;
using SpellChecker.Dictionary.Affix;
using SpellChecker.Dictionary.Phonetic;

namespace SpellChecker.Dictionary
{

	/// <summary>
	/// The WordDictionary class contains all the logic for managing the word list.
	/// </summary>
	[ToolboxBitmap(typeof(SpellChecker.Dictionary.WordDictionary), "Dictionary.bmp")]
	public class WordDictionary : System.ComponentModel.Component
	{
		private Hashtable _baseWords = new Hashtable();
		private string _copyright = "";
		private string _dictionaryFile = Thread.CurrentThread.CurrentCulture.Name + ".dic";
		private string _dictionaryFolder = "";
		private bool _enableUserFile = true;
		private bool _initialized = false;
		private PhoneticRuleCollection _phoneticRules = new PhoneticRuleCollection();
		private ArrayList _possibleBaseWords = new ArrayList();
		private AffixRuleCollection _prefixRules = new AffixRuleCollection();
		private ArrayList _replaceCharacters = new ArrayList();
		private AffixRuleCollection _suffixRules = new AffixRuleCollection();
		private string _tryCharacters = "";
		private string _userFile = "user.dic";
		private Hashtable _userWords = new Hashtable();
		private System.ComponentModel.Container components = null;

		/// <summary>
		///     Initializes a new instance of the class
		/// </summary>
		public WordDictionary()
		{
			InitializeComponent();
		}

		/// <summary>
		///     Initializes a new instance of the class
		/// </summary>
		public WordDictionary(System.ComponentModel.IContainer container)
		{
			container.Add(this);
			InitializeComponent();
		}

		/// <summary>
		///     Loads the user dictionary file
		/// </summary>
		private void LoadUserFile()
		{
			// load user words
			_userWords.Clear();

			// quit if user file is disabled
			if(!this.EnableUserFile) return;

			string userPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), "NetSpell");
			string filePath = Path.Combine(userPath, _userFile);

			if (File.Exists(filePath)) 
			{
				TraceWriter.TraceInfo("Loading User Dictionary:{0}", filePath);

				//IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly();
				//fs = new IsolatedStorageFileStream(_UserFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, isf);
				FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
				StreamReader sr = new StreamReader(fs, Encoding.UTF8);

				// read line by line
				while (sr.Peek() >= 0) 
				{
					string tempLine = sr.ReadLine().Trim();
					if (tempLine.Length > 0)
					{
						_userWords.Add(tempLine, tempLine);
					}
				}

				fs.Close();
				sr.Close();
				//isf.Close();

				TraceWriter.TraceInfo("Loaded User Dictionary; Words:{0}", _userWords.Count);
			}
		}

		/// <summary>
		///     Saves the user dictionary file
		/// </summary>
		/// <remarks>
		///		If the file does not exist, it will be created
		/// </remarks>
		private void SaveUserFile()
		{

			// quit if user file is disabled
			if(!this.EnableUserFile) return;

			string userPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), "NetSpell");
			if (!Directory.Exists(userPath)) Directory.CreateDirectory(userPath);

			string filePath = Path.Combine(userPath, _userFile);

			TraceWriter.TraceInfo("Saving User Dictionary:{0}", filePath);

			//IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForAssembly();
			//FileStream fs = new IsolatedStorageFileStream(_UserFile, FileMode.Create, FileAccess.Write, isf);
			FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write);
			StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
			sw.NewLine = "\n";

			foreach (string tempWord in _userWords.Keys)
			{
				sw.WriteLine(tempWord);
			}
		
			sw.Close();
			fs.Close();
			//isf.Close();

			TraceWriter.TraceInfo("Saved User Dictionary; Words:{0}", _userWords.Count);
		}

		/// <summary>
		///     Verifies the base word has the affix key
		/// </summary>
		/// <param name="word" type="string">
		///     <para>
		///         Base word to check
		///     </para>
		/// </param>
		/// <param name="affixKey" type="string">
		///     <para>
		///         Affix key to check 
		///     </para>
		/// </param>
		/// <returns>
		///     True if word contains affix key
		/// </returns>
		private bool VerifyAffixKey(string word, char affixKey)
		{
			// make sure base word has this affix key
			Word baseWord = (Word)this.BaseWords[word];
			ArrayList keys = new ArrayList(baseWord.AffixKeys.ToCharArray());
			return keys.Contains(affixKey);			
		}

		/// <summary>
		///     Adds a word to the user list
		/// </summary>
		/// <param name="word" type="string">
		///     <para>
		///         The word to add
		///     </para>
		/// </param>
		/// <remarks>
		///		This method is only affects the user word list
		/// </remarks>
		public void Add(string word)
		{
			_userWords.Add(word, word);
			this.SaveUserFile();
		}

		/// <summary>
		///     Clears the user list of words
		/// </summary>
		/// <remarks>
		///		This method is only affects the user word list
		/// </remarks>
		public void Clear()
		{
			_userWords.Clear();
			this.SaveUserFile();
		}

		/// <summary>
		///     Searches all contained word lists for word
		/// </summary>
		/// <param name="word" type="string">
		///     <para>
		///         The word to search for
		///     </para>
		/// </param>
		/// <returns>
		///     Returns true if word is found
		/// </returns>
		public bool Contains(string word)
		{
			// clean up possible base word list
			_possibleBaseWords.Clear();

			// Step 1 Search UserWords
			if (_userWords.Contains(word)) 
			{
				TraceWriter.TraceVerbose("Word Found in User Dictionary: {0}", word);
				return true;  // word found
			}

			// Step 2 Search BaseWords
			if (_baseWords.Contains(word)) 
			{
				TraceWriter.TraceVerbose("Word Found in Base Words: {0}", word);
				return true; // word found
			}

			// Step 3 Remove suffix, Search BaseWords

			// save suffixed words for use when removing prefix
			ArrayList suffixWords = new ArrayList();
			// Add word to suffix word list
			suffixWords.Add(word);

			foreach(AffixRule rule in SuffixRules.Values)
			{	
				foreach(AffixEntry entry in rule.AffixEntries)
				{
					string tempWord = AffixUtility.RemoveSuffix(word, entry);
					if(tempWord != word)
					{
						if (_baseWords.Contains(tempWord))
						{
							if(this.VerifyAffixKey(tempWord, rule.Name[0]))
							{
								TraceWriter.TraceVerbose("Word Found With Base Words: {0}; Suffix Key: {1}", tempWord, rule.Name[0]);
								return true; // word found
							}
						}
						
						if(rule.AllowCombine)
						{
							// saving word to check if it is a word after prefix is removed
							suffixWords.Add(tempWord);
						}
						else 
						{
							// saving possible base words for use in generating suggestions
							_possibleBaseWords.Add(tempWord);
						}
					}
				}
			}
			// saving possible base words for use in generating suggestions
			_possibleBaseWords.AddRange(suffixWords);

			// Step 4 Remove Prefix, Search BaseWords
			foreach(AffixRule rule in PrefixRules.Values)
			{
				foreach(AffixEntry entry in rule.AffixEntries)
				{
					foreach(string suffixWord in suffixWords)
					{
						string tempWord = AffixUtility.RemovePrefix(suffixWord, entry);
						if (tempWord != suffixWord)
						{
							if (_baseWords.Contains(tempWord))
							{
								if(this.VerifyAffixKey(tempWord, rule.Name[0]))
								{
									TraceWriter.TraceVerbose("Word Found With Base Words: {0}; Prefix Key: {1}", tempWord, rule.Name[0]);
									return true; // word found
								}
							}
							
							// saving possible base words for use in generating suggestions
							_possibleBaseWords.Add(tempWord);
						}
					} // suffix word
				} // prefix rule entry
			} // prefix rule
			// word not found 
			TraceWriter.TraceVerbose("Possible Base Words: {0}", _possibleBaseWords.Count);
			return false;
		}

		/// <summary>
		///     Expands an affix compressed base word
		/// </summary>
		/// <param name="word" type="SpellChecker.Dictionary.Word">
		///     <para>
		///         The word to expand
		///     </para>
		/// </param>
		/// <returns>
		///     A System.Collections.ArrayList of words expanded from base word
		/// </returns>
		public ArrayList ExpandWord(Word word)
		{
			ArrayList suffixWords = new ArrayList();
			ArrayList words = new ArrayList();

			suffixWords.Add(word.Text);
			string prefixKeys = "";


			// check suffix keys first
			foreach(char key in word.AffixKeys)
			{
				if (_suffixRules.ContainsKey(key.ToString(CultureInfo.CurrentUICulture)))
				{
					AffixRule rule = _suffixRules[key.ToString(CultureInfo.CurrentUICulture)];
					string tempWord = AffixUtility.AddSuffix(word.Text, rule);
					if (tempWord != word.Text)
					{
						if (rule.AllowCombine) 
						{
							suffixWords.Add(tempWord);
						}
						else 
						{
							words.Add(tempWord);
						}
					}
				}
				else if (_prefixRules.ContainsKey(key.ToString(CultureInfo.CurrentUICulture)))
				{
					prefixKeys += key.ToString(CultureInfo.CurrentUICulture);
				}
			}

			// apply prefixes
			foreach(char key in prefixKeys)
			{
				AffixRule rule = _prefixRules[key.ToString(CultureInfo.CurrentUICulture)];
				// apply prefix to all suffix words
				foreach (string suffixWord in suffixWords)
				{
					string tempWord = AffixUtility.AddPrefix(suffixWord, rule);
					if (tempWord != suffixWord)
					{
						words.Add(tempWord);
					}
				}
			}

			words.AddRange(suffixWords);

			TraceWriter.TraceVerbose("Word Expanded: {0}; Child Words: {1}", word.Text, words.Count);
			return words;
		}

		/// <summary>
		///     Initializes the dictionary by loading and parsing the
		///     dictionary file and the user file.
		/// </summary>
		public void Initialize()
		{
			// clean up data first
			_baseWords.Clear();
			_replaceCharacters.Clear();
			_prefixRules.Clear();
			_suffixRules.Clear();
			_phoneticRules.Clear();
			_tryCharacters = "";
			

			// the following is used to split a line by white space
			Regex _spaceRegx = new Regex(@"[^\s]+", RegexOptions.Compiled);
			MatchCollection partMatches;

			string currentSection = "";
			AffixRule currentRule = null;
			string dictionaryPath = Path.Combine(_dictionaryFolder, _dictionaryFile);

			TraceWriter.TraceInfo("Loading Dictionary:{0}", dictionaryPath);

			// open dictionary file
			FileStream fs = new FileStream(dictionaryPath, FileMode.Open, FileAccess.Read, FileShare.Read);
			StreamReader sr = new StreamReader(fs, Encoding.UTF8);
			
			// read line by line
			while (sr.Peek() >= 0) 
			{
				string tempLine = sr.ReadLine().Trim();
				if (tempLine.Length > 0)
				{
					// check for section flag
					switch (tempLine)
					{
						case "[Copyright]" :
						case "[Try]" : 
						case "[Replace]" : 
						case "[Prefix]" :
						case "[Suffix]" :
						case "[Phonetic]" :
						case "[Words]" :
							// set current section that is being parsed
							currentSection = tempLine;
							break;
						default :		
							// parse line and place in correct object
						switch (currentSection)
						{
							case "[Copyright]" :
								this.Copyright += tempLine + "\r\n";
								break;
							case "[Try]" : // ISpell try chars
								this.TryCharacters += tempLine;
								break;
							case "[Replace]" : // ISpell replace chars
								this.ReplaceCharacters.Add(tempLine);
								break;
							case "[Prefix]" : // MySpell prefix rules
							case "[Suffix]" : // MySpell suffix rules

								// split line by white space
								partMatches = _spaceRegx.Matches(tempLine);
									
								// if 3 parts, then new rule  
								if (partMatches.Count == 3)
								{
									currentRule = new AffixRule();
									
									// part 1 = affix key
									currentRule.Name = partMatches[0].Value;
									// part 2 = combine flag
									if (partMatches[1].Value == "Y") currentRule.AllowCombine = true;
									// part 3 = entry count, not used

									if (currentSection == "[Prefix]")
									{
										// add to prefix collection
										this.PrefixRules.Add(currentRule.Name, currentRule);
									}
									else 
									{
										// add to suffix collection
										this.SuffixRules.Add(currentRule.Name, currentRule);
									}
								}
									//if 4 parts, then entry for current rule
								else if (partMatches.Count == 4)
								{
									// part 1 = affix key
									if (currentRule.Name == partMatches[0].Value)
									{
										AffixEntry entry = new AffixEntry();

										// part 2 = strip char
										if (partMatches[1].Value != "0") entry.StripCharacters = partMatches[1].Value;
										// part 3 = add chars
										entry.AddCharacters = partMatches[2].Value;
										// part 4 = conditions
										AffixUtility.EncodeConditions(partMatches[3].Value, entry);

										currentRule.AffixEntries.Add(entry);
									}
								}	
								break;
							case "[Phonetic]" : // ASpell phonetic rules
								// split line by white space
								partMatches = _spaceRegx.Matches(tempLine);
								if (partMatches.Count >= 2)
								{
									PhoneticRule rule = new PhoneticRule();
									PhoneticUtility.EncodeRule(partMatches[0].Value, ref rule);
									rule.ReplaceString = partMatches[1].Value;
									_phoneticRules.Add(rule);
								}
								break;
							case "[Words]" : // dictionary word list
								// splits word into its parts
								string[] parts = tempLine.Split('/');
								Word tempWord = new Word();
								// part 1 = base word
								tempWord.Text = parts[0];
								// part 2 = affix keys
								if (parts.Length >= 2) tempWord.AffixKeys = parts[1];
								// part 3 = phonetic code
								if (parts.Length >= 3) tempWord.PhoneticCode = parts[2];
								
								this.BaseWords.Add(tempWord.Text, tempWord);
								break;
						} // currentSection swith
							break;
					} //tempLine switch
				} // if templine
			} // read line
			// close files
			sr.Close();
			fs.Close();

			TraceWriter.TraceInfo("Dictionary Loaded BaseWords:{0}; PrefixRules:{1}; SuffixRules:{2}; PhoneticRules:{3}",
				this.BaseWords.Count, this.PrefixRules.Count, this.SuffixRules.Count, this.PhoneticRules.Count);

			this.LoadUserFile();

			_initialized = true;
		}


		/// <summary>
		///     Generates a phonetic code of how the word sounds
		/// </summary>
		/// <param name="word" type="string">
		///     <para>
		///         The word to generated the sound code from
		///     </para>
		/// </param>
		/// <returns>
		///     A code of how the word sounds
		/// </returns>
		public string PhoneticCode(string word)
		{
			string tempWord = word.ToUpper();
			string prevWord = "";
			StringBuilder code = new StringBuilder();

			while (tempWord.Length > 0)
			{
				// save previous word
				prevWord = tempWord;
				foreach (PhoneticRule rule in _phoneticRules)
				{
					bool begining = tempWord.Length == word.Length ? true : false;
					bool ending = rule.ConditionCount == tempWord.Length ? true : false;

					if ((rule.BeginningOnly == begining || !rule.BeginningOnly)
						&& (rule.EndOnly == ending || !rule.EndOnly)
						&& rule.ConditionCount <= tempWord.Length)
					{
						int passCount = 0;
						// loop through conditions
						for (int i = 0;  i < rule.ConditionCount; i++) 
						{
							int charCode = (int)tempWord[i];
							if ((rule.Condition[charCode] & (1 << i)) == (1 << i))
							{
								passCount++; // condition passed
							}
							else 
							{
								break; // rule fails if one condition fails
							}
						}
						// if all condtions passed
						if (passCount == rule.ConditionCount)
						{
							if (rule.ReplaceMode)
							{
								tempWord = rule.ReplaceString + tempWord.Substring(rule.ConditionCount - rule.ConsumeCount);
							}
							else
							{
								if (rule.ReplaceString != "_")
								{
									code.Append(rule.ReplaceString);
								}
								tempWord = tempWord.Substring(rule.ConditionCount - rule.ConsumeCount);
							}
							break;
						}
					} 
				} // for each

				// if no match consume one char
				if (prevWord.Length == tempWord.Length) 
				{
					
					tempWord = tempWord.Substring(1);
				}
			}// while

			return code.ToString();
		}

		/// <summary>
		///     Removes a word from the user list
		/// </summary>
		/// <param name="word" type="string">
		///     <para>
		///         The word to remove
		///     </para>
		/// </param>
		/// <remarks>
		///		This method is only affects the user word list
		/// </remarks>
		public void Remove(string word)
		{
			_userWords.Remove(word);
			this.SaveUserFile();
		}

		/// <summary> 
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}

		/// <summary>
		///     The collection of base words for the dictionary
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public Hashtable BaseWords
		{
			get {return _baseWords;}
		}

		/// <summary>
		///     Copyright text for the dictionary
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public string Copyright
		{
			get {return _copyright;}
			set {_copyright = value;}
		}

		/// <summary>
		///     The file name for the main dictionary
		/// </summary>
		[DefaultValue("en-US.dic")]
		[CategoryAttribute("Dictionary")]
		[Description("The file name for the main dictionary")]
		[NotifyParentProperty(true)]
		public string DictionaryFile
		{
			get {return _dictionaryFile;}
			set 
			{
				_dictionaryFile = value;
				if (_dictionaryFile.Length == 0)
				{
					_dictionaryFile = Thread.CurrentThread.CurrentCulture.Name + ".dic";
				}
			}
		}


		/// <summary>
		///     Folder containing the dictionaries
		/// </summary>
		[DefaultValue("")]
		[CategoryAttribute("Dictionary")]
		[Description("The folder containing dictionaries")]
		[Editor(typeof(FolderNameEditor), typeof(UITypeEditor))]
		[NotifyParentProperty(true)]
		public string DictionaryFolder
		{
			get {return _dictionaryFolder;}
			set {_dictionaryFolder = value;}
		}

		/// <summary>
		///     Set this to true to automaticly create a user dictionary when
		///     a word is added.
		/// </summary>
		/// <remarks>
		///		This should be set to false in a web environment
		/// </remarks>
		[DefaultValue(true)]
		[CategoryAttribute("Options")]
		[Description("Set this to true to automaticly create a user dictionary")]
		[NotifyParentProperty(true)]
		public bool EnableUserFile
		{
			get {return _enableUserFile;}
			set {_enableUserFile = value;}
		}


		/// <summary>
		///     True if the dictionary has been initialized
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public bool Initialized
		{
			get {return _initialized;}
		}


		/// <summary>
		///     Collection of phonetic rules for this dictionary
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public PhoneticRuleCollection PhoneticRules
		{
			get {return _phoneticRules;}
		}


		/// <summary>
		///     Collection of affix prefixes for the base words in this dictionary
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public AffixRuleCollection PrefixRules
		{
			get {return _prefixRules;}
		}

		/// <summary>
		///     List of characters to use when generating suggestions using the near miss stratigy
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public ArrayList ReplaceCharacters
		{
			get {return _replaceCharacters;}
		}


		/// <summary>
		///     Collection of affix suffixes for the base words in this dictionary
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public AffixRuleCollection SuffixRules
		{
			get {return _suffixRules;}
		}

		/// <summary>
		///     List of characters to try when generating suggestions using the near miss stratigy
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public string TryCharacters
		{
			get {return _tryCharacters;}
			set {_tryCharacters = value;}
		}

		/// <summary>
		///     The file name for the user word list for this dictionary
		/// </summary>
		[DefaultValue("user.dic")]
		[CategoryAttribute("Dictionary")]
		[Description("The file name for the user word list for this dictionary")]
		[NotifyParentProperty(true)]
		public string UserFile
		{
			get {return _userFile;}
			set {_userFile = value;}
		}

		/// <summary>
		///     List of user entered words in this dictionary
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public Hashtable UserWords
		{
			get {return _userWords;}
		}

		/// <summary>
		///     List of text saved from when 'Contains' is called. 
		///     This list is used to generate suggestions from if Contains
		///     doesn't find a word.
		/// </summary>
		/// <remarks>
		///		These are not actual words.
		/// </remarks>
		internal ArrayList PossibleBaseWords
		{
			get {return _possibleBaseWords;}
		}


		#region Component Designer generated code
		/// <summary>
		/// Required method for Designer support - do not modify
		/// the contents of this method with the code editor.
		/// </summary>
		private void InitializeComponent()
		{
			components = new System.ComponentModel.Container();
		}
		#endregion
		
	}

}

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
Vietnam Maritime University
Vietnam Vietnam
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions