using System; using System.Collections.Generic; using System.Linq; using System.Text; using OpenCvSharp; namespace AtariRobot.Pong { public class Recognizer { public PongScreen Recognize(IplImage image) { IplImage smoothFrame = new IplImage(image.Width, image.Height, image.Depth, image.ElemChannels); Cv.Smooth(image, smoothFrame, SmoothType.Gaussian); Cv.Smooth(smoothFrame, smoothFrame, SmoothType.Gaussian); IplImage greyFrame = new IplImage(smoothFrame.Width, smoothFrame.Height, BitDepth.U8, 1); Cv.CvtColor(smoothFrame, greyFrame, ColorConversion.RgbToGray); smoothFrame.Dispose(); IplImage cannyFrame = new IplImage(image.Width, image.Height, BitDepth.U8, 1); Cv.Canny(greyFrame, cannyFrame, 100, 100); greyFrame.Dispose(); Cv.ShowImage("EdgeDetect", cannyFrame); CvMemStorage memStorage = new CvMemStorage(); CvSeq contourSet; Cv.FindContours(cannyFrame, memStorage, out contourSet, CvContour.SizeOf, ContourRetrieval.External); cannyFrame.Dispose(); List colorBoxes = new List(); if (contourSet != null) { IplImage contourFrame = new IplImage(image.Width, image.Height, image.Depth, image.ElemChannels); contourFrame.Zero(); for (CvSeq currentContour = contourSet; currentContour != null; currentContour = currentContour.HNext) { CvRect bounding = currentContour.BoundingRect(); if (bounding.Width < 3 || bounding.Height < 3) { continue; } CvColor color = Helper.GetColorFromBounding(image, bounding); color = Helper.FindClosestKnownColor(color); Cv.DrawContours(contourFrame, currentContour, color, color, 0); ColorBox newBox = new ColorBox() { Box = bounding, Color = color }; if (!colorBoxes.Contains(newBox)) { colorBoxes.Add(newBox); } } contourSet.Dispose(); Cv.ShowImage("Contours", contourFrame); contourFrame.Dispose(); } memStorage.Dispose(); PongScreen screen = new PongScreen(); List discardedBoxes = new List(); //Find borders. //Borders are big white-like things that take up >70% of the screen width. foreach (ColorBox colorBox in colorBoxes) { CvRect box = colorBox.Box; if (box.Width > .7 * image.Width) { if (box.Y > (image.Height / 2)) { screen.BottomBorder = colorBox; } else { screen.TopBorder = colorBox; } discardedBoxes.Add(colorBox); } } if (screen.TopBorder != null && screen.BottomBorder != null) { //Throw out the boundaries and everything outside of them. colorBoxes.RemoveAll(x => discardedBoxes.Contains(x)); colorBoxes.RemoveAll(x => x.Box.Y <= screen.TopBorder.Box.Y || x.Box.Y >= screen.BottomBorder.Box.Y); colorBoxes.RemoveAll(x => x.Color.Equals(CvColor.Black)); colorBoxes.Sort((a, b) => Comparer.Default.Compare(b.Box.Height * b.Box.Width, a.Box.Height * a.Box.Width)); if (colorBoxes.Count < 3) { //I don't have enough stuff, so I don't know what's going on. return screen; } //Find paddles. //Most of the time, they're the two biggest things in the playfield. ColorBox paddleCandidateA = colorBoxes[0]; ColorBox paddleCandidateB = colorBoxes[1]; if (paddleCandidateA.Box.X < paddleCandidateB.Box.X) { screen.LeftPaddle = paddleCandidateA; screen.RightPaddle = paddleCandidateB; } else { screen.LeftPaddle = paddleCandidateB; screen.RightPaddle = paddleCandidateA; } //Find ball. //Most of the time, it's the third biggest thing in the playfield. screen.Ball = colorBoxes[2]; } return screen; } } }