/// <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> /// 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> /// 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> /// 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> /// Sets the database the symbols will be learned into, and makes the /// proper interface elementents (un)sensitive. /// </summary> /// <param name="database"> /// A <see cref="MathTextDatabase"/> /// </param> private void SetDatabase(MathTextDatabase database) { this.database = database; database.StepDone += new ProcessingStepDoneHandler(OnLearningStepDone); messageInfoHB.Visible = false; mtb = null; toolSaveAs.Sensitive = true; menuSaveAs.Sensitive = true; imagesHB.Sensitive = true; imagesVB.Sensitive = true; imagesStore.Clear(); hboxSymbolWidgets.Sensitive = false; nextButtonsHB.Sensitive = false; editPropertiesBtn.Sensitive = true; symbolLabelEditor.Label = ""; SetDatabaseInfo(); }
private void OnImagesIVSelectionChanged(object s, EventArgs a) { removeImageBtn.Sensitive = imagesIV.SelectedItems.Length > 0; TreeIter selectedIter; if (imagesIV.SelectedItems.Length > 0) { TreePath selectedImagePath = imagesIV.SelectedItems[0]; imagesStore.GetIter(out selectedIter, selectedImagePath); Gdk.Pixbuf orig = (Gdk.Pixbuf)(imagesStore.GetValue(selectedIter, 1)); mtb = new MathTextBitmap(orig); mtb.ProcessImage(database.Processes); imageAreaOriginal.Image = orig; imageAreaProcessed.Image = mtb.LastProcessedImage.CreatePixbuf(); } else { imageAreaOriginal.Image = null; imageAreaProcessed.Image = null; } }
/// <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); }
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> /// 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> /// <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); }
/// <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>(); FloatBitmap processedImage = image.LastProcessedImage; CheckVector vector = CreateVector(processedImage); // We have this image vector, now we will compare with the stored ones. // We consider a threshold. int threshold = (int)(characteristics.Count * epsilon); foreach (CheckVector storedVector in symbolsDict) { // If the distance is below the threshold, we consider it valid. if (vector.Distance(storedVector) <= 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); }
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.Height]; for (int i = 0; i < image.Height; i++) { for (int j = 0; j < image.Width; j++) { if (image[j, i] != FloatBitmap.White) { Values[i]++; } } } }
/// <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> /// 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>(); }
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> /// 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> /// 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> /// 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> /// Muestra el mensaje que indica como termino el proceso de aprendizaje, /// e inicicializa contadores y otras variables relacionadas /// con el proceso de aprendizaje actual. /// </summary> private void AllImagesLearned() { string conflictsMessage = ""; // Generamos la cadena que informa de los conflictos switch (conflicts) { case 0: break; case 1: conflictsMessage = "Hubo un conflicto entre caracteres."; break; default: conflictsMessage = String.Format("Hubo {0} conflictos entre caracteres.", conflicts); break; } // Informamos al usuario. OkDialog.Show(mainWindow, MessageType.Info, "Todos los caracteres fueron procesados.\n{0}", conflictsMessage); mtb = null; // Reinciamos el contador de conflictos. conflicts = 0; nextImageBtn.Sensitive = false; imagesVB.Sensitive = true; }
/// <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; }
/// <summary> /// Obtiene el menor rectangulo que contiene los pixeles negros de /// la imagen y devuelve sus esquinas superior izquierda e /// inferior derecha como (x1,y1) y (x2,y2) respectivamente. /// </summary> /// <param name="image">Imagen sobre la que se trabaja</param> /// <param name="x1">Minima coordenada horizontal</param> /// <param name="y1">Minima coordenada vertical</param> /// <param name="x2">Maxima coordenada horizontal</param> /// <param name="y2">Maxima coordenada vertical</param> public static void BoxImage(MathTextBitmap image, out int x1, out int y1, out int x2, out int y2) { FloatBitmap im = image.LastProcessedImage; BoxImage(im, out x1, out y1, out x2, out y2); }
/// <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; }
internal VerticalBitmapProjection(MathTextBitmap image) : base(image) { }
/// <summary> /// Creates the projection. /// </summary> /// <param name="image"> /// The <see cref="MathTextBitmap"/> being projected. /// </param> protected abstract void CreateProjection(MathTextBitmap image);
/// <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. /// </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);
/// <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> /// Con este metodo cramos un arbol de imagenes, de forma recursiva. /// Primero intentamos reconocer la imagen como un caracter, si no es /// posible, la intentamos segmentar. Si ninguno de estos procesos es /// posible, la imagen nopudo ser reconocida. /// </summary> /// <param name="node"> /// El nodo donde esta la imagen sobre la que trabajaremos. /// </param> private void RecognizerTreeBuild(SegmentedNode node) { // Seleccionamos el nodo. NodeBeingProcessedInvoker(node); SuspendByNode(); node.Select(); while (node.ChildCount > 0) { node.RemoveChild((SegmentedNode)node[0]); } MathTextBitmap bitmap = node.MathTextBitmap; MessageLogSentInvoker("======================================="); MessageLogSentInvoker("Tratando la subimagen «{0}»", node.Name); // Lanzamos el reconocedor de caracteres para cada una de // las bases de datos. List <MathSymbol> associatedSymbols = new List <MathSymbol>(); if (searchDatabase) { foreach (MathTextDatabase database in databases) { MessageLogSentInvoker("---------- «{0}» ------------", database.ShortDescription); bitmap.ProcessImage(database.Processes); BitmapProcessedByDatabaseInvoker(bitmap); // Añadimos los caracteres reconocidos por la base de datos foreach (MathSymbol symbol in database.Recognize(bitmap)) { if (!associatedSymbols.Contains(symbol)) { // Solo añadimos si no esta ya el simbolo. associatedSymbols.Add(symbol); } } } } // We associate all symbols to the node, so we can postargate // the decission step. node.Symbols = associatedSymbols; //Si no hemos reconocido nada, pues intentaremos segmentar el caracter. if (associatedSymbols.Count == 0) { if (searchDatabase) { MessageLogSentInvoker("La imagen no pudo ser reconocida como un " + "simbolo por la base de datos, se tratará de " + "segmentar"); } else { MessageLogSentInvoker("Se procede directamente a segmentar la imagen"); this.SearchDatabase = true; } List <MathTextBitmap> children = CreateChildren(bitmap); if (children.Count > 1) { MessageLogSentInvoker("La imagen se ha segmentado correctamente"); //Si solo conseguimos un hijo, es la propia imagen, asi que nada List <SegmentedNode> nodes = new List <SegmentedNode>(); foreach (MathTextBitmap child in children) { SegmentedNode childNode = new SegmentedNode(child, node.View); node.AddSegmentedChild(childNode); nodes.Add(childNode); } foreach (SegmentedNode childNode in nodes) { RecognizerTreeBuild(childNode); } } else { MessageLogSentInvoker("La imagen no pudo ser segmentada, el " + "símbolo queda sin reconocer"); } } else { // We prepare the string. string text = ""; foreach (MathSymbol s in associatedSymbols) { text += String.Format("«{0}», ", s.Text); } text = text.TrimEnd(',', ' '); MessageLogSentInvoker("Símbolo reconocido por la base de datos como {0}.", text); } }
/// <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);