public double[] detectionGlyph(bool CalculTailleTerrain) { bool Trouve = false; double[] ratio = new double[2] { 0, 0 }; SimpleShapeChecker shapeChecker = new SimpleShapeChecker(); BlobCounter blobCounter = new BlobCounter(); blobCounter.MinHeight = 23; blobCounter.MinWidth = 23; blobCounter.FilterBlobs = true; blobCounter.ObjectsOrder = ObjectsOrder.Size; // 4 - find all stand alone blobs blobCounter.ProcessImage(imgContour); Blob[] blobs = blobCounter.GetObjectsInformation(); // 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; // Test de la forme selectionnée if (shapeChecker.IsQuadrilateral(edgePoints, out corners)) { // Détection des points de coutour List<IntPoint> leftEdgePoints, rightEdgePoints, topEdgePoints, bottomEdgePoints; Line Horizontale = Line.FromPoints(new IntPoint(0,0),new IntPoint(10,0)); blobCounter.GetBlobsLeftAndRightEdges(blobs[i], out leftEdgePoints, out rightEdgePoints); blobCounter.GetBlobsTopAndBottomEdges(blobs[i], out topEdgePoints, out bottomEdgePoints); // calculate average difference between pixel values from outside of the // shape and from inside float diff = CalculateAverageEdgesBrightnessDifference(leftEdgePoints, rightEdgePoints, imgNB); // check average difference, which tells how much outside is lighter than // inside on the average if (diff > 20) { // Transformation de l'image reçu en un carré pour la reconnaissance QuadrilateralTransformation quadrilateralTransformation = new QuadrilateralTransformation(corners, 60, 60); UnmanagedImage glyphImage = quadrilateralTransformation.Apply(imgNB); // Filtre de contraste OtsuThreshold otsuThresholdFilter = new OtsuThreshold(); otsuThresholdFilter.ApplyInPlace(glyphImage); imgContour = glyphImage; // Reconnaissance du Glyph Glyph Gl = new Glyph(glyphImage, GlyphSize); Gl.ReconnaissanceGlyph(corners, imgNB); // Si le Glyph est valide if (Gl.getIdentifiant() > 0) { if (AutAffichage[0]) { // Coloration des contours des zones détectées UnImgReel.SetPixels(leftEdgePoints, Color.Red); UnImgReel.SetPixels(rightEdgePoints, Color.Red); UnImgReel.SetPixels(topEdgePoints, Color.Red); UnImgReel.SetPixels(bottomEdgePoints, Color.Red); } // Détection du milieu Line line = Line.FromPoints(corners[0], corners[2]); Line line2 = Line.FromPoints(corners[1], corners[3]); IntPoint intersection = (IntPoint)line.GetIntersectionWith(line2); if (AutAffichage[1]) { dessinePoint(intersection, UnImgReel, 4, Color.Yellow); } // Calcul de la rotation Line ComparasionAngle = Line.FromPoints(corners[0], corners[1]); Double rotation = (int) ComparasionAngle.GetAngleBetweenLines(Horizontale); rotation += 90 * Gl.getNbRotation(); Gl.rotation = 360 - rotation; rotation *= (Math.PI / 180.0); // Calcul d'un point en bout de pince float Taille = corners[0].DistanceTo(corners[1]); float taille = (Taille / BibliotequeGlyph.Biblioteque[Gl.getPosition()].taille) * BibliotequeGlyph.Biblioteque[Gl.getPosition()].DistancePince; int x = -(int)(System.Math.Sin(rotation) * taille); int y = -(int)(System.Math.Cos(rotation) * taille); x += (int)intersection.X; y += (int)intersection.Y; Gl.Position = new int[2]{x,y}; if (AutAffichage[2]) { dessinePoint(new IntPoint(x, y), UnImgReel, 4, Color.Cyan); } imgContour = Gl.getImage(); addGlyph(Gl); if (CalculTailleTerrain == true && Trouve == false) { Trouve = true; int tailleglyph = BibliotequeGlyph.Biblioteque[Gl.getPosition()].taille; // Pythagore pour detection taille Rectangle a = blobs[i].Rectangle; double angle = - Gl.rotation + 180; List<IntPoint> coins = new List<IntPoint>(); coins.Add(new IntPoint(100,100)); coins.Add(new IntPoint(100, 100 + tailleglyph)); coins.Add(new IntPoint(100 + tailleglyph , 100 + tailleglyph)); coins.Add(new IntPoint(100 + tailleglyph, 100)); IntPoint Centre = new IntPoint((coins[2].X + coins[0].X)/2, (coins[2].Y + coins[0].Y) / 2); int radius = (int)(0.5 * Math.Sqrt(coins[0].DistanceTo(coins[1]) * coins[0].DistanceTo(coins[1]) + coins[1].DistanceTo(coins[2]) * coins[1].DistanceTo(coins[2]))); double alpha = Math.Atan2(coins[0].DistanceTo(coins[1]), coins[1].DistanceTo(coins[2])) * (180 / Math.PI); double ang = 0; for(i = 0; i < 4; i++) { IntPoint tmp = coins[i]; switch (i) { case 0: ang = alpha - 180 + angle; break; case 1: ang = + angle - alpha; break; case 2: ang = + angle + alpha; break; case 3: ang = - alpha + 180 + angle; break; } ang *= (Math.PI / 180); tmp.X = (int)(Centre.X + radius * Math.Cos(ang)); tmp.Y = (int)(Centre.Y + radius * Math.Sin(ang)); coins[i] = tmp; } Rectangle r = new Rectangle(min(coins[0].X, coins[1].X, coins[2].X, coins[3].X), min(coins[0].Y, coins[1].Y, coins[2].Y, coins[3].Y), max(coins[0].X, coins[1].X, coins[2].X, coins[3].X) - min(coins[0].X, coins[1].X, coins[2].X, coins[3].X), max(coins[0].Y, coins[1].Y, coins[2].Y, coins[3].Y) - min(coins[0].Y, coins[1].Y, coins[2].Y, coins[3].Y)); ratio[0] = ((double)r.Width / (double)a.Width) * 1.48; ratio[1] = ((double)r.Height / (double)a.Height) * 1.48; } } } } } if (Trouve == false || ratio[0] == 0 || ratio[0] == 1 || ratio[1] == 0 || ratio[1] == 1) { return null; } ratio[0] *= 0.7; ratio[1] *= 0.7; return ratio; }
public MagicCard(Bitmap cameraBitmap, IEnumerable<IntPoint> corners) { this.CameraBitmap = cameraBitmap; QuadrilateralTransformation transformFilter = new QuadrilateralTransformation(corners.ToList(), 211, 298); this.CardBitmap = transformFilter.Apply(cameraBitmap); QuadrilateralTransformation cardArtFilter = new QuadrilateralTransformation(artCorners.ToList(), 183, 133); this.CardArtBitmap = cardArtFilter.Apply(this.CardBitmap); this.Corners = corners.ToList(); }
/// <summary> /// source code : modified and updated from https://www.codeproject.com/Articles/884518/Csharp-Optical-Marks-Recognition-OMR-Engine-a-Mar /// </summary> public static Bitmap ApplyWrap(List <AForge.IntPoint> quad, Bitmap originalIage, double scale, Pattern pattern) { //pattern witdh and height double ratio = Math.Max((double)originalIage.Width / (double)pattern.Width, (double)originalIage.Height / (double)pattern.Height); ratio *= scale; AForge.Imaging.Filters.QuadrilateralTransformation wrap = new AForge.Imaging.Filters.QuadrilateralTransformation(quad); wrap.UseInterpolation = false; Size sr = new Size() { Width = pattern.Width, Height = pattern.Height }; sr = new Size( (int)Math.Round((double)sr.Width * ratio), (int)Math.Round((double)sr.Height * ratio) ); wrap.AutomaticSizeCalculaton = false; wrap.NewWidth = sr.Width; wrap.NewHeight = sr.Height; return(wrap.Apply(originalIage)); // wrap }
public static Bitmap Clip(Bitmap source, List<IntPoint> corners) { #if NO // define quadrilateral's corners List<IntPoint> corners = new List<IntPoint>(); corners.Add(new IntPoint(99, 99)); corners.Add(new IntPoint(156, 79)); corners.Add(new IntPoint(184, 126)); corners.Add(new IntPoint(122, 150)); #endif int width = 0; int height = 0; GetWidthHeight(corners, out width, out height); // create filter QuadrilateralTransformation filter = new QuadrilateralTransformation(corners, width, height); // apply the filter return filter.Apply(source); }
private void detectQuads(Bitmap bitmap) { // Greyscale filteredBitmap = Grayscale.CommonAlgorithms.BT709.Apply(bitmap); // edge filter SobelEdgeDetector edgeFilter = new SobelEdgeDetector(); edgeFilter.ApplyInPlace(filteredBitmap); // Threshhold filter Threshold threshholdFilter = new Threshold(190); threshholdFilter.ApplyInPlace(filteredBitmap); BitmapData bitmapData = filteredBitmap.LockBits( new Rectangle(0, 0, filteredBitmap.Width, filteredBitmap.Height), ImageLockMode.ReadWrite, filteredBitmap.PixelFormat); BlobCounter blobCounter = new BlobCounter(); blobCounter.FilterBlobs = true; blobCounter.MinHeight = 125; blobCounter.MinWidth = 125; blobCounter.ProcessImage(bitmapData); Blob[] blobs = blobCounter.GetObjectsInformation(); filteredBitmap.UnlockBits(bitmapData); SimpleShapeChecker shapeChecker = new SimpleShapeChecker(); Bitmap bm = new Bitmap(filteredBitmap.Width, filteredBitmap.Height, PixelFormat.Format24bppRgb); Graphics g = Graphics.FromImage(bm); g.DrawImage(filteredBitmap, 0, 0); Pen pen = new Pen(Color.Red, 5); List<IntPoint> cardPositions = new List<IntPoint>(); // Loop through detected shapes for (int i = 0, n = blobs.Length; i < n; i++) { List<IntPoint> edgePoints = blobCounter.GetBlobsEdgePoints(blobs[i]); List<IntPoint> corners; bool sameCard = false; // is triangle or quadrilateral if (shapeChecker.IsConvexPolygon(edgePoints, out corners)) { // get sub-type PolygonSubType subType = shapeChecker.CheckPolygonSubType(corners); // Only return 4 corner rectanges if ((subType == PolygonSubType.Parallelogram || subType == PolygonSubType.Rectangle) && corners.Count == 4) { // Check if its sideways, if so rearrange the corners so it's veritcal rearrangeCorners(corners); // Prevent it from detecting the same card twice foreach (IntPoint point in cardPositions) { if (corners[0].DistanceTo(point) < 40) sameCard = true; } if (sameCard) continue; // Hack to prevent it from detecting smaller sections of the card instead of the whole card if (GetArea(corners) < 20000) continue; cardPositions.Add(corners[0]); g.DrawPolygon(pen, ToPointsArray(corners)); // Extract the card bitmap QuadrilateralTransformation transformFilter = new QuadrilateralTransformation(corners, 211, 298); cardBitmap = transformFilter.Apply(cameraBitmap); List<IntPoint> artCorners = new List<IntPoint>(); artCorners.Add(new IntPoint(14, 35)); artCorners.Add(new IntPoint(193, 35)); artCorners.Add(new IntPoint(193, 168)); artCorners.Add(new IntPoint(14, 168)); // Extract the art bitmap QuadrilateralTransformation cartArtFilter = new QuadrilateralTransformation(artCorners, 183, 133); cardArtBitmap = cartArtFilter.Apply(cardBitmap); MagicCard card = new MagicCard(); card.corners = corners; card.cardBitmap = cardBitmap; card.cardArtBitmap = cardArtBitmap; magicCards.Add(card); } } } pen.Dispose(); g.Dispose(); filteredBitmap = bm; }
public List<Object> findObjects(VideoReader videoReader, IDepthReader depthReader, Action<ushort[], CameraSpacePoint[]> mappingFunction, IProgress<int> progress) { var shapeOptimizer = new FlatAnglesOptimizer(160); Console.WriteLine("Find glyph box"); List<Object> objects = new List<Object>(); if (videoReader == null) return objects; /// For each frame (int frameNo) /// For each recognized glyph in frame (int faceIndex) /// Store A tuple of /// - A list of bounding points for recognized glyph /// - A glyphface instance var recognizedGlyphs = new Dictionary<int, Dictionary<int, Dictionary<int, Tuple<List<System.Drawing.PointF>, GlyphFace, List<Point3>>>>>(); Bitmap image = null; Mat m = null; Bitmap grayImage = null; Bitmap edges = null; UnmanagedImage grayUI = null; Bitmap transformed = null; Bitmap transformedOtsu = null; //A control flag, true if at the previous frame loop, there is detection of some glyph // When this is true, only searching for glyph box in some neighborhood of the previous glyphs // if the frame is not an anchor frame bool previousFrameDetection = false; for (int frameNo = 0; frameNo < videoReader.frameCount; frameNo++) //for (int frameNo = 80; frameNo < 81; frameNo++) { if (progress != null) progress.Report(frameNo); Console.WriteLine("============================================="); Console.WriteLine("Frame no " + frameNo); m = videoReader.getFrame(frameNo); if (m == null) { break; } var startPos = new System.Drawing.Point(); getImageForProcessing(recognizedGlyphs, m, previousFrameDetection, frameNo, ref image, ref startPos); // Reset right after using previousFrameDetection = false; Stopwatch stopwatch = Stopwatch.StartNew(); /// Adapt from Glyph Recognition Prototyping /// Copyright © Andrew Kirillov, 2009-2010 /// // 1 - Grayscale grayImage = Grayscale.CommonAlgorithms.BT709.Apply(image); stopwatch.Stop(); Console.WriteLine("Gray scale time = " + stopwatch.ElapsedMilliseconds); stopwatch.Restart(); // 2 - Edge detection DifferenceEdgeDetector edgeDetector = new DifferenceEdgeDetector(); edges = edgeDetector.Apply(grayImage); stopwatch.Stop(); Console.WriteLine("Edge detection time = " + stopwatch.ElapsedMilliseconds); stopwatch.Restart(); // 3 - Threshold edges // Was set to 20 and the number of detected glyphs are too low // Should be set higher Threshold thresholdFilter = new Threshold(60); thresholdFilter.ApplyInPlace(edges); stopwatch.Stop(); Console.WriteLine("Threshold time = " + stopwatch.ElapsedMilliseconds); stopwatch.Restart(); // 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(); stopwatch.Stop(); Console.WriteLine("Blob finding time = " + stopwatch.ElapsedMilliseconds); stopwatch.Restart(); //// create unmanaged copy of source image, so we could draw on it //UnmanagedImage imageData = UnmanagedImage.FromManagedImage(image); // Get unmanaged copy of grayscale image, so we could access it's pixel values grayUI = UnmanagedImage.FromManagedImage(grayImage); // 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(); Console.WriteLine("edgePoints"); // 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 ) ) { // 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 = this.CalculateAverageEdgesBrightnessDifference( leftEdgePoints, rightEdgePoints, grayUI); // check average difference, which tells how much outside is lighter than inside on the average if (diff > 20) { //Drawing.Polygon(imageData, corners, Color.FromArgb(255, 255, 0, 0)); // add the object to the list of interesting objects for further processing foundObjects.Add(corners); } } } } stopwatch.Stop(); Console.WriteLine("Finding black quadiralateral surrounded by white area = " + stopwatch.ElapsedMilliseconds); stopwatch.Restart(); int recordedTimeForRgbFrame = (int)(videoReader.totalMiliTime * frameNo / (videoReader.frameCount - 1)); CameraSpacePoint[] csps = new CameraSpacePoint[videoReader.frameWidth * videoReader.frameHeight]; if (depthReader != null) { ushort[] depthValues = depthReader.readFrameAtTime(recordedTimeForRgbFrame); mappingFunction(depthValues, csps); } stopwatch.Stop(); Console.WriteLine("Mapping into 3 dimensional = " + stopwatch.ElapsedMilliseconds); stopwatch.Restart(); // further processing of each potential glyph foreach (List<IntPoint> corners in foundObjects) { Console.WriteLine("found some corner"); // 6 - do quadrilateral transformation QuadrilateralTransformation quadrilateralTransformation = new QuadrilateralTransformation(corners, 20 * (glyphSize + 2), 20 * (glyphSize + 2)); transformed = quadrilateralTransformation.Apply(grayImage); // 7 - otsu thresholding OtsuThreshold otsuThresholdFilter = new OtsuThreshold(); transformedOtsu = otsuThresholdFilter.Apply(transformed); // +2 for offset int glyphSizeWithBoundary = glyphSize + 2; SquareBinaryGlyphRecognizer gr = new SquareBinaryGlyphRecognizer(glyphSizeWithBoundary); bool[,] glyphValues = gr.Recognize(ref transformedOtsu, new Rectangle(0, 0, 20 * (glyphSize + 2), 20 * (glyphSize + 2))); bool[,] resizedGlyphValues = new bool[glyphSize, glyphSize]; for (int i = 0; i < glyphSize; i++) for (int j = 0; j < glyphSize; j++) { resizedGlyphValues[i, j] = glyphValues[i + 1, j + 1]; } GlyphFace face = new GlyphFace(resizedGlyphValues, glyphSize); Console.WriteLine("Find glyph face " + face.ToString()); // Transfer back to original coordinates List<IntPoint> originalCorners = new List<IntPoint>(); foreach (var corner in corners) { IntPoint p = new IntPoint(corner.X + startPos.X, corner.Y + startPos.Y); originalCorners.Add(p); } Console.WriteLine("Corner points"); foreach (var corner in originalCorners) { Console.WriteLine(corner); } for (int boxPrototypeIndex = 0; boxPrototypeIndex < boxPrototypes.Count; boxPrototypeIndex++) { var boxPrototype = boxPrototypes[boxPrototypeIndex]; foreach (int faceIndex in boxPrototype.indexToGlyphFaces.Keys) { if (face.Equals(boxPrototype.indexToGlyphFaces[faceIndex])) { if (!recognizedGlyphs.ContainsKey(boxPrototypeIndex)) { Console.WriteLine("Detect new type of prototype " + boxPrototypeIndex); recognizedGlyphs[boxPrototypeIndex] = new Dictionary<int, Dictionary<int, Tuple<List<System.Drawing.PointF>, GlyphFace, List<Point3>>>>(); } if (!recognizedGlyphs[boxPrototypeIndex].ContainsKey(frameNo)) { Console.WriteLine("Detect glyph at frame " + frameNo + " for prototype " + boxPrototypeIndex); if (!previousFrameDetection) { previousFrameDetection = true; } recognizedGlyphs[boxPrototypeIndex][frameNo] = new Dictionary<int, Tuple<List<System.Drawing.PointF>, GlyphFace, List<Point3>>>(); } recognizedGlyphs[boxPrototypeIndex][frameNo][faceIndex] = new Tuple<List<System.Drawing.PointF>, GlyphFace, List<Point3>>( originalCorners.Select(p => new System.Drawing.PointF(p.X, p.Y)).ToList(), face, depthReader != null ? originalCorners.Select(p => p.X + p.Y * videoReader.frameWidth >= 0 && p.X + p.Y * videoReader.frameWidth < videoReader.frameWidth * videoReader.frameHeight ? new Point3(csps[p.X + p.Y * videoReader.frameWidth].X, csps[p.X + p.Y * videoReader.frameWidth].Y, csps[p.X + p.Y * videoReader.frameWidth].Z) : new Point3()).ToList() : new List<Point3>() ); break; } } } } foreach (IDisposable o in new IDisposable[] { image, m, grayImage, edges, grayUI, transformed, transformedOtsu }) { if (o != null) { o.Dispose(); } } stopwatch.Stop(); Console.WriteLine("Transforming and detect glyph = " + stopwatch.ElapsedMilliseconds); stopwatch.Restart(); } if (progress != null) progress.Report(videoReader.frameCount); if (recognizedGlyphs.Keys.Count != 0) { foreach (int boxPrototypeIndex in recognizedGlyphs.Keys) { Console.WriteLine("For boxPrototypeIndex = " + boxPrototypeIndex + " Found glyph box at " + recognizedGlyphs[boxPrototypeIndex].Keys.Count + " frames"); GlyphBoxObject oneBox = null; var boxPrototype = boxPrototypes[boxPrototypeIndex]; oneBox = new GlyphBoxObject(currentSession, "", Color.Black, 1, videoReader.fileName); oneBox.boxPrototype = boxPrototype; foreach (int frameNo in recognizedGlyphs[boxPrototypeIndex].Keys) { var glyphs = recognizedGlyphs[boxPrototypeIndex][frameNo]; var glyphBounds = new List<List<System.Drawing.PointF>>(); var glyph3DBounds = new List<List<Point3>>(); var faces = new List<GlyphFace>(); foreach (var glyph in glyphs) { glyphBounds.Add(glyph.Value.Item1); faces.Add(glyph.Value.Item2); glyph3DBounds.Add(glyph.Value.Item3); } oneBox.setBounding(frameNo, glyphSize, glyphBounds, faces); oneBox.set3DBounding(frameNo, glyphSize, glyph3DBounds, faces); //Point3 center = new Point3(); //Quaternions quaternions = new Quaternions(); //oneBox.set3DBounding(frameNo, new CubeLocationMark(frameNo, center, quaternions)); } objects.Add(oneBox); } } return objects; }
/// <summary> /// Detects and recognizes cards from source image /// </summary> /// <param name="source">Source image to be scanned</param> /// <returns>Recognized Cards</returns> public CardCollection Recognize(Bitmap source) { CardCollection collection = new CardCollection(); //Collection that will hold cards Bitmap temp = source.Clone() as Bitmap; //Clone image to keep original image FiltersSequence seq = new FiltersSequence(); seq.Add(Grayscale.CommonAlgorithms.BT709); //First add grayScaling filter seq.Add(new OtsuThreshold()); //Then add binarization(thresholding) filter temp = seq.Apply(source); // Apply filters on source image //Extract blobs from image whose size width and height larger than 150 BlobCounter extractor = new BlobCounter(); extractor.FilterBlobs = true; extractor.MinWidth = extractor.MinHeight = 150; extractor.MaxWidth = extractor.MaxHeight = 350; extractor.ProcessImage(temp); //Will be used transform(extract) cards on source image QuadrilateralTransformation quadTransformer = new QuadrilateralTransformation(); //Will be used resize(scaling) cards ResizeBilinear resizer = new ResizeBilinear(CardWidth, CardHeight); foreach (Blob blob in extractor.GetObjectsInformation()) { //Get Edge points of card List<IntPoint> edgePoints = extractor.GetBlobsEdgePoints(blob); //Calculate/Find corners of card on source image from edge points List<IntPoint> corners = PointsCloud.FindQuadrilateralCorners(edgePoints); quadTransformer.SourceQuadrilateral = corners; //Set corners for transforming card quadTransformer.AutomaticSizeCalculaton = true; Bitmap cardImg = quadTransformer.Apply(source); //Extract(transform) card image if (cardImg.Width > cardImg.Height) //If card is positioned horizontally cardImg.RotateFlip(RotateFlipType.Rotate90FlipNone); //Rotate cardImg = resizer.Apply(cardImg); //Normalize card size Card card = new Card(cardImg, corners.ToArray()); //Create Card Object bool faceCard = IsFaceCard(cardImg); //Determine type of card(face or not) ResizeBicubic res; seq.Clear(); seq.Add(Grayscale.CommonAlgorithms.BT709); seq.Add(new OtsuThreshold()); Bitmap topLeftSuit = card.GetTopLeftSuitPart(); Bitmap bmp = seq.Apply(topLeftSuit); bmp = CutWhiteSpaces(bmp); res = new ResizeBicubic(32, 40); bmp = res.Apply(bmp); Bitmap topLeftRank = card.GetTopLeftRankPart(); Bitmap bmp2 = seq.Apply(topLeftRank); bmp2 = CutWhiteSpaces(bmp2); seq.Clear(); seq.Add(new OtsuThreshold()); bmp = seq.Apply(bmp); card.Suit = ScanSuit(bmp); if (!faceCard) { res = new ResizeBicubic(26, 40); bmp2 = res.Apply(bmp2); seq.Clear(); seq.Add(new OtsuThreshold()); bmp2 = seq.Apply(bmp2); card.Rank = ScanRank(bmp2); } else { res = new ResizeBicubic(32, 40); bmp2 = res.Apply(bmp2); seq.Clear(); seq.Add(new OtsuThreshold()); bmp2 = seq.Apply(bmp2); card.Rank = ScanFaceRank(bmp2); } collection.Add(card); //Add card to collection } return collection; }
/// <summary> /// Detects and recognizes cards from source image /// </summary> /// <param name="source">Source image to be scanned</param> /// <returns>Recognized Cards</returns> public List<Card> Recognize(Bitmap source) { List<Card> collection = new List<Card>(); Bitmap temp = source.Clone(source.PixelFormat) as Bitmap; //Clone image to keep original image FiltersSequence seq = new FiltersSequence(); seq.Add(Grayscale.CommonAlgorithms.BT709); //First add grayScaling filter seq.Add(new OtsuThreshold()); //Then add binarization(thresholding) filter temp = seq.Apply(source); // Apply filters on source image //Extract blobs from image whose size width and height larger than 150 BlobCounter extractor = new BlobCounter(); extractor.FilterBlobs = true; extractor.MinWidth = extractor.MinHeight = 150; extractor.MaxWidth = extractor.MaxHeight = 350; extractor.ProcessImage(temp); //Will be used transform(extract) cards on source image QuadrilateralTransformation quadTransformer = new QuadrilateralTransformation(); //Will be used resize(scaling) cards ResizeBilinear resizer = new ResizeBilinear(CardWidth, CardHeight); foreach (Blob blob in extractor.GetObjectsInformation()) { //Get Edge points of card List<IntPoint> edgePoints = extractor.GetBlobsEdgePoints(blob); //Calculate/Find corners of card on source image from edge points List<IntPoint> corners = PointsCloud.FindQuadrilateralCorners(edgePoints); quadTransformer.SourceQuadrilateral = corners; //Set corners for transforming card quadTransformer.AutomaticSizeCalculaton = true; Bitmap cardImg = quadTransformer.Apply(source); //Extract(transform) card image if (cardImg.Width > cardImg.Height) //If card is positioned horizontally { WriteableBitmap wbmp=(WriteableBitmap)cardImg; wbmp = wbmp.Rotate(90); cardImg = (Bitmap)wbmp; //Rotate } cardImg = resizer.Apply(cardImg); //Normalize card size Card card = new Card(cardImg, corners.ToArray()); //Create Card Object char color = ScanColor(card.GetTopLeftPart()); //Scan color bool faceCard = IsFaceCard(cardImg); //Determine type of card(face or not) if (!faceCard) { card.Suit = ScanSuit(cardImg, color); //Scan Suit of non-face card card.Rank = ScanRank(cardImg); //Scan Rank of non-face card } else { Bitmap topLeft = card.GetTopLeftPart(); seq = null; seq = new FiltersSequence(); seq.Add(Grayscale.CommonAlgorithms.BT709); seq.Add(new BradleyLocalThresholding()); topLeft = seq.Apply(topLeft); BlobsFiltering bFilter = new BlobsFiltering(5, 5, 150, 150); bFilter.ApplyInPlace(topLeft); //Filter blobs that can not be a suit //topLeft.Save("topleft.bmp", ImageFormat.Bmp); card.Suit = ScanFaceSuit(topLeft, color); //Scan suit of face card card.Rank = ScanFaceRank(topLeft); //Scan rank of face card } collection.Add(card); //Add card to collection } return collection; }
public void PerspectiveTransform(string path) { List<Rectangle> minX = outerMarker.OrderBy(r => r.X).Take(2).ToList(); List<Rectangle> maxX = outerMarker.OrderBy(r => r.X).Reverse().Take(2).ToList(); Rectangle topLeft = minX.OrderBy(r => r.Y).First(); Rectangle bottomLeft = minX.OrderBy(r => r.Y).Reverse().First(); Rectangle topRight = maxX.OrderBy(r => r.Y).First(); Rectangle bottomRight = maxX.OrderBy(r => r.Y).Reverse().First(); List<AForge.IntPoint> corners = new List<AForge.IntPoint>(); corners.Add(new AForge.IntPoint( topLeft.X, topLeft.Y)); corners.Add(new AForge.IntPoint(topRight.X + topRight.Width , topRight.Y )); corners.Add(new AForge.IntPoint(bottomRight.X + bottomRight.Width , bottomRight.Y + bottomRight.Height)); corners.Add(new AForge.IntPoint(bottomLeft.X, bottomLeft.Y + bottomRight.Height )); QuadrilateralTransformation filter = new QuadrilateralTransformation(corners, 1000, 1375); Bitmap newImage = filter.Apply(originalImage.ToBitmap()); newImage.Save(path); }
// 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++; } }
private Bitmap ExtractImage(Bitmap bitmap, Bitmap originalImg, int fillint, int contint) { int bmpWidth = bitmap.Width; int bmpHeight = bitmap.Height; // lock image, Bitmap itself takes much time to be processed BitmapData bitmapData = bitmap.LockBits( new Rectangle(0, 0, bmpWidth, bmpHeight), ImageLockMode.ReadWrite, bitmap.PixelFormat); // Store sheet corner locations (if anyone is detected) List<IntPoint> quad = new List<IntPoint>(); try { // step 2 - locating objects BlobCounter blobCounter = new BlobCounter(); blobCounter.FilterBlobs = true; blobCounter.MinHeight = 25; // both these variables have to be given when calling the blobCounter.MinWidth = 25; // method, the can also be queried from the XML reader using OMREnums UnmanagedImage unmanagedImage = new UnmanagedImage(bitmapData); blobCounter.ProcessImage(unmanagedImage); AForge.Imaging.Blob[] blobs = blobCounter.GetObjects(unmanagedImage, false); // this helps filtering out much smaller and much larger blobs depending upon the size of image. double minbr = 0.0001; double maxbr = 0.005; // Detect left edge. foreach (AForge.Imaging.Blob blob in blobs) { // filters out very small or very larg blobs if (((double)blob.Area) / ((double)bmpWidth * bmpHeight) > minbr && ((double)blob.Area) / ((double)bmpWidth * bmpHeight) < maxbr && blob.Rectangle.X < (bmpWidth) / 4) { // filters out blobs having insanely wrong aspect ratio if ((double)blob.Rectangle.Width / blob.Rectangle.Height < 1.4 && (double)blob.Rectangle.Width / blob.Rectangle.Height > 0.6) { using (Bitmap leftBoundingResized = ResizeImage(this.leftBoundingImg, blob.Rectangle.Width, blob.Rectangle.Height)) { if (isSame(blob.Image, leftBoundingResized)) { quad.Add(new IntPoint((int)blob.CenterOfGravity.X, (int)blob.CenterOfGravity.Y)); } } } } } // Sort out the list in right sequence, UpperLeft, LowerLeft, LowerRight, UpperRight if (quad.Count >= 2) { if (quad[0].Y > quad[1].Y) { IntPoint tp = quad[0]; quad[0] = quad[1]; quad[1] = tp; } } // Detect right edge. foreach (AForge.Imaging.Blob blob in blobs) { if ( ((double)blob.Area) / ((double)bmpWidth * bmpHeight) > minbr && ((double)blob.Area) / ((double)bmpWidth * bmpHeight) < maxbr && blob.Rectangle.X > (bmpWidth * 3) / 4) { if ((double)blob.Rectangle.Width / blob.Rectangle.Height < 1.4 && (double)blob.Rectangle.Width / blob.Rectangle.Height > 0.6) { using (Bitmap rightBoundingResized = ResizeImage(this.rightBoundingImg, blob.Rectangle.Width, blob.Rectangle.Height)) { if (isSame(blob.Image, rightBoundingResized)) { quad.Add(new IntPoint((int)blob.CenterOfGravity.X, (int)blob.CenterOfGravity.Y)); } } } } } if (quad.Count >= 4) { if (quad[2].Y < quad[3].Y) { IntPoint tp = quad[2]; quad[2] = quad[3]; quad[3] = tp; } } } finally { bitmap.UnlockBits(bitmapData); } //Again, filter out if wrong blobs pretended to our blobs. if (quad.Count == 4) { // clear if, both edges have insanely wrong lengths if (((double)quad[1].Y - (double)quad[0].Y) / ((double)quad[2].Y - (double)quad[3].Y) < 0.75 || ((double)quad[1].Y - (double)quad[0].Y) / ((double)quad[2].Y - (double)quad[3].Y) > 1.25) { quad.Clear(); } // clear if, sides appear to be "wrong sided" else if (quad[0].X > bmpWidth / 2 || quad[1].X > bmpWidth / 2 || quad[2].X < bmpWidth / 2 || quad[3].X < bmpWidth / 2) { quad.Clear(); } } // sheet found if (quad.Count == 4) { IntPoint tp2 = quad[3]; quad[3] = quad[1]; quad[1] = tp2; //sort the edges for wrap operation QuadrilateralTransformation wrap = new QuadrilateralTransformation(quad); wrap.UseInterpolation = false; //perspective wrap only, no binary. wrap.AutomaticSizeCalculaton = false; wrap.NewWidth = this.ОMRConfig.ImageWidth; wrap.NewHeight = this.ОMRConfig.ImageHeight; return wrap.Apply(originalImg); } else { return null; } }
private void drawRowsAndColsOnCurrentWordsearch() { //If there is a currently loaded bitmap to draw on if(bitmapToShow != null) { //Get Rows & Cols uint rows, cols; uint.TryParse(txtNumRows.Text, out rows); uint.TryParse(txtNumCols.Text, out cols); //Check we have all four coordinates of the wordsearch image if (txtTopLeftX.Text != "" && txtTopLeftY.Text != "" && txtTopRightX.Text != "" && txtTopRightY.Text != "" && txtBottomRightX.Text != "" && txtBottomRightY.Text != "" && txtBottomLeftX.Text != "" && txtBottomLeftY.Text != "") { List<IntPoint> corners = new List<IntPoint>(4); try { corners.Add(getTopLeftCoordinate()); corners.Add(getTopRightCoordinate()); corners.Add(getBottomRightCoordinate()); corners.Add(getBottomLeftCoordinate()); } catch (FormatException) { //There was an error parsing one of the coordinates MessageBox.Show("Unable to parse coordinates as integers, please check they are valid"); return; } //Draw the bounding box onto the main image Bitmap drawnOn = bitmapToShow.DeepCopy(); DrawShapes.PolygonInPlace(drawnOn, corners, Color.Red); //If there is a Bitmap which will become unused in memory, dispose of it if(picBoxImage.Image != bitmapToShow) { picBoxImage.Image.Dispose(); } picBoxImage.Image = drawnOn; //Extract the wordsearch image & draw the grid on it QuadrilateralTransformation quadTransform = new QuadrilateralTransformation(corners, picBoxWordsearchImage.Width, picBoxWordsearchImage.Height); Bitmap transformed = quadTransform.Apply(currentBitmap.Key); DrawGrid.GridInPlace(transformed, (int)rows, (int)cols); //If there is a Bitmap which will become unused in memory, dispose if it if(picBoxWordsearchImage.Image != null) { picBoxWordsearchImage.Image.Dispose(); } picBoxWordsearchImage.Image = transformed; //If we're currently showing the picture box image in the larger display, show it there too if(picBoxWordsearchImageLarge.Visible) { picBoxWordsearchImageLarge.Image = transformed; } } } }
//Method to extract a Bitmap of the best match for a wordsearch in an image using a specified Wordsearch Segmentation Algorithm //returns null if no Wordsearch candidate could be found in the image public static Tuple<List<IntPoint>, Bitmap> ExtractBestWordsearch(Bitmap image, SegmentationAlgorithm segAlg, bool removeSmallRowsAndCols) { double blobMinDimensionDbl = BLOB_MIN_DIMENSION_PERCENTAGE / 100; int minWidth = (int)Math.Ceiling(image.Width * blobMinDimensionDbl); //Round up, so that the integer comaprison minimum will always be correct int minHeight = (int)Math.Ceiling(image.Height * blobMinDimensionDbl); List<List<IntPoint>> quads = ShapeFinder.Quadrilaterals(image, minWidth, minHeight); //Check that there are some quads found to search through for the best wordsearch candidate if(quads.Count != 0) { double bestScore = double.NegativeInfinity; List<IntPoint> bestCoords = null; Bitmap bestBitmap = null; //Search for the Bitmap that yields the best score foreach (List<IntPoint> quad in quads) { //Extract the Bitmap of this quad QuadrilateralTransformation quadTransform = new QuadrilateralTransformation(quad); Bitmap quadBitmap = quadTransform.Apply(image); //Score this wordsearch candidate double score; //If an InvalidRowsAndCols Exception gets throw by the segmentation // (due to there being 0 rows/cols in the returned segmentation) // this is obviously not a good wordsearch candidate try { Segmentation segmentation = segAlg.Segment(quadBitmap); //If removing erroneously small rows and cols before scoring the segmentation, do so now if(removeSmallRowsAndCols) { segmentation = segmentation.RemoveSmallRowsAndCols(); } CandidateScorer scorer = new CandidateScorer(segmentation); score = scorer.WordsearchRecognitionScore; } catch(InvalidRowsAndColsException) { //This is slightly better than the default score of Negative Infinity as any candidate // (even one with no rows or cols found in it) is better than no candidate whatsoever score = double.MinValue; } //If this score is better than the previous best (don't // override equal scores as the list is size ordered and // we'll default to the biggest wordsearch as being better) if(score > bestScore) { bestScore = score; bestCoords = quad; //Dispose of the previously best Bitmap resource if(bestBitmap != null) { bestBitmap.Dispose(); } //Update the ptr to the new one bestBitmap = quadBitmap; } else { //Clean up quadBitmap.Dispose(); } } return Tuple.Create(bestCoords, bestBitmap); } else //Otherwise there are no quads to search through { return null; } }
public void QuadrilateralSlotsTransformation(Point[] curvePoints, Bitmap[] bmParkingSlots, Bitmap bmBackgound, int i) { List<AForge.IntPoint> corners = new List<AForge.IntPoint>(); corners.Add(new AForge.IntPoint(curvePoints[3 + (4 * i)].X, curvePoints[3 + (4 * i)].Y)); corners.Add(new AForge.IntPoint(curvePoints[0 + (4 * i)].X, curvePoints[0 + (4 * i)].Y)); corners.Add(new AForge.IntPoint(curvePoints[1 + (4 * i)].X, curvePoints[1 + (4 * i)].Y)); corners.Add(new AForge.IntPoint(curvePoints[2 + (4 * i)].X, curvePoints[2 + (4 * i)].Y)); QuadrilateralTransformation filter = new QuadrilateralTransformation(corners, 200, 200); bmParkingSlots[i] = filter.Apply(bmBackgound); }
private Bitmap Transform(List<IntPoint> corners, Bitmap image) { // otestovat zamenu za SimpleQuadrilateralTransformation - mela by byt rychlejsi QuadrilateralTransformation filter = new QuadrilateralTransformation(corners); Bitmap newImage = filter.Apply(image); // zpetna rotace kvuli transformaci do puvodniho tvaru if (newImage.Height > newImage.Width) { newImage.RotateFlip(RotateFlipType.Rotate270FlipNone); } newImage = this.Resize(newImage); return this.Threshold(newImage, _thresholdLevel); }
// Transform quadrilateral private void quadrilateralTransformationMenuItem_Click( object sender, EventArgs e ) { // get corners of the quadrilateral QuadrilateralFinder qf = new QuadrilateralFinder( ); List<IntPoint> corners = qf.ProcessImage( image ); // create filter QuadrilateralTransformation filter = new QuadrilateralTransformation( corners ); ApplyFilter( filter ); }
/// <summary> /// Scans dominant color on image and returns it. /// Crops rank part on image and analyzes suit part on image /// </summary> /// <param name="bmp">Bitmap to be scanned</param> /// <returns>Returns 'B' for black , 'R' for red</returns> private char ScanColor(Bitmap bmp) { //System.Diagnostics.Debug.Flush(); //System.Diagnostics.Debug.Print("I'm here"); char color = 'B'; //Crop rank part Crop crop = new Crop(new Rectangle(0, bmp.Height / 2, bmp.Width, bmp.Height / 2)); bmp = crop.Apply(bmp); Bitmap temp = commonSeq.Apply(bmp); //Apply filters //Find suit blob on image BlobCounter counter = new BlobCounter(); counter.ProcessImage(temp); Blob[] blobs = counter.GetObjectsInformation(); if (blobs.Length > 0) //If blobs found { Blob max = blobs[0]; //Find blob whose size is biggest foreach (Blob blob in blobs) { if (blob.Rectangle.Height > max.Rectangle.Height) max = blob; else if (blob.Rectangle.Height == max.Rectangle.Height) max = blob.Rectangle.Width > max.Rectangle.Width ? blob : max; } QuadrilateralTransformation trans = new QuadrilateralTransformation(); trans.SourceQuadrilateral = PointsCloud.FindQuadrilateralCorners(counter.GetBlobsEdgePoints(max)); bmp = trans.Apply(bmp); //Extract suit } //Lock Bits for processing //int bitsPerPixel = ((int)bmp.PixelFormat & 0xff00) >> 8; //int bytesPerPixel = (bitsPerPixel + 7) / 8; //int stride = 4 * ((bmp.Width * bytesPerPixel + 3) / 4); // BitmapData imageData=new BitmapData(bmp.Width, bmp.Height, bmp. ) // BitmapData imageData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),ImageLockMode.ReadOnly, bmp.PixelFormat); int totalRed = 0; int totalBlack = 0; WriteableBitmap wbmp = (WriteableBitmap)bmp; wbmp.ForEach((x, y, pcolor) => { int r=Convert.ToInt32(pcolor.R); int g=Convert.ToInt32(pcolor.G); int b=Convert.ToInt32(pcolor.B); if (r > g + b) totalRed++; else if (r <= g + b && (r < 50 && g < 50)) totalBlack++; return pcolor; }); if (totalRed > totalBlack) //If red is dominant color = 'R'; //Set color as Red return color; }
public void DetectQuads() { // Greyscale FilteredBitmap = Grayscale.CommonAlgorithms.BT709.Apply(CameraBitmap); // edge filter var edgeFilter = new SobelEdgeDetector(); edgeFilter.ApplyInPlace(FilteredBitmap); // Threshhold filter var threshholdFilter = new Threshold(190); threshholdFilter.ApplyInPlace(FilteredBitmap); var bitmapData = FilteredBitmap.LockBits( new Rectangle(0, 0, FilteredBitmap.Width, FilteredBitmap.Height), ImageLockMode.ReadWrite, FilteredBitmap.PixelFormat); var blobCounter = new BlobCounter { FilterBlobs = true, MinHeight = 125, MinWidth = 125 }; blobCounter.ProcessImage(bitmapData); var blobs = blobCounter.GetObjectsInformation(); FilteredBitmap.UnlockBits(bitmapData); var shapeChecker = new SimpleShapeChecker(); var bm = new Bitmap(FilteredBitmap.Width, FilteredBitmap.Height, PixelFormat.Format24bppRgb); var g = Graphics.FromImage(bm); g.DrawImage(FilteredBitmap, 0, 0); var pen = new Pen((Color)new ColorConverter().ConvertFromString(Application.Current.Resources["AccentColor"].ToString()), 5); var cardPositions = new List<IntPoint>(); // Loop through detected shapes for (int i = 0, n = blobs.Length; i < n; i++) { var edgePoints = blobCounter.GetBlobsEdgePoints(blobs[i]); List<IntPoint> corners; var sameCard = false; // is triangle or quadrilateral if (shapeChecker.IsQuadrilateral(edgePoints, out corners)) { if (!corners.Any()) return; // get sub-type var subType = shapeChecker.CheckPolygonSubType(corners); // Only return 4 corner rectanges if ((subType != PolygonSubType.Parallelogram && subType != PolygonSubType.Rectangle) || corners.Count != 4) continue; // Check if its sideways, if so rearrange the corners so it's veritcal RearrangeCorners(corners); // Prevent it from detecting the same card twice foreach (var point in cardPositions) { var distance = corners[0].DistanceTo(point); if (corners[0].DistanceTo(point) < 40) sameCard = true; } if (sameCard) continue; // Hack to prevent it from detecting smaller sections of the card instead of the whole card var area = GetArea(corners); if (area < 20000)// || area > 35000) continue; cardPositions.Add(corners[0]); g.DrawPolygon(pen, ToPointsArray(corners)); // Extract the card bitmap var transformFilter = new QuadrilateralTransformation(corners, 225, 325); CardBitmap = transformFilter.Apply(CameraBitmap); TmpCard = new MagicCard { Corners = corners, CardBitmap = CardBitmap }; } } pen.Dispose(); g.Dispose(); FilteredBitmap = bm; }
/// <summary> /// Scans dominant color on image and returns it. /// Crops rank part on image and analyzes suit part on image /// </summary> /// <param name="bmp">Bitmap to be scanned</param> /// <returns>Returns 'B' for black , 'R' for red</returns> private char ScanColor(Bitmap bmp) { char color = 'B'; //Crop rank part Crop crop = new Crop(new Rectangle(0, bmp.Height / 2, bmp.Width, bmp.Height / 2)); bmp = crop.Apply(bmp); Bitmap temp = commonSeq.Apply(bmp); //Apply filters //Find suit blob on image BlobCounter counter = new BlobCounter(); counter.ProcessImage(temp); Blob[] blobs = counter.GetObjectsInformation(); if (blobs.Length > 0) //If blobs found { Blob max = blobs[0]; //Find blob whose size is biggest foreach (Blob blob in blobs) { if (blob.Rectangle.Height > max.Rectangle.Height) max = blob; else if (blob.Rectangle.Height == max.Rectangle.Height) max = blob.Rectangle.Width > max.Rectangle.Width ? blob : max; } QuadrilateralTransformation trans = new QuadrilateralTransformation(); trans.SourceQuadrilateral = PointsCloud.FindQuadrilateralCorners(counter.GetBlobsEdgePoints(max)); bmp = trans.Apply(bmp); //Extract suit } //Lock Bits for processing BitmapData imageData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, bmp.PixelFormat); int totalRed = 0; int totalBlack = 0; unsafe { //Count red and black pixels try { UnmanagedImage img = new UnmanagedImage(imageData); int height = img.Height; int width = img.Width; int pixelSize = (img.PixelFormat == PixelFormat.Format24bppRgb) ? 3 : 4; byte* p = (byte*)img.ImageData.ToPointer(); // for each line for (int y = 0; y < height; y++) { // for each pixel for (int x = 0; x < width; x++, p += pixelSize) { int r = (int)p[RGB.R]; //Red pixel value int g = (int)p[RGB.G]; //Green pixel value int b = (int)p[RGB.B]; //Blue pixel value if (r > g + b) //If red component is bigger then total of green and blue component totalRed++; //then its red if (r <= g + b && r < 50 && g < 50 && b < 50) //If all components less 50 totalBlack++; //then its black } } } finally { bmp.UnlockBits(imageData); //Unlock } } if (totalRed > totalBlack) //If red is dominant color = 'R'; //Set color as Red return color; }
// New frame received by the player private void videoSourcePlayer_NewFrame( object sender, ref Bitmap image ) { BitmapData imageData = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, image.PixelFormat); try { UnmanagedImage unmanagedImg = new UnmanagedImage( imageData ); int x = (int)(unmanagedImg.Width / 3.19); // 443 int y = (int)(unmanagedImg.Height / 2.2); // 490 int w = (int)(unmanagedImg.Width / 2.7); // 553 int h = (int)(unmanagedImg.Height / 4); // 270 int s = (int)(unmanagedImg.Width / 10.2); // 141 // Crop the player scroll window. Speeds up the next couple operations Crop onePlayer = new Crop(new Rectangle(x, y, w, h)); UnmanagedImage img = onePlayer.Apply(unmanagedImg); // Use a quadrilateral transformation to make the scroller a big square. List<IntPoint> corners = new List<IntPoint>(); corners.Add(new IntPoint(s, 0)); corners.Add(new IntPoint(img.Width - s, 0)); ; corners.Add(new IntPoint(img.Width, img.Height)); corners.Add(new IntPoint(0, img.Height)); QuadrilateralTransformation filter = new QuadrilateralTransformation(corners, img.Width, img.Height); img = filter.Apply(img); // Crop the bottom half since it appears to have the best imagery Crop bottom = new Crop(new Rectangle(0, img.Height / 2, img.Width, img.Height / 2)); img = bottom.Apply(img); UnmanagedImage grayImg = UnmanagedImage.Create(img.Width, img.Height, PixelFormat.Format8bppIndexed); Grayscale.CommonAlgorithms.BT709.Apply(img, grayImg); OtsuThreshold threshold = new OtsuThreshold(); threshold.ApplyInPlace(grayImg); // Divide the square into 5 peices. One for each color. UnmanagedImage[] colorImg = new UnmanagedImage[5]; for(int i=0; i < 5; i++) { int colorWidth = grayImg.Width / 5; int colorHeight = grayImg.Height; Crop colorCrop = new Crop(new Rectangle(colorWidth * i, 0, colorWidth, colorHeight)); colorImg[i] = colorCrop.Apply(grayImg); } greenCol.Image = colorImg[GREEN].ToManagedImage(); redCol.Image = colorImg[RED].ToManagedImage(); yellowCol.Image = colorImg[YELLOW].ToManagedImage(); blueCol.Image = colorImg[BLUE].ToManagedImage(); orangeCol.Image = colorImg[ORANGE].ToManagedImage(); VerticalIntensityStatistics[] hist = new VerticalIntensityStatistics[5]; for (int i = 0; i < 5; i++) { hist[i] = new VerticalIntensityStatistics(colorImg[i]); } findPucks(hist); greenHist.Values = hist[GREEN].Gray.Values; redHist.Values = hist[RED].Gray.Values; yellowHist.Values = hist[YELLOW].Gray.Values; blueHist.Values = hist[BLUE].Gray.Values; orangeHist.Values = hist[ORANGE].Gray.Values; pictureBox1.Image = img.ToManagedImage(); } finally { image.UnlockBits( imageData ); } }
private Bitmap cutLot(Bitmap blackCanvas, System.Drawing.Point[] ParkingPoint, int i) { Bitmap compoundImage = null; System.Drawing.Point[] parkingLot = { ParkingPoint[0 + (4 * i)], ParkingPoint[1 + (4 * i)], ParkingPoint[2 + (4 * i)], ParkingPoint[3 + (4 * i)] }; List<AForge.IntPoint> corners = new List<AForge.IntPoint>(); corners.Add(new AForge.IntPoint(ParkingPoint[3 + (4 * i)].X, ParkingPoint[3 + (4 * i)].Y)); corners.Add(new AForge.IntPoint(ParkingPoint[0 + (4 * i)].X, ParkingPoint[0 + (4 * i)].Y)); corners.Add(new AForge.IntPoint(ParkingPoint[1 + (4 * i)].X, ParkingPoint[1 + (4 * i)].Y)); corners.Add(new AForge.IntPoint(ParkingPoint[2 + (4 * i)].X, ParkingPoint[2 + (4 * i)].Y)); QuadrilateralTransformation Qfilter = new QuadrilateralTransformation(corners, 200, 200); //Bitmap frame = new Bitmap(@"currImage.jpg"); Bitmap frame = (Bitmap)currentFrame.Clone(); Bitmap nframe = Qfilter.Apply(frame); nframe.RotateFlip(RotateFlipType.Rotate180FlipY); BackwardQuadrilateralTransformation BQfilter = new BackwardQuadrilateralTransformation(nframe, corners); compoundImage = BQfilter.Apply(blackCanvas); return nframe; }
public Bitmap GetBitmapCustomResolution(int width, int height) { //Fetch the bitmap of the main image this wordsearch image is in Image fromImage = FromImage; fromImage.RegisterInterestInBitmap(); //Transform the region containing this wordsearch into a new image QuadrilateralTransformation quadTransform = new QuadrilateralTransformation( new List<IntPoint>(Coordinates), width, height); Bitmap customResBitmap = quadTransform.Apply(fromImage.Bitmap); //Deregister interest in the main Image's Bitmap to allow for it to dispose of it appropriately fromImage.DeregisterInterestInBitmap(); return customResBitmap; }