/// <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; }
/// <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; }
public static int BlackNeighbours(FloatBitmap image, int x, int y) { int res=0; int width = image.Width; int height = image.Height; if(x-1>=0 && y-1 >= 0 && image[x-1,y-1]!=FloatBitmap.White) res++; if(x-1>=0 && image[x-1,y]!=FloatBitmap.White) res++; if(x-1>=0 && y+1 < height && image[x-1,y+1]!=FloatBitmap.White) res++; if(y-1 >= 0 && image[x,y-1]!=FloatBitmap.White) res++; if(y+1 < height && image[x,y+1]!=FloatBitmap.White) res++; if(x+1 < width && y-1 >= 0 && image[x+1,y-1]!=FloatBitmap.White) res++; if(x+1 < width && image[x+1,y]!=FloatBitmap.White) res++; if(x+1 < width && y+1 < height && image[x+1,y+1]!=FloatBitmap.White) res++; return res; }
/// <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; }
/// <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> /// 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> /// Cuenta el numero de pixeles negros en la mitad indicada por <c>h</c>. /// </summary> /// <remarks> /// Si la imagen tiene un numero impar de pixeles se incluye la fila /// o columna central, segun el caso. /// </remarks> /// <param name="image">Imagen sobre la que se trabaja</param> /// <param name="h">Mitad a analizar</param> /// <returns>Numero de pixeles negros</returns> public static int NumBlackPixelsInHalf(FloatBitmap image, Half h) { int n=0; int width = image.Width; int height = image.Height; int halfWidth = width/2; int halfHeight = height/2; switch(h) { case(Half.Top): n=NumPixelsInArea(image,FloatBitmap.Black, 0,0, width,halfHeight); break; case(Half.Bottom): n=NumPixelsInArea(image,FloatBitmap.Black, 0,halfHeight, width,height); break; case(Half.Left): n=NumPixelsInArea(image,FloatBitmap.Black, 0,0, halfWidth,height); break; case(Half.Right): n=NumPixelsInArea(image,FloatBitmap.Black, halfWidth,0, width,height); break; } return n; }
/// <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> /// 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; } } } }
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; }
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; }
/// <summary> /// Constructor de un nuevo <c>MathTextBitmap</c> a partir de un /// array de float y su posicion y un modo de proyeccion. /// </summary> /// <remarks> /// El array se clona para evitar efectos laterales. La imagen se /// procesa mediante el metodo <c>ProcessImage()</c>. /// </remarks> public MathTextBitmap(FloatBitmap image, Point pos) { this.image = image; this.position = pos; processedImages = new List<FloatBitmap>(); width = image.Width; height = image.Height; }
/// <summary> /// Constructor de un nuevo <c>MathTextBitmap</c> a partir de un /// <c>Bitmap</c>. /// </summary> /// <remarks> /// El bitmap se convierte a escala de grises y se procesa mediante /// el metodo <c>ProcessImage()</c>. /// </remarks> /// <seealso cref="System.Drawing.Bitmap"/> public MathTextBitmap(Pixbuf b) { this.position = new Point(0,0); processedImages = new List<FloatBitmap>(); image = FloatBitmap.CreateFromPixbuf(b); width = b.Width; height = b.Height; }
/// <summary> /// Constructor de un nuevo <c>MathTextBitmap</c> a partir de un /// <c>Bitmap</c>. /// </summary> /// <remarks> /// El bitmap se convierte a escala de grises y se procesa mediante /// el metodo <c>ProcessImage()</c>. /// </remarks> /// <seealso cref="System.Drawing.Bitmap"/> public MathTextBitmap(Pixbuf b) { this.position = new Point(0, 0); processedImages = new List <FloatBitmap>(); image = FloatBitmap.CreateFromPixbuf(b); width = b.Width; height = b.Height; }
/// <summary> /// Constructor de un nuevo <c>MathTextBitmap</c> a partir de un /// array de float y su posicion y un modo de proyeccion. /// </summary> /// <remarks> /// El array se clona para evitar efectos laterales. La imagen se /// procesa mediante el metodo <c>ProcessImage()</c>. /// </remarks> public MathTextBitmap(FloatBitmap image, Point pos) { this.image = image; this.position = pos; processedImages = new List <FloatBitmap>(); width = image.Width; height = image.Height; }
public void CountPixelsHelperHalfsTest() { FloatBitmap bitmap = new FloatBitmap(5,5); bitmap[1,0] = FloatBitmap.Black; bitmap[1,1] = FloatBitmap.Black; bitmap[3,2] = FloatBitmap.Black; int ct1 = CountPixelsHelper.NumBlackPixelsInHalf(bitmap, Half.Top); int cb1 = CountPixelsHelper.NumBlackPixelsInHalf(bitmap, Half.Bottom); int cl1 = CountPixelsHelper.NumBlackPixelsInHalf(bitmap, Half.Left); int cr1 = CountPixelsHelper.NumBlackPixelsInHalf(bitmap, Half.Right); Assert.AreEqual(2, ct1,"Arriba 1"); Assert.AreEqual(1, cb1,"Abajo 1"); Assert.AreEqual(2, cl1,"Izquierda 1"); Assert.AreEqual(1, cr1,"Derecha 1"); bitmap = bitmap.Rotate90(); int ct2 = CountPixelsHelper.NumBlackPixelsInHalf(bitmap, Half.Top); int cb2 = CountPixelsHelper.NumBlackPixelsInHalf(bitmap, Half.Bottom); int cl2 = CountPixelsHelper.NumBlackPixelsInHalf(bitmap, Half.Left); int cr2 = CountPixelsHelper.NumBlackPixelsInHalf(bitmap, Half.Right); Assert.AreEqual(1, ct2,"Arriba 2"); Assert.AreEqual(2, cb2,"Abajo 2"); Assert.AreEqual(2, cl2,"Izquierda 2 "); Assert.AreEqual(1, cr2,"Derecha 2"); bitmap = bitmap.Rotate90(); int ct3 = CountPixelsHelper.NumBlackPixelsInHalf(bitmap, Half.Top); int cb3 = CountPixelsHelper.NumBlackPixelsInHalf(bitmap, Half.Bottom); int cl3 = CountPixelsHelper.NumBlackPixelsInHalf(bitmap, Half.Left); int cr3 = CountPixelsHelper.NumBlackPixelsInHalf(bitmap, Half.Right); Assert.AreEqual(0, ct3,"Arriba 3"); Assert.AreEqual(3, cb3,"Abajo 3"); Assert.AreEqual(1, cl3,"Izquierda 3"); Assert.AreEqual(2, cr3,"Derecha 3"); bitmap = bitmap.Rotate90(); int ct4 = CountPixelsHelper.NumBlackPixelsInHalf(bitmap, Half.Top); int cb4 = CountPixelsHelper.NumBlackPixelsInHalf(bitmap, Half.Bottom); int cl4 = CountPixelsHelper.NumBlackPixelsInHalf(bitmap, Half.Left); int cr4 = CountPixelsHelper.NumBlackPixelsInHalf(bitmap, Half.Right); Assert.AreEqual(2, ct4,"Arriba 4"); Assert.AreEqual(1, cb4,"Abajo 4"); Assert.AreEqual(0, cl4,"Izquierda 4"); Assert.AreEqual(3, cr4,"Derecha 4"); }
/// <summary> /// Constructor copia de <c>FloatBitmap</c>. /// </summary> /// <param name="source"> /// La instancia que se copiará. /// </param> public FloatBitmap(FloatBitmap source) : this(source.Width, source.Height) { for (int i = 0; i < source.Width; i++) { for (int j = 0; j < source.Height; j++) { this.image[i, j] = source.image[i, j]; } } }
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]); }
public override bool Apply(FloatBitmap image) { if(CountColorChangesHelper.NumColorChangesColumn(image, image.Height/2) < 2) { return true; } else { return false; } }
public override bool Apply(FloatBitmap image) { if(CountColorChangesHelper.NumColorChangesRow(image, image.Width/2) > 4) { return true; } else { return false; } }
/// <summary> /// Procesa la imagen actual mediante binarizacion, encuadre, /// normalizacion y adelgazamiento. /// </summary> public void ProcessImage(List <BitmapProcess> processes) { FloatBitmap processedImage = new FloatBitmap(image); foreach (BitmapProcess process in processes) { processedImage = process.Apply(processedImage); } processedImages.Add(processedImage); }
/// <summary> /// Constructor copia de <c>FloatBitmap</c>. /// </summary> /// <param name="source"> /// La instancia que se copiará. /// </param> public FloatBitmap(FloatBitmap source) : this(source.Width, source.Height) { for(int i=0; i<source.Width; i++) { for(int j=0;j<source.Height; j++) { this.image[i,j] = source.image[i,j]; } } }
public override bool Apply(FloatBitmap image) { int npixelsNW=CountPixelsHelper.NumBlackPixelsInQuadrant(image, Quadrant.NW); int npixelsNE=CountPixelsHelper.NumBlackPixelsInQuadrant(image, Quadrant.NE); int npixelsSW=CountPixelsHelper.NumBlackPixelsInQuadrant(image, Quadrant.SW); int npixelsSE=CountPixelsHelper.NumBlackPixelsInQuadrant(image, Quadrant.SE); int tolerance = (int)((image.Width * image.Height)*epsilon); return (npixelsSE>npixelsNE + tolerance) && (npixelsSE>npixelsNW + tolerance) && (npixelsSE>npixelsSW + tolerance); }
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> /// Devuelve una sub imagene. /// </summary> /// <param name="x">Minima posicion horizontal de la subimagen.</param> /// <param name="y">Minima posicion vertical de la subimagen.</param> /// <param name="width">Anchura de la subimagen medida desde <c>x</c></param> /// <param name="height">Altura de la subimagen medida desde <c>y</c></param> /// <returns>Array de float que representa la subimagen deseada.</returns> public FloatBitmap SubImage(int x, int y, int width, int height) { FloatBitmap resImage = new FloatBitmap(width, height); for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { resImage[i, j] = this[x + i, y + j]; } } return(resImage); }
/// <summary> /// Cuenta el numero de pixeles negros en una imagen en la /// fila indicada. /// </summary> /// <param name="image">Imagen sobre la que se trabaja</param> /// <param name="row">Fila a analizar</param> /// <returns>Numero de pixeles negros</returns> public static int NumBlackPixelsRow(FloatBitmap image, int row) { int nBlackPixels=0; for(int i=0; i<image.Width; i++) { if(image[i,row] != FloatBitmap.White) { nBlackPixels++; } } return nBlackPixels; }
public void CountBlackNeighboursHelperTest() { FloatBitmap bitmap = new FloatBitmap(5,5); bitmap[1,1] = FloatBitmap.Black; bitmap[1,2] = FloatBitmap.Black; bitmap[2,3] = FloatBitmap.Black; int res11 = CountBlackNeighboursHelper.BlackNeighbours(bitmap,1,1); int res12 = CountBlackNeighboursHelper.BlackNeighbours(bitmap,1,2); Assert.AreEqual(1,res11, "Pixel (1,1)"); Assert.AreEqual(2,res12, "Pixel (1,2)"); }
/// <summary> /// Gira 90 grados una imagen de forma no destructiva. /// </summary> /// <returns> /// La copia de la original, girada. /// </returns> public FloatBitmap Rotate90() { FloatBitmap res = new FloatBitmap(this.Height, this.Width); for (int i = 0; i < this.Width; i++) { for (int j = 0; j < this.Height; j++) { res[j, Width - i - 1] = this[i, j]; } } return(res); }
/// <summary> /// Cuenta el numero de cambios blanco-negro en una imagen en la /// fila indicada. /// </summary> /// <param name="image">Imagen sobre la que se trabaja</param> /// <param name="row">Fila a analizar</param> /// <returns>Numero de cambios de color</returns> public static int NumColorChangesRow(FloatBitmap image, int row) { int nChanges=0; for(int i=1; i<image.Width; i++) { if(image[i,row]!=image[i-1,row]) { nChanges++; } } return nChanges; }
/// <summary> /// Cuenta el numero de cambios blanco-negro en una imagen en la /// columna indicada. /// </summary> /// <param name="image">Imagen sobre la que se trabaja</param> /// <param name="column">Columna a analizar</param> /// <returns>Numero de cambios de color</returns> public static int NumColorChangesColumn(FloatBitmap image, int column) { int nChanges=0; for(int j=1; j<image.Height; j++) { if(image[column,j] != image[column,j-1]) { nChanges++; } } return nChanges; }
/// <summary> /// Cuenta el numero de pixeles negros en una imagen en la /// columna indicada. /// </summary> /// <param name="image">Imagen sobre la que se trabaja</param> /// <param name="column">Columna a analizar</param> /// <returns>Numero de pixeles negros</returns> public static int NumBlackPixelsColumn(FloatBitmap image, int column) { int nBlackPixels=0; for(int i=0; i<image.Height; i++) { if(image[column,i] != FloatBitmap.White) { nBlackPixels++; } } return nBlackPixels; }
/// <summary> /// Este metodo se invoca para adelgazar una imagen. /// </summary> /// <param name="image"> /// La imagen que queremos adelgazar, en formato de matriz bidimensional. /// </param> /// <returns> /// La imagen adelgazada. /// </returns> public override FloatBitmap Apply(FloatBitmap image) { //Seguimos el algoritmo que aparece en Parker, reescrito en CSharp FloatBitmap im; int i,j; nrows=image.Width+2; ncols=image.Height+2; im = new FloatBitmap(nrows,ncols); for (i=0; i<nrows-2; i++) for (j=0; j<ncols-2; j++) im[i+1,j+1] = image[i,j]; for (i=0; i<nrows; i++) { im[i,0] = 1; im[i,ncols-1] = 1; } for (j=0; j<ncols; j++) { im[0,j] = 1; im[nrows-1,j] = 1; } if(presmooth) { PreSmooth(im); } ThinStentiford(im); FloatBitmap res = new FloatBitmap(nrows - 2, ncols -2); for (i=0; i<nrows-2; i++) { for (j=0; j<ncols-2; j++) { res[i,j] = im[i+1,j+1]; } } return res; }
/// <summary> /// Obtiene la coordenada superior del minimo rectangulo que contiene /// todos los pixeles negros de la imagen. /// </summary> /// <param name="image">Imagen sobre la que se trabaja</param> /// <returns>Coordenada del borde superior del rectangulo que /// contiene los pixeles negros de la imagen</returns> /// <exception "System.ApplicationException">Lanzada si no se encuentra /// ningun pixel negro en la imagen</exception> private static int FindTop(FloatBitmap image) { int width=image.Width; int height=image.Height; for(int i=0;i<height;i++) { for(int j=0;j<width;j++) { if(image[j,i]!=FloatBitmap.White) { return i; } } } throw new ApplicationException("No se ha encontrado ningun pixel negro en ImageBoxerHelper.FindTop()!"); }
public void CountColorChangesHelperTest() { FloatBitmap bitmap = new FloatBitmap(5,5); bitmap[1,2] = FloatBitmap.Black; bitmap[3,2] = FloatBitmap.Black; int resC1 = CountColorChangesHelper.NumColorChangesColumn(bitmap,1); int resC2 = CountColorChangesHelper.NumColorChangesColumn(bitmap,2); int resR1 = CountColorChangesHelper.NumColorChangesRow(bitmap,1); int resR2 = CountColorChangesHelper.NumColorChangesRow(bitmap,2); Assert.AreEqual(2,resC1,"Columna 1"); Assert.AreEqual(0,resC2,"Columna 2"); Assert.AreEqual(0,resR1, "Fila 1"); Assert.AreEqual(4,resR2, "Fila 2"); }
public override bool Apply(FloatBitmap image) { int x1,y1,x2,y2; try { ImageBoxerHelper.BoxImage(image,out x1,out y1,out x2,out y2); } catch(ApplicationException) { return false; } int width=(x2-x1+1); int height=(y2-y1+1); int tolerance = (int) (image.Width * epsilon); return height >= 2*width + tolerance; }
/// <summary> /// Convierte el <c>Pixbuf</c> pasado como parámetro a un array de float. /// </summary> /// <param name="b"> /// Imagen bitmap a convertir. /// </param> /// <returns> /// Array de float bidimensional conteniendo la misma /// informacion de pixeles que el bitmap original, pero en escala de /// grises, y de forma que la coordenada Y de la imagen se almacena /// en la primera componente del array. /// </returns> public static FloatBitmap CreateFromPixbuf(Pixbuf b) { FloatBitmap imageRes = new FloatBitmap(b.Width, b.Height); int pixelStep = b.NChannels; // Tenemos que compensar los pixeles que se añaden para tener // un rowstride optimo. int rowstrideCompensation = b.Rowstride - pixelStep * b.Width; unsafe { byte *data = (byte *)b.Pixels; int k = 0; float color; for (int j = 0; j < b.Height; j++) { for (int i = 0; i < b.Width; i++) { // Usamos la formula para la luminosidad NTSC color = data[k] * 0.299f + data[k + 1] * 0.587f + data[k + 2] * 0.114f; imageRes[i, j] = color / 255.0f; k += pixelStep; } k += rowstrideCompensation; } } if (imageRes.Width != b.Width || imageRes.Height != b.Height) { throw new Exception("Error al crear la matriz a partir del Pixbuf"); } return(imageRes); }
/// <summary> /// Cuenta el numero de pixeles negros en la imagen que tengan /// <c>neighbours</c> o mas vecinos. /// </summary> /// <param name="image">Imagen sobre la que se trabaja</param> /// <param name="neighbours">Numero de vecinos</param> /// <returns>Numero de pixeles negros con <c>neighbours</c> o mas /// vecinos</returns> public static int CountPixelsXOrMoreNeighbours(FloatBitmap image, int neighbours) { int count=0; int width=image.Width; int height = image.Height; for(int i=0;i<width;i++) { for(int j=0;j<height;j++) { if(image[i,j]==FloatBitmap.Black && CountBlackNeighboursHelper.BlackNeighbours(image,i,j)>=neighbours) { count++; } } } return count; }