/// <summary> /// Este es el metodo a invocar para realizar el /// adelgazamiento de la imagen. /// </summary> /// <param name="image"> /// La imagen a ser procesada. /// </param> public override FloatBitmap Apply(FloatBitmap image) { width = image.Width + 2; height = image.Height + 2; FloatBitmap newImage = CreateAuxImage(image); /* Pre_process */ PreSmooth(newImage); Aae(newImage); ZhangSuenStentifordHoltThinning(newImage); FloatBitmap res = new FloatBitmap(width - 2, height - 2); for (int i = 0; i < width - 2; i++) { for (int j = 0; j < height - 2; j++) { res[i, j] = newImage[i + 1, j + 1]; } } return(res); }
public static void Copy(FloatBitmap src, FloatBitmap dst) { Parallel.For(0, dst.Image.Length, i => { dst.Image[i] = src.Image[i % src.Image.Length]; }); }
void InitHandles() { try { fbmp = new FloatBitmap((int)GradientImage.ActualWidth, (int)GradientImage.ActualHeight); HandleHolder.Children.Clear(); handles = new List <GradientHandle>(); object obj = property.GetValue(propertyOwner); if (obj is Gradient) { Gradient g = (Gradient)obj; if (g != null && g.positions != null && g.colors != null && g.positions.Length == g.colors.Length) { for (int i = 0; i < g.positions.Length; i++) { AddHandle(new System.Windows.Point(g.positions[i] * HandleHolder.ActualWidth - HANDLE_HALF_WIDTH, 0), g.colors[i]); } } else { AddHandle(new System.Windows.Point(-HANDLE_HALF_WIDTH, 0), new MVector(0, 0, 0, 1)); AddHandle(new System.Windows.Point(HandleHolder.ActualWidth - HANDLE_HALF_WIDTH, 0), new MVector(1, 1, 1, 1)); } } } catch (Exception e) { Log.Error(e); } }
/// <summary> /// Creates a bitmap out of a projection /// </summary> /// <param name="projection"> /// The projection <see cref="BitmapProjection"/> /// </param> /// <returns> /// A <see cref="Pixbuf"/> with the projection graphical representation. /// </returns> public static Pixbuf CreateProjectionBitmap(BitmapProjection projection) { int max = -1; foreach (int i in projection.Values) { if (max < i) { max = i; } } FloatBitmap bitmap = new FloatBitmap(projection.Values.Length, max); for (int k = 0; k < projection.Values.Length; k++) { Console.WriteLine(projection.Values[k]); for (int j = 0; j < projection.Values[k]; j++) { bitmap[k, j] = FloatBitmap.Black; } } return(bitmap.CreatePixbuf()); }
void InitHandles() { try { fbmp = new FloatBitmap((int)GradientImage.ActualWidth, (int)GradientImage.ActualHeight); HandleHolder.Children.Clear(); handles = new List <GradientHandle>(); object obj = property.GetValue(propertyOwner); if (obj is Gradient) { Gradient g = (Gradient)obj; if (g != null && g.positions != null && g.colors != null && g.positions.Length == g.colors.Length) { for (int i = 0; i < g.positions.Length; i++) { AddHandle(new Point(g.positions[i] * (HandleHolder.ActualWidth - 4), 0), g.colors[i]); } } else { AddHandle(new Point(0, 0), new MVector(0, 0, 0, 1)); AddHandle(new Point(HandleHolder.Width - 4, 0), new MVector(1, 1, 1, 1)); } } } catch (Exception e) { Console.WriteLine(e.Message + " | " + e.StackTrace); } }
/// <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(FloatBitmap image, out int x1, out int y1, out int x2, out int y2) { x1 = FindLeft(image); y1 = FindTop(image); x2 = FindRight(image); y2 = FindBottom(image); }
/// <summary> /// Crea una imagen auxiliar con dos filas y columnas mas que la original, /// necesario para el correcto funcionamiento del algoritmo de adelgazado. /// </summary> /// <param name="image">La imagen que vamos a adelgazar.</param> /// <returns>La imagen con las columnas y filas aadidas.</returns> private FloatBitmap CreateAuxImage(FloatBitmap image) { // La nueva imagen tiene dos filas y dos columnas ms que la original FloatBitmap newImage = new FloatBitmap(width, height); for (int i = 0; i < width - 2; i++) { for (int j = 0; j < height - 2; j++) { newImage[i + 1, j + 1] = image[i, j]; } } // Aadimos un marco blanco de un pixel de ancho for (int i = 0; i < width; i++) { newImage[i, 0] = 1; newImage[i, height - 1] = 1; } for (int j = 0; j < height; j++) { newImage[0, j] = 1; newImage[width - 1, j] = 1; } return(newImage); }
/// <summary> /// Se aplica el algoritmo de binarización de umbral fijo. /// </summary> /// <param name = "image"> /// La imagen que se binarizará. /// </param> /// <returns> /// La imagen binarizada. /// </returns> public override FloatBitmap Apply(FloatBitmap image) { int nrows = image.Width; int ncols = image.Height; FloatBitmap res = new FloatBitmap(nrows, ncols); float fthreshold = threshold / 255f; for (int i = 0; i < nrows; i++) { for (int j = 0; j < ncols; j++) { if (image[i, j] < fthreshold) { res[i, j] = FloatBitmap.Black; } else { res[i, j] = FloatBitmap.White; } } } return(res); }
public static void Clear(FloatBitmap dst) { Parallel.For(0, dst.Image.Length, i => { dst.Image[i] = 0; }); }
/// <summary> /// Calcula el numero de Yokoi de un pixel. /// </summary> /// <param name="im">La imagen donde se encuentra el pixel.</param> /// <param name="r">La coordenada Y del pixel.</param> /// <param name="c">La coordenada X del pixel.</param> /// <returns></returns> private int Yokoi(FloatBitmap im, int r, int c) { int[] N = new int[9]; int i, k, i1, i2; N[0] = (im[r, c] != 0?1:0); N[1] = (im[r, c + 1] != 0?1:0); N[2] = (im[r - 1, c + 1] != 0?1:0); N[3] = (im[r - 1, c] != 0?1:0); N[4] = (im[r - 1, c - 1] != 0?1:0); N[5] = (im[r, c - 1] != 0?1:0); N[6] = (im[r + 1, c - 1] != 0?1:0); N[7] = (im[r + 1, c] != 0?1:0); N[8] = (im[r + 1, c + 1] != 0?1:0); k = 0; for (i = 1; i <= 7; i += 2) { i1 = i + 1; if (i1 > 8) { i1 -= 8; } i2 = i + 2; if (i2 > 8) { i2 -= 8; } k += (N[i] - N[i] * N[i1] * N[i2]); } return(k); }
/// <summary> /// Este metodo implementa el presuavizado que se puede aplicar antes de /// adelgazar con este metodo. /// </summary> /// <param name="im">La imagen a la que se aplicara el presuavizado.</param> private void PreSmooth(FloatBitmap im) { int i, j; for (i = 0; i < nrows; i++) { for (j = 0; j < ncols; j++) { if (im[i, j] == 0) { if (NumberAdjacents8(im, i, j) <= 2 && Yokoi(im, i, j) < 2) { im[i, j] = 2; } } } } for (i = 0; i < nrows; i++) { for (j = 0; j < ncols; j++) { if (im[i, j] == 2) { im[i, j] = 1; } } } }
/// <summary> /// Deshace la rotacion hecha con el metodo <c>Rotate</c>. /// </summary> /// <param name="bitmap"> /// La imagen a rotar. /// </param> /// <returns> /// La imagen girada. /// </returns> private FloatBitmap UndoRotate(FloatBitmap bitmap) { FloatBitmap image = null; switch (mode) { case (WaterfallSegmenterMode.RightToLeft): image = bitmap.Rotate90().Rotate90().Rotate90(); break; case (WaterfallSegmenterMode.TopToBottom): image = new FloatBitmap(bitmap); break; case (WaterfallSegmenterMode.BottomToTop): image = bitmap.Rotate90().Rotate90(); break; default: image = bitmap.Rotate90(); break; } return(image); }
/// <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> /// Crea una nueva imagen añadiendo columnas en blanco a izquierda y derecha. /// </summary> /// <param name="image">La imagen a la que vamos a añadir columnas.</param> /// <param name="pad">El ancho del relleno a añadir.</param> /// <param name="y1">La coordenada Y de la esquina superior izquierda del contenido.</param> /// <param name="x1">La coordenada X de la esquina superior izquierda del contenido.</param> /// <param name="height">La altura del contenido.</param> /// <param name="width">La anchura del contenido.</param> /// <returns> /// Una matriz bidimensional con la imagen con las columnas añadidas. /// </returns> private FloatBitmap CreateNewImageColumns(FloatBitmap image, int pad, int y1, int x1, int height, int width) { // La nueva altura es la antigua mas dos, porque añdimos una // filas en blanco como borde // la nueva anchura es igual a la altura int newWidth = height + 2; int newHeight = height + 2; FloatBitmap newImage = new FloatBitmap(newWidth, newHeight); for (int i = 0; i < newWidth; i++) { for (int j = 0; j < newHeight; j++) { newImage[i, j] = FloatBitmap.White; } } // Copiamos la imagen original centrada for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { int centerH = i + (int)Math.Ceiling(((double)pad) / 2.0) + 1; newImage[centerH, j + 1] = image[i + x1, j + y1]; } } return(newImage); }
/// <summary> /// Crea una nueva imagen añadiendo filas en blanco arriba y abajo. /// </summary> /// <param name="image"> /// Una matriz bidemiensional con la imagen la que queremos añadir filas. /// </param> /// <param name="pad">El ancho del relleno.</param> /// <param name="y1"> /// La coordenada Y de la esquina superior izquierda del contenido. /// </param> /// <param name="x1"> /// La coordenada X de la esquina superior izquierda del contenido. /// </param> /// <param name="height">La altura del contenido.</param> /// <param name="width">La anchura del contenido.</param> /// <returns> /// Una matriz bidimensional con la imagen con las filas añadidas. /// </returns> private FloatBitmap CreateNewImageRows(FloatBitmap image, int pad, int y1, int x1, int height, int width) { int newWidth = width + 2; int newHeight = width + 2; // La nueva altura es la antigua mas dos, porque añadimos una // fila en blanco como borde la nueva anchura es igual a la altura FloatBitmap newImage = new FloatBitmap(newWidth, newHeight); for (int i = 0; i < newWidth; i++) { for (int j = 0; j < newHeight; j++) { newImage[i, j] = FloatBitmap.White; } } for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { int centerV = j + (int)Math.Ceiling(((double)pad) / 2.0) + 1; newImage[i + 1, centerV] = image[i + x1, j + y1]; } } return(newImage); }
/// <summary> /// Creamos una nueva imagen con dos filas y columnas mas, necesesario para el algoritmo. /// </summary> /// <param name="image">La imagen a procesar.</param> /// <returns>La imagen con las filas y columnas añadidas.</returns> private FloatBitmap CreateAuxImage(FloatBitmap image) { // La nueva imagen tiene dos filas y dos columnas mas que la original FloatBitmap newImage = new FloatBitmap(width, height); float brightness; for (int i = 0; i < width - 2; i++) { for (int j = 0; j < height - 2; j++) { brightness = image[i, j]; newImage[i + 1, j + 1] = brightness; } } for (int i = 0; i < width; i++) { newImage[i, 0] = 1; newImage[i, height - 1] = 1; } for (int j = 0; j < height; j++) { newImage[0, j] = 1; newImage[width - 1, j] = 1; } return(newImage); }
/* Stentiford's boundary smoothing method */ private void PreSmooth(FloatBitmap im) { int i, j; for (i = 0; i < width; i++) { for (j = 0; j < height; j++) { if (im[i, j] == 0) { if (Snays(im, i, j) <= 2 && Yokoi(im, i, j) < 2) { im[i, j] = 2; } } } } for (i = 0; i < width - 1; i++) { for (j = 0; j < height - 1; j++) { if (im[i, j] == 2) { im[i, j] = 1; } } } }
void UpdateGradientPreview(bool updateProperty = false) { if (handles.Count >= 2) { if (fbmp == null || fbmp.Width == 0 || fbmp.Height == 0) { fbmp = new FloatBitmap((int)ImageWrapper.ActualWidth, (int)ImageWrapper.ActualHeight); } if (fbmp.Width > 0 && fbmp.Height > 0) { handles.Sort((a, b) => { if (a.Position < b.Position) { return(-1); } else if (a.Position > b.Position) { return(1); } return(0); }); try { float[] pos = new float[handles.Count]; MVector[] cols = new MVector[handles.Count]; for (int i = 0; i < handles.Count; i++) { pos[i] = handles[i].Position; cols[i] = handles[i].SColor; } Utils.CreateGradient(fbmp, pos, cols); if (updateProperty) { Gradient g = new Gradient(); g.colors = cols; g.positions = pos; if (propertyOwner != null && property != null) { property.SetValue(propertyOwner, g); } } GradientImage.Source = fbmp.ToImageSource(); } catch (Exception e) { Log.Error(e); } } } }
/// <summary> /// Este metodo efectua el escalado de la imagen. /// </summary> /// <param name="image">La imagen que queremos escalar.</param> /// <returns>La imagen escalada al tamaño establecido.</returns> public override FloatBitmap Apply(FloatBitmap image) { Pixbuf pb = image.CreatePixbuf(); pb = pb.ScaleSimple(normalizedSize, normalizedSize, InterpType.Bilinear); return(FloatBitmap.CreateFromPixbuf(pb)); }
/// <summary> /// helper function for filling a float bitmap /// with a gradient from positions and colors /// colors and positions must be the same length /// as they should be 1 to 1 reference /// </summary> /// <param name="dst"></param> /// <param name="positions"></param> /// <param name="colors"></param> public static void CreateGradient(FloatBitmap dst, float[] positions, MVector[] colors) { if (positions.Length != colors.Length) { return; } //sort from least to greater Array.Sort(positions); List <float> pos = new List <float>(positions); List <MVector> cols = new List <MVector>(colors); if (positions[0] > 0) { pos.Insert(0, 0); MVector c = colors[0]; cols.Insert(0, c); } if (positions[positions.Length - 1] < 1) { pos.Add(1); MVector c = colors[colors.Length - 1]; cols.Add(c); } for (int i = 0; i < pos.Count - 1; i++) { float p1 = pos[i]; float p2 = pos[i + 1]; MVector c1 = cols[i]; MVector c2 = cols[i + 1]; ConvertToLAB(ref c1); ConvertToLAB(ref c2); int imin = (int)(p1 * dst.Width); int imax = (int)(p2 * dst.Width); for (int x = imin; x < imax; x++) { //minus 1 on imax otherwise we won't reach 1 for t float t = (float)(x - imin) / (float)(imax - 1 - imin); MVector n = MVector.Lerp(c1, c2, t); ConvertLABToRGB(ref n); for (int y = 0; y < dst.Height; y++) { dst.SetPixel(x, y, n.X, n.Y, n.Z, n.W); } } } }
/// <summary> /// Enmarca una imagen. /// </summary> /// <param name="image"> /// La imagen a enmarcar. /// </param> /// <param name="pos"> /// La esquina de la zona a recortar. /// </param> /// <param name="size"> /// El tamaño de la zona a recortar. /// </param> protected void GetEdges(FloatBitmap image, out Point pos, out Size size) { pos = new Point(0, 0); size = new Size(image.Width, image.Height); bool found = false; for (int i = 0; i < image.Width && !found; i++) { for (int j = 0; j < image.Height && !found; j++) { if (image[i, j] != FloatBitmap.White) { pos.X = i - 1; found = true; } } } found = false; for (int i = image.Width - 1; i >= 0 && !found; i--) { for (int j = 0; j < image.Height && !found; j++) { if (image[i, j] != FloatBitmap.White) { size.Width = i - pos.X + 2; found = true; } } } found = false; for (int j = 0; j < image.Height && !found; j++) { for (int i = 0; i < image.Width && !found; i++) { if (image[i, j] != FloatBitmap.White) { pos.Y = j - 1; found = true; } } } found = false; for (int j = image.Height - 1; j >= 0 && !found; j--) { for (int i = 0; i < image.Width && !found; i++) { if (image[i, j] != FloatBitmap.White) { size.Height = j - pos.Y + 2; found = true; } } } }
/// <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); }
public override bool Apply(FloatBitmap image) { int npixelsTop = CountPixelsHelper.NumBlackPixelsInHalf(image, Half.Top); int npixelsBottom = CountPixelsHelper.NumBlackPixelsInHalf(image, Half.Bottom); int tolerance = (int)((image.Width * image.Height) * epsilon); return(npixelsTop > npixelsBottom + tolerance); }
public override bool Apply(FloatBitmap image) { int npixelsLeft = CountPixelsHelper.NumBlackPixelsInHalf(image, Half.Left); int npixelsRight = CountPixelsHelper.NumBlackPixelsInHalf(image, Half.Right); int tolerance = (int)((image.Width * image.Height) * epsilon); return(npixelsLeft > npixelsRight + tolerance); }
void LoadDefaultTextures() { lightMat = new Material.PBRLight(); if (defaultBlack == null) { FloatBitmap black = new FloatBitmap(128, 128); Nodes.Helpers.Utils.Fill(black, 0, 0, 0f, 0f, 0f, 1); defaultBlack = new GLTextuer2D(PixelInternalFormat.Rgba8); defaultBlack.Bind(); defaultBlack.SetData(black.Image, GLInterfaces.PixelFormat.Rgba, 128, 128); defaultBlack.GenerateMipMaps(); defaultBlack.SetFilter((int)TextureMinFilter.LinearMipmapLinear, (int)TextureMagFilter.Linear); defaultBlack.SetWrap((int)TextureWrapMode.Repeat); GLTextuer2D.Unbind(); Materia.Nodes.Atomic.MeshNode.DefaultBlack = defaultBlack; } if (defaultWhite == null) { FloatBitmap black = new FloatBitmap(128, 128); Nodes.Helpers.Utils.Fill(black, 0, 0, 1f, 1f, 1f, 1); defaultWhite = new GLTextuer2D(PixelInternalFormat.Rgba8); defaultWhite.Bind(); defaultWhite.SetData(black.Image, GLInterfaces.PixelFormat.Rgba, 128, 128); defaultWhite.GenerateMipMaps(); defaultWhite.SetFilter((int)TextureMinFilter.LinearMipmapLinear, (int)TextureMagFilter.Linear); defaultWhite.SetWrap((int)TextureWrapMode.Repeat); GLTextuer2D.Unbind(); Materia.Nodes.Atomic.MeshNode.DefaultWhite = defaultWhite; } if (defaultGray == null) { FloatBitmap black = new FloatBitmap(128, 128); Nodes.Helpers.Utils.Fill(black, 0, 0, 0.5f, 0.5f, 0.5f, 1); defaultGray = new GLTextuer2D(PixelInternalFormat.Rgba8); defaultGray.Bind(); defaultGray.SetData(black.Image, GLInterfaces.PixelFormat.Rgba, 128, 128); defaultGray.GenerateMipMaps(); defaultGray.SetFilter((int)TextureMinFilter.LinearMipmapLinear, (int)TextureMagFilter.Linear); defaultGray.SetWrap((int)TextureWrapMode.Repeat); GLTextuer2D.Unbind(); } if (defaultDarkGray == null) { FloatBitmap black = new FloatBitmap(128, 128); Nodes.Helpers.Utils.Fill(black, 0, 0, 0.25f, 0.25f, 0.25f, 1); defaultDarkGray = new GLTextuer2D(PixelInternalFormat.Rgba8); defaultDarkGray.Bind(); defaultDarkGray.SetData(black.Image, GLInterfaces.PixelFormat.Rgba, 128, 128); defaultDarkGray.GenerateMipMaps(); defaultDarkGray.SetFilter((int)TextureMinFilter.LinearMipmapLinear, (int)TextureMagFilter.Linear); defaultDarkGray.SetWrap((int)TextureWrapMode.Repeat); GLTextuer2D.Unbind(); Materia.Nodes.Atomic.MeshNode.DefaultDarkGray = defaultDarkGray; } }
/// <summary> /// <c>Token</c>'s constructor. /// </summary> /// <param name="text"> /// The new token's text. /// </param> /// <param name="x"> /// The new token's upper left corner x coordinate. /// </param> /// <param name="y"> /// The new token's upper left corner y coordinate. /// </param> /// <param name="image"> /// The image which represents the new token. /// </param> public Token(string text, int x, int y, FloatBitmap image) : this() { this.text = text; this.x = x; this.y = y; this.image = image; }
public void FloatImageRotationTest() { FloatBitmap bitmap = new FloatBitmap(5, 4); bitmap[3, 1] = FloatBitmap.White; FloatBitmap rotatedBitmap = bitmap.Rotate90(); Assert.AreEqual(FloatBitmap.White, rotatedBitmap[1, 2]); }
private void FillLUT() { if (LUT == null) { LUT = new FloatBitmap(256, 2); } //generate gradient Utils.CreateGradient(LUT, gradient.positions, gradient.colors); }
public static void Fill(FloatBitmap dst, int xOffset, int yOffset, float r = 0, float g = 0, float b = 0, float a = 0) { Parallel.For(yOffset, dst.Height, y => { for (int x = xOffset; x < dst.Width; x++) { dst.SetPixel(x, y, r, g, b, a); } }); }
/// <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); }