public void LearningTest()
		{
			MathTextBitmap bitmap = 
				new MathTextBitmap(new FloatBitmap(5,5), new Gdk.Point(0,0));
			
			bitmap.ProcessImage(new List<BitmapProcesses.BitmapProcess>());
			
			CharacteristicTreeDatabase database = 
				new CharacteristicTreeDatabase();
			
			MathSymbol aSymbol = new MathSymbol("a");
			MathSymbol bSymbol = new MathSymbol("b");
			
			database.Learn(bitmap, aSymbol);
			
			List<MathSymbol> symbols = database.Match(bitmap);
			
			bool a1Learned = symbols.Count == 1 
				&& symbols[0] == aSymbol;
			
			bool a2Learned = true;
							
			a2Learned =	database.Learn(bitmap, aSymbol);
			
			
			database.Learn(bitmap, bSymbol);
			symbols = database.Match(bitmap);
			
			bool b1Learned = symbols.Count == 2;
			
			Assert.IsTrue(a1Learned, "Fallo el aprender la primera a");
			Assert.IsFalse(a2Learned, "No se detecto el conflicto de la segunda a");
			Assert.IsTrue(b1Learned, "Fallo el aprender la b");
		}
		/// <summary>
		/// <c>SegmentedNode</c>'s constructor
		/// </summary>
		/// <param name="bitmap">
		/// A <see cref="MathTextBitmap"/>
		/// </param>
		/// <param name="view">
		/// The parent node's <c>NodeView</c>.
		/// </param>
		public SegmentedNode(MathTextBitmap bitmap, NodeView view)
		{
		
			this.view = view;
			
			this.bitmap =bitmap;
			
			this.position = String.Format("({0}, {1})",
			                              bitmap.Position.X, bitmap.Position.Y);
		}
		public override bool Apply (FloatBitmap image)
		{
			MathTextBitmap bitmap = new MathTextBitmap(image, new Gdk.Point(0,0));
			foreach (BitmapSegmenter segmenter in segmenters) 
			{
				if(segmenter.Segment(bitmap).Count>1)
				{
					return true;
				}
			}
			
			return false;
		}
		/// <summary>
		/// Creates the projection
		/// </summary>
		/// <param name="image">
		/// The <see cref="MathTextBitmap"/> to be projected.
		/// </param>
		protected override void CreateProjection(MathTextBitmap image)
		{	
			Values=new int [image.Width];
			for(int i=0;i<image.Width;i++)
			{
				for(int j=0;j<image.Height;j++)
				{
					if(image[i,j]!=FloatBitmap.White)
					{
						Values[i]++;	
					}
				}						
			}
		}
		/// <summary>
		/// El constructor de <code>FormulaNode</code>
		/// </summary>
		/// <param name="name">
		/// El texto que aparecera en el nodo.
		/// </param>
		/// <param name="bitmap">
		/// El objeto <code>MathTextBitmap</code> asociado al nodo.
		/// </param>
		/// <param name="view">
		/// El árbol al que añadimos el nodo.
		/// </param>		
		public SegmentedNode(string name, MathTextBitmap bitmap, NodeView view)
		    : base()
		{
			this.name=name;
			this.label="";
			this.bitmap=bitmap;
			
			position = String.Format("({0}, {1})",
			                         bitmap.Position.X,
			                         bitmap.Position.Y);
			
			this.view = view;
			
			this.symbols = new List<MathSymbol>();
		}
        /// <summary>
        /// Creates BitmapProjection instances.
        /// </summary>
        /// <param name="mode">
        /// The <see cref="ProjectionMode"/> used for the projection.
        /// </param>
        /// <param name="mtb">
        /// The <see cref="MathTextBitmap"/> to be projected.
        /// </param>
        /// <returns>
        /// The <see cref="BitmapProjection"/> created.
        /// </returns>
		public static BitmapProjection CreateProjection(ProjectionMode mode, 
                                                        MathTextBitmap mtb)
		{
			BitmapProjection res=null;
			
			switch(mode)
			{
				case(ProjectionMode.Horizontal):
					res=new HorizontalBitmapProjection(mtb);
					break;
				case(ProjectionMode.Vertical):
					res=new VerticalBitmapProjection(mtb);
					break;
				default:
					throw new ArgumentException(
						"No puede usar None para crear una nueva projección");				
			}
			return res;
		}
		/// <summary>
		/// Con este metodos almacenamos un nuevo simbolo en la base de
		/// datos.
		/// 
		/// Lanza <c>DuplicateSymbolException</c> si ya existe un simbolo
		/// con la misma etiqueta y caracteristicas binarias en la base de datos.
		/// </summary>
		/// <param name="bitmap">
		/// La imagen cuyas caracteristicas aprenderemos.
		/// </param>
		/// <param name="symbol">
		/// El simbolo que representa a la imagen.
		///</param>
		public override bool Learn(MathTextBitmap bitmap,MathSymbol symbol)
		{
			if(characteristics == null)
				characteristics=CharacteristicFactory.CreateCharacteristicList();
			
			CharacteristicNode node=rootNode;
			bool characteristicValue;	
			
			FloatBitmap processedBitmap = bitmap.LastProcessedImage;
			
			// Recorremos las caracteristicas, y vamos creando el arbol segun
			// vamos necesitando nodos.
			foreach(BinaryCharacteristic bc in characteristics)
			{					
				if(characteristicValue=bc.Apply(processedBitmap))
				{
					if(node.TrueTree==null)
					{
						node.TrueTree=new CharacteristicNode();						
					}
					
					node=node.TrueTree;					
				}
				else
				{
					if(node.FalseTree==null)
					{
						node.FalseTree=new CharacteristicNode();						
					}					
					node=node.FalseTree;					
				}	
				
				StepDoneArgs a = 
						CreateStepDoneArgs(bc,characteristicValue,null);
					
				this.StepDoneInvoker(a);
			}	
			
			return node.AddSymbol(symbol);				
			
		}
		/// <summary>
		/// Este metodo realiza la descomposicion de la imagen descomponiendola por
		/// los bordes de los huecos presentes en la proyeccion.
		/// Distintas implementaciones difieren en si se eliminan alguno de estos huecos
		/// de forma que se agrupan ciertas subimagenes para ser tratadas como una sola.
		/// </summary>
		/// <param name="image">La imagen que deseamos descomponer.</param>
		/// <returns>
		/// Un array con las distintas partes en que hemos descompuesto
		/// la imagen de entrada.
		/// </returns>
		public override List<MathTextBitmap> Segment(MathTextBitmap image)
		{
		
			//Creamos la proyeccion
			BitmapProjection proj=BitmapProjection.CreateProjection(mode,image);
			List<Hole> holes=proj.Holes;
					
			//Aqui decidimos a partir de que tamao de hueco vamos a cortar la imagen			
						
			int threshold = GetImageCutThreshold(holes);	
			
			//Eliminamos los huecos que tengan menor tamaño que el umbral
			int i=1;
			while(i<holes.Count-1){

				if(((Hole)holes[i]).Size<threshold){
					holes.Remove(holes[i]);				
				}else{
					i++;
				}
			}		
			
			return ImageCut(image,holes);
		}	
		/// <summary>
		/// Sets the image to be processed.
		/// </summary>
		/// <param name="image">
		/// The initial image's path.
		/// </param>
		private void SetInitialImage(string  filename)
		{
			segmentationStore.Clear();
			
			Gdk.Pixbuf imageOriginal = new Gdk.Pixbuf(filename);
			originalImageArea.Image=imageOriginal;				
			
			
			// Generamos el MaxtTextBitmap inicial, y lo añadimos como
			// un nodo al arbol.
			MathTextBitmap mtb = new MathTextBitmap(imageOriginal);			
			SegmentedNode node = 
				new SegmentedNode(System.IO.Path.GetFileNameWithoutExtension(filename),
				                  mtb,
				                  null);
			    
			segmentationStore.AddNode(node);
			
			ocrController.StartNode = node;

			Log("¡Archivo de imagen «{0}» cargado correctamente!", filename);
			
			unassistedProcessBtn.Sensitive = true;
			
		}
示例#10
0
		/// <summary>
		/// Con este metodos almacenamos un nuevo simbolo en la base de
		/// datos.
		/// </summary>
		/// <param name="bitmap">
		/// La imagen que aprenderemos.
		/// </param>
		/// <param name="symbol">
		/// El simbolo que representa a la imagen.
		/// </param>
		/// <returns>
		/// If the symbol was learned or there was a conflict.
		/// </returns>
		public abstract bool Learn(MathTextBitmap bitmap,MathSymbol symbol);
		internal HorizontalBitmapProjection(MathTextBitmap image)
			: base(image)
		{
		
		}		
		internal VerticalBitmapProjection(MathTextBitmap image)
			: base(image)
		{
		
		}
		/// <summary>
		/// Segmenta una imagen.
		/// </summary>
		/// <param name="image">
		/// La imagen a segmentar.
		/// </param>
		/// <returns>
		/// Una lista con las imagenes que se han obtenido.
		/// </returns>
		private List<MathTextBitmap> CreateChildren(MathTextBitmap image)
		{
			List<MathTextBitmap> children = new List<MathTextBitmap>();
			
			bool segmented = false;
			for(int i=0; i< segmenters.Count && !segmented; i++)
			{
				// Intentamos segmentar.
				children = segmenters[i].Segment(image);
				
				if(children.Count > 1)
					segmented = true;
				else if(children.Count == 1)
				{
					throw new Exception("MathTextBitmap incorrectly segmented");
				}
			}
			
			return children;
		}
		/// <summary>
		/// El metodo que sera invocado para intentar descomponer
		/// una imagen.
		/// </summary>
		/// <param name="mtb">La imagen que queremos descomponer.</param>
		/// <returns>
		/// Un array con las distintas partes en que se
		/// ha descompuesto la imagen.
		/// </returns>
		public abstract List<MathTextBitmap> Segment(MathTextBitmap mtb);
		/// <summary>
		/// Este metodo intenta recuperar los simbolos de la base de datos 
		/// correspondiente a una imagen.
		/// </summary>
		/// <param name="image">
		/// La imagen cuyos simbolos asociados queremos encontrar.
		/// </param>
		/// <returns>
		/// Los simbolos que se puedieron asociar con la imagen.
		/// </returns>
		public override List<MathSymbol> Match(MathTextBitmap image)
		{
			if(characteristics == null)
				characteristics=CharacteristicFactory.CreateCharacteristicList();
			
			List<MathSymbol> res = new List<MathSymbol>();
			CharacteristicNode nodo=rootNode;
			BinaryCharacteristic bc;
			
			bool exists=true; 
			bool characteristicValue;
			
			List<bool> vector=new List<bool>();
			
			FloatBitmap processedImage = image.LastProcessedImage;
			
			for(int i=0;i<characteristics.Count && exists;i++)
			{
				bc=(BinaryCharacteristic)(characteristics[i]);				

				if(characteristicValue=bc.Apply(processedImage))
				{
				
						if(nodo.TrueTree==null)
						{
							exists=false;
						}					
						nodo=nodo.TrueTree;
			
				}
				else
				{
				
						 if(nodo.FalseTree==null)
						 {
							 exists=false;
						 }					
						 nodo=nodo.FalseTree;
				 }		
				
				 StepDoneArgs args; 
				 //Avisamos de que hemos dado un paso
				 if(nodo!=null)
				 {
					args =CreateStepDoneArgs(bc,
					                         characteristicValue,
					                         nodo.ChildrenSymbols);
				 }
				 else
				 {
				 	args =CreateStepDoneArgs(bc,
					                         characteristicValue,
					                         null);
				 }
				
				 StepDoneInvoker(args);
			}
			
			if(exists)
			{
				res=nodo.Symbols;
			}			
			
			return res;

		}
		/// <summary>
		/// Establece la imagen inicial para segmentar y reconocer sus
		/// caracteres. 
		/// </summary>
		/// <param name="image">
		/// La imagen inicial.
		/// </param>
		private void SetInitialImage(string  filename)
		{
			
			imageOriginal = new Pixbuf(filename);
			imageAreaOriginal.Image=imageOriginal;				
			store.Clear();
			
			// Generamos el MaxtTextBitmap inical, y lo añadimos como
			// un nodo al arbol.
			MathTextBitmap mtb = new MathTextBitmap(imageOriginal);			
			SegmentedNode node = 
				new SegmentedNode(System.IO.Path.GetFileNameWithoutExtension(filename),
				                  mtb,
				                  treeview);
			    
			store.AddNode(node);
			
			rootNode = node;
			
			controller.StartNode = node;

			Log("¡Archivo de imagen «{0}» cargado correctamente!", filename);
			
			alignNextButtons.Sensitive=true;
			segmentBtn.Sensitive = true;
			gotoTokenizerBtn.Sensitive = false;
			buttonsNB.Page = 0;
		}
		/// <summary>
		/// Creates the projection.
		/// </summary>
		/// <param name="image">
		/// The <see cref="MathTextBitmap"/> being projected.
		/// </param>
		protected abstract void CreateProjection(MathTextBitmap image);
		/// <summary>
		/// Metodo que obtiene las subimagenes a partir de los huecos.
		/// </summary>
		/// <param name="image">La imagen a segmentar.</param>
		/// <param name="holes">
		/// Los huecos considerados para separar las partes de la imagen.
		/// </param>
		/// <returns>Un array con las subimagenes obtenidas al segmentar.</returns>
		private List<MathTextBitmap> ImageCut(MathTextBitmap image,
		                                      List<Hole> holes)
		{
			
			List<MathTextBitmap> newBitmaps=new List<MathTextBitmap>();
			
			int start,size;
			int xpos=image.Position.X;
			int ypos=image.Position.Y;
			
			for(int i=0;i<holes.Count-1;i++)
			{
				//El texto esta entre el final de un hueco, y el inicio del siguiente;
				start=((Hole)holes[i]).EndPixel;
				size=((Hole)holes[i+1]).StartPixel-start+1;
				
				int x0,y0;
				int width0, height0;
				if(mode == ProjectionMode.Horizontal)
				{
					x0 = start;
					y0 = 0;
					width0 = size;
					height0 = image.Height;
				}
				else
				{
					x0 = 0;
					y0 = start;
					width0 = image.Width;
					height0 = size;
				}
				
				// Recortamos
				FloatBitmap cutImage= 
					image.FloatImage.SubImage(x0, y0, width0,height0);
				
				// Encuadramos
				Gdk.Point pd;
				Gdk.Size sd;				
				GetEdges(cutImage, out pd, out sd);
				cutImage =  cutImage.SubImage(pd.X, pd.Y, sd.Width,sd.Height);
				// Montamos el bitmap
				MathTextBitmap newBitmap =
					new MathTextBitmap(cutImage,
					                   new Point(xpos + x0 +  pd.X,
					                             ypos + y0+ pd.Y));
					
				newBitmaps.Add(newBitmap);
			}		
			
			if (newBitmaps.Count == 1)
			{
				newBitmaps.Clear();
			}
			
			
			return newBitmaps;
		}
		/// <summary>
		/// Este metodo intenta recuperar los simbolos de la base de datos 
		/// correspondiente a una imagen.
		/// </summary>
		/// <param name="image">
		/// La imagen cuyos simbolos asociados queremos encontrar.
		/// </param>
		/// <returns>
		/// Los simbolos que se puedieron asociar con la imagen.
		/// </returns>
		public override List<MathSymbol> Match(MathTextBitmap image)
		{
			List<MathSymbol> res = new List<MathSymbol>();
			
			FloatBitmap processedImage = image.LastProcessedImage;
			
			TristateCheckVector vector = CreateVector(processedImage);
			
			// We have this image vector, now we will compare with the stored ones.
			
			
			
			foreach(TristateCheckVector storedVector in symbolsDict)
			{
				// We consider a threshold.
				int threshold = (int)((storedVector.Length - storedVector.DontCares) * epsilon); 
				
				// If the distance is below the threshold, we consider it valid.
				if(storedVector.Distance(vector) <= threshold)
				{
					foreach(MathSymbol symbol in storedVector.Symbols)
					{
						// We don't want duplicated symbols.
						if(!res.Contains(symbol))
						{
							res.Add(symbol);
						}
					}
					
					string msg = 
						String.Format("Distancia({0}, {1}) <= {2}",
						             vector.ToString(),
						             storedVector.ToString(),
						             threshold);
					
					msg += "\nSe añadieron los símbolos almacenados.";
					
					StepDoneInvoker(new StepDoneArgs(msg));
				}
				else
				{
					string msg = String.Format("Distancia({0}, {1}) > {2}",
					                           vector.ToString(),
					                           storedVector.ToString(),
					                           threshold);
					
					StepDoneInvoker(new StepDoneArgs(msg));
				}
				
				
			}
		
			return res;

		}
		/// <summary>
		/// This method is used to add new symbols to the database.
		/// </summary>
		/// <param name="bitmap">
		/// The image containing the symbol.
		/// </param>
		/// <param name="symbol">
		/// The symbol contained in the image.
		/// </param>
		public override bool Learn(MathTextBitmap bitmap,MathSymbol symbol)
		{
			FloatBitmap processedBitmap = bitmap.LastProcessedImage;
			TristateCheckVector newVector = CreateVector(processedBitmap);			
			
			TristateCheckVector found =null;
			foreach (TristateCheckVector vector in symbolsDict) 
			{
				if(vector.Symbols.Contains(symbol))
				{
					found  = vector;
					break;
				}
			}
			
			if(found ==null)
			{
				// The symbol wasnt present in the database.
				newVector.Symbols.Add(symbol);
				symbolsDict.Add(newVector);
			}
			else
			{
				// The symbol is present, we may have to retrain the database.
				if(newVector.Equals(found))
				{
					// It's the same one, so there is a conflict.
					return false;
				}
				else
				{
					// We have to find the differnces, and change them to
					// don't care values.
					for(int i=0; i< found.Length; i++)
					{
						if(found[i] != TristateValue.DontCare
						   && found[i] != newVector[i])
						{
							// If the value is different from what we had learned, 
							// then we make the vector more general in that point.
							found[i] = TristateValue.DontCare;
						}
					}
				}
			}
			
			return true;
		}
		/// <summary>
		/// Con este metodos almacenamos un nuevo simbolo en la base de
		/// datos.
		/// 
		/// Lanza <c>DuplicateSymbolException</c> si ya existe un simbolo
		/// con la misma etiqueta y caracteristicas binarias en la base de datos.
		/// </summary>
		/// <param name="bitmap">
		/// La imagen cuyas caracteristicas aprenderemos.
		/// </param>
		/// <param name="symbol">
		/// El simbolo que representa a la imagen.
		/// </param>
		public override bool Learn(MathTextBitmap bitmap,MathSymbol symbol)
		{
			if(characteristics == null)
				characteristics=CharacteristicFactory.CreateCharacteristicList();
		
			FloatBitmap processedBitmap = bitmap.LastProcessedImage;
			CheckVector vector = CreateVector(processedBitmap);
			
			
			int position = symbolsDict.IndexOf(vector);
			if(position >=0)
			{
				// The vector exists in the list
			
				List<MathSymbol> symbols = symbolsDict[position].Symbols;
				if(symbols.Contains(symbol))
				{
					return false;
				}
				else
				{
					symbols.Add(symbol);
				}
			}
			else
			{
				vector.Symbols.Add(symbol);
				symbolsDict.Add(vector);
			}
			
			return true;
		}
		/// <summary>
		/// Constructor de la clase.
		/// </summary>
		/// <param name="bitmap">La imagen que se ha comenzado a reconocer.</param>
		public BitmapProcessedArgs(MathTextBitmap bitmap)
			:base()
	    {
			b=bitmap;
		}
		private void OnUnassistedProcessBtnClicked(object sender, EventArgs args)
		{
			// We try to free memory.
			GC.Collect();
			segmentationStore.Clear();
			
			
			MainRecognizerWindow.ProcessItemsSensitive = true;
			unassistedControlHBB.Sensitive = false;
			unassistedTaskNameLabel.Text = "Segmentación y OCR";
			
			unassistedGlobalProgressBar.Fraction = 0;
			
			// We create the image to be recognized.
			
			Bitmap  bitmap = handwritingArea.Bitmap;
			
			FloatBitmap floatBitmap = 
				new FloatBitmap(bitmap.Width, bitmap.Height);
			
			for(int i=0; i< bitmap.Width; i++)
			{
				for(int j = 0; j < bitmap.Height; j++)
				{
					floatBitmap[i,j] = bitmap.GetPixel(i,j).GetBrightness();
				}
			}
			
			// The original image is set.
			originalImage = floatBitmap.CreatePixbuf();
			
			MathTextBitmap mtb = new MathTextBitmap(originalImage);
			
			SegmentedNode node = new SegmentedNode("Raíz",
			                                       mtb,
			                                       null);
			    
			segmentationStore.AddNode(node);
			
			ocrController.StartNode = node;
			ocrController.SearchDatabase = false;
			ocrController.Databases =  
				Config.RecognizerConfig.Instance.Databases;
			ocrController.Next(ControllerStepMode.UntilEnd);
		}
		/// <summary>
		/// Reconoce una imagen dada una imagen.
		/// </summary>
		/// <param name="image">
		/// La imagen a intentar asociar al caracter.
		/// </param>
		/// <returns>
		/// Los símbolos que se han podido asociar a la imagen.
		/// </returns>
		public List<MathSymbol> Recognize(MathTextBitmap image)
		{
			return database.Match(image);
		}
		/// <summary>
		/// Este método intenta asignar a una imagen un simbolo segun los parametros
		/// almacenados para la misma.
		/// </summary>
		/// <param name="image">
		/// La imagen que a la que queremos asociar un simbolo.
		/// </param>
		/// <returns>
		/// Una lista de simbolos cuyos parametros coinciden con los de la 
		/// imagen.
		/// </returns>
		public override List<MathSymbol> Match (MathTextBitmap image)
		{
			return null;
		}
示例#26
0
		/// <summary>
		/// Busca una imagen en la base de datos.
		/// </summary>
		/// <param name="image">
		/// La imagen que se procesará y buscará en la base de datos.
		/// </param>
		/// <returns>
		/// Una lista con todos los simbolos que coincidan con los parametros
		/// asociados a la imagen.
		/// </returns>
		public abstract List<MathSymbol> Match(MathTextBitmap image);
		/// <summary>
		/// Metodo que se encarga de dibujar un recuadro sobre la imagen original
		/// para señalar la zona que estamos tratando.
		/// </summary>
		/// <param name="mtb">La subimagen que estamos tratando.</param>
		private void MarkImage(MathTextBitmap mtb)
		{
			
			Pixbuf originalMarked= imageOriginal.Copy();	
			
			// We tint the copy in red.
			originalMarked = 
				originalMarked.CompositeColorSimple(originalMarked.Width,
				                                    originalMarked.Height,
				                                    InterpType.Bilinear,
				                                    100,1,
				                                    0xAAAAAA,0xAAAAAA);
				
			// Over the red tinted copy, we place the piece we want to be
			// normal.
			imageOriginal.CopyArea(mtb.Position.X,
			                       mtb.Position.Y,
			                       mtb.Width,
			                       mtb.Height,
			                       originalMarked,
			                       mtb.Position.X,
			                       mtb.Position.Y);
		
			
			imageAreaOriginal.Image=originalMarked;
			
		}
		/// <summary>
		/// Envolvemos el lanzamiento del evento BitmapBeingRecognized, por comodidad.
		/// </summary>
		/// <param name="bitmap">
		/// La imagen que hemos comenzado a reconocer, que sera enviada como
		/// argumentod del evento.
		/// </param>		
		protected void BitmapProcessedByDatabaseInvoker(MathTextBitmap bitmap)
		{
			if(BitmapProcessedByDatabase!=null)
			{
				BitmapProcessedByDatabase(this,new BitmapProcessedArgs(bitmap));
			}
		}
		/// <summary>
		/// <see cref="BitmapProjection"/>'s constructor
		/// </summary>
		/// <param name="image">
		/// The <see cref="MathTextBitmap"/> to be projected.
		/// </param>
		protected BitmapProjection(MathTextBitmap image)
		{		
			CreateProjection(image);
		}
		/// <summary>
		/// Con este metodos almacenamos un nuevo simbolo en la base de
		/// datos.
		/// 
		/// Lanza <c>DuplicateSymbolException</c> si ya hay un simbolo con las
		/// mismas propiedades y etiqueta en la base de datos.
		/// </summary>
		/// <param name="bitmap">
		/// La imagen que queremos añadir a la base de datos.
		/// </param>
		/// <param name="symbol">
		/// El simbolo que representa a la imagen.
		/// </param>
		public bool Learn(MathTextBitmap bitmap,MathSymbol symbol)
		{
			return database.Learn(bitmap,symbol);
		}