// Process specified image trying to recognize counter's image public void Process(Bitmap image, IImageProcessingLog log) { log.AddMessage("Image size: " + image.Width + " x " + image.Height); // 1 - Grayscale Bitmap grayImage = Grayscale.CommonAlgorithms.BT709.Apply(image); log.AddImage("Grayscale", grayImage); // 2 - Edge detection DifferenceEdgeDetector edgeDetector = new DifferenceEdgeDetector( ); Bitmap edges = edgeDetector.Apply(grayImage); log.AddImage("Edges", edges); // 3 - Threshold edges Threshold thresholdFilter = new Threshold(40); thresholdFilter.ApplyInPlace(edges); log.AddImage("Thresholded Edges", edges); // 4 - Blob Counter BlobCounter blobCounter = new BlobCounter( ); blobCounter.MinHeight = 32; blobCounter.MinWidth = 32; blobCounter.FilterBlobs = true; blobCounter.ObjectsOrder = ObjectsOrder.Size; blobCounter.ProcessImage(edges); Blob[] blobs = blobCounter.GetObjectsInformation( ); // create copy of source image, so we could draw on it Bitmap imageCopy = AForge.Imaging.Image.Clone(image); BitmapData imageData = imageCopy.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, imageCopy.PixelFormat); // lock grayscale image, so we could access it's pixel values BitmapData grayData = grayImage.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, grayImage.PixelFormat); UnmanagedImage grayUI = new UnmanagedImage(grayData); // list of found dark/black quadrilaterals surrounded by white area List <List <IntPoint> > foundObjects = new List <List <IntPoint> >( ); // shape checker for checking quadrilaterals SimpleShapeChecker shapeChecker = new SimpleShapeChecker( ); // 5 - check each blob for (int i = 0, n = blobs.Length; i < n; i++) { List <IntPoint> edgePoints = blobCounter.GetBlobsEdgePoints(blobs[i]); List <IntPoint> corners = null; // does it look like a quadrilateral ? if (shapeChecker.IsQuadrilateral(edgePoints, out corners)) { // do some more checks to filter so unacceptable shapes // if ( CheckIfShapeIsAcceptable( corners ) ) { log.AddMessage("Blob size: " + blobs[i].Rectangle.Width + " x " + blobs[i].Rectangle.Height); // get edge points on the left and on the right side List <IntPoint> leftEdgePoints, rightEdgePoints; blobCounter.GetBlobsLeftAndRightEdges(blobs[i], out leftEdgePoints, out rightEdgePoints); // calculate average difference between pixel values from outside of the shape and from inside float diff = CalculateAverageEdgesBrightnessDifference( leftEdgePoints, rightEdgePoints, grayUI); log.AddMessage("Avg Diff: " + diff); // check average difference, which tells how much outside is lighter than inside on the average if (diff > 20) { Drawing.Polygon(imageData, corners, Color.Red); // add the object to the list of interesting objects for further processing foundObjects.Add(corners); } } } } imageCopy.UnlockBits(imageData); grayImage.UnlockBits(grayData); log.AddImage("Potential glyps", imageCopy); int counter = 1; // further processing of each potential glyph foreach (List <IntPoint> corners in foundObjects) { log.AddMessage("Glyph #" + counter); log.AddMessage(string.Format("Corners: ({0}), ({1}), ({2}), ({3})", corners[0], corners[1], corners[2], corners[3])); // 6 - do quadrilateral transformation QuadrilateralTransformation quadrilateralTransformation = new QuadrilateralTransformation(corners, 250, 250); Bitmap transformed = quadrilateralTransformation.Apply(grayImage); log.AddImage("Transformed #" + counter, transformed); // 7 - otsu thresholding OtsuThreshold otsuThresholdFilter = new OtsuThreshold( ); Bitmap transformedOtsu = otsuThresholdFilter.Apply(transformed); log.AddImage("Transformed Otsu #" + counter, transformedOtsu); int glyphSize = 5; SquareBinaryGlyphRecognizer gr = new SquareBinaryGlyphRecognizer(glyphSize); bool[,] glyphValues = gr.Recognize(transformedOtsu, new Rectangle(0, 0, 250, 250)); log.AddImage("Glyph lines #" + counter, transformedOtsu); // output recognize glyph to log log.AddMessage(string.Format("glyph: {0:F2}%", gr.confidence * 100)); for (int i = 0; i < glyphSize; i++) { StringBuilder sb = new StringBuilder(" "); for (int j = 0; j < glyphSize; j++) { sb.Append((glyphValues[i, j]) ? "1 " : "0 "); } log.AddMessage(sb.ToString( )); } counter++; } }
// Process specified image trying to recognize counter's image public void Process( Bitmap image, IImageProcessingLog log ) { log.AddMessage( "Image size: " + image.Width + " x " + image.Height ); // 1 - Grayscale Bitmap grayImage = Grayscale.CommonAlgorithms.BT709.Apply( image ); log.AddImage( "Grayscale", grayImage ); // 2 - Edge detection DifferenceEdgeDetector edgeDetector = new DifferenceEdgeDetector( ); Bitmap edges = edgeDetector.Apply( grayImage ); log.AddImage( "Edges", edges ); // 3 - Threshold edges Threshold thresholdFilter = new Threshold( 40 ); thresholdFilter.ApplyInPlace( edges ); log.AddImage( "Thresholded Edges", edges ); // 4 - Blob Counter BlobCounter blobCounter = new BlobCounter( ); blobCounter.MinHeight = 32; blobCounter.MinWidth = 32; blobCounter.FilterBlobs = true; blobCounter.ObjectsOrder = ObjectsOrder.Size; blobCounter.ProcessImage( edges ); Blob[] blobs = blobCounter.GetObjectsInformation( ); // create copy of source image, so we could draw on it Bitmap imageCopy = AForge.Imaging.Image.Clone( image ); BitmapData imageData = imageCopy.LockBits( new Rectangle( 0, 0, image.Width, image.Height ), ImageLockMode.ReadWrite, imageCopy.PixelFormat ); // lock grayscale image, so we could access it's pixel values BitmapData grayData = grayImage.LockBits( new Rectangle( 0, 0, image.Width, image.Height ), ImageLockMode.ReadOnly, grayImage.PixelFormat ); UnmanagedImage grayUI = new UnmanagedImage( grayData ); // list of found dark/black quadrilaterals surrounded by white area List<List<IntPoint>> foundObjects = new List<List<IntPoint>>( ); // shape checker for checking quadrilaterals SimpleShapeChecker shapeChecker = new SimpleShapeChecker( ); // 5 - check each blob for ( int i = 0, n = blobs.Length; i < n; i++ ) { List<IntPoint> edgePoints = blobCounter.GetBlobsEdgePoints( blobs[i] ); List<IntPoint> corners = null; // does it look like a quadrilateral ? if ( shapeChecker.IsQuadrilateral( edgePoints, out corners ) ) { // do some more checks to filter so unacceptable shapes // if ( CheckIfShapeIsAcceptable( corners ) ) { log.AddMessage( "Blob size: " + blobs[i].Rectangle.Width + " x " + blobs[i].Rectangle.Height ); // get edge points on the left and on the right side List<IntPoint> leftEdgePoints, rightEdgePoints; blobCounter.GetBlobsLeftAndRightEdges( blobs[i], out leftEdgePoints, out rightEdgePoints ); // calculate average difference between pixel values from outside of the shape and from inside float diff = CalculateAverageEdgesBrightnessDifference( leftEdgePoints, rightEdgePoints, grayUI ); log.AddMessage( "Avg Diff: " + diff ); // check average difference, which tells how much outside is lighter than inside on the average if ( diff > 20 ) { Drawing.Polygon( imageData, corners, Color.Red ); // add the object to the list of interesting objects for further processing foundObjects.Add( corners ); } } } } imageCopy.UnlockBits( imageData ); grayImage.UnlockBits( grayData ); log.AddImage( "Potential glyps", imageCopy ); int counter = 1; // further processing of each potential glyph foreach ( List<IntPoint> corners in foundObjects ) { log.AddMessage( "Glyph #" + counter ); log.AddMessage( string.Format( "Corners: ({0}), ({1}), ({2}), ({3})", corners[0], corners[1], corners[2], corners[3] ) ); // 6 - do quadrilateral transformation QuadrilateralTransformation quadrilateralTransformation = new QuadrilateralTransformation( corners, 250, 250 ); Bitmap transformed = quadrilateralTransformation.Apply( grayImage ); log.AddImage( "Transformed #" + counter, transformed ); // 7 - otsu thresholding OtsuThreshold otsuThresholdFilter = new OtsuThreshold( ); Bitmap transformedOtsu = otsuThresholdFilter.Apply( transformed ); log.AddImage( "Transformed Otsu #" + counter, transformedOtsu ); int glyphSize = 5; SquareBinaryGlyphRecognizer gr = new SquareBinaryGlyphRecognizer( glyphSize ); bool[,] glyphValues = gr.Recognize( transformedOtsu, new Rectangle( 0, 0, 250, 250 ) ); log.AddImage( "Glyph lines #" + counter, transformedOtsu ); // output recognize glyph to log log.AddMessage( string.Format( "glyph: {0:F2}%", gr.confidence * 100 ) ); for ( int i = 0; i < glyphSize; i++ ) { StringBuilder sb = new StringBuilder( " " ); for ( int j = 0; j < glyphSize; j++ ) { sb.Append( ( glyphValues[i, j] ) ? "1 " : "0 " ); } log.AddMessage( sb.ToString( ) ); } counter++; } }