Exemple #1
0
 public ShapeContour(ContourNode node)
 {
     Node = node;
     Shape = GetShape();
     if (Shape == Shape.Other)
     {
         throw new ArgumentException("The contour does not match a shape");
     }
 }
Exemple #2
0
        public ContourNode(Contour<Point> node)
        {
            this.Contour = node;

            Children = new List<ContourNode>();
            List<Contour<Point>> kids = new List<Contour<Point>>(ContourNode.GetChildren(this.Contour));
            foreach(Contour<Point> kid in kids)
            {
                ContourNode kidnode = new ContourNode(kid);
                kidnode.Parent = this;
                Children.Add(kidnode);
            }
            //if (kids.Count > 3)
            //{
            //    throw new SetGameException("Found a card with more than 3 shapes on it.");
            //}
        }
        public static KeyValuePair<Card, ContourNode> AnalyzeNode(ContourNode cardNode)
        {
            if (cardNode.Shape != Shape.Card)
            {
                throw new ArgumentException("The node is not labeled as a Card", "node.Shape");
            }
            else
            {
                int count = cardNode.Children.Count;
                CardColor color = cardNode.Children[0].Color;
                Shape shape = cardNode.Children[0].Shape;
                Card card = new Card(color, shape, cardNode.Fill, count);

                cardNode.Color = color; //Set this, the card's background is actually white, but this color matters for the game and in debugging

                return new KeyValuePair<Card, ContourNode>(card, cardNode);
            }
        }
 public static IEnumerable<KeyValuePair<Card, CardContour>> GiveCards(ContourNode tree)
 {
     if (ContourAnalyzer.IsCard(tree))
     {
         CardContour cardcont = new CardContour(tree);
         yield return new KeyValuePair<Card, CardContour>(cardcont.GetCard(), cardcont);
     }
     else
     {
         foreach (ContourNode child in tree.Children)
         {
             foreach (KeyValuePair<Card, CardContour> pair in GiveCards(child))
             {
                 yield return pair;
             }
         }
     }
 }
        public Dictionary<Card, Point> LocateCards(Image<Bgr, Byte> table, Settings settings)
        {
            #region process image
            //Convert the image to grayscale and filter out the noise
            Image<Gray, Byte> gray = table.Convert<Gray, Byte>();

            Gray cannyThreshold = new Gray(180);
            Gray cannyThresholdLinking = new Gray(120);
            Gray circleAccumulatorThreshold = new Gray(120);

            Image<Gray, Byte> cannyEdges = gray.Canny(cannyThreshold, cannyThresholdLinking);

            StructuringElementEx el = new StructuringElementEx(3, 3, 1, 1, CV_ELEMENT_SHAPE.CV_SHAPE_RECT);
            cannyEdges = cannyEdges.MorphologyEx(el, CV_MORPH_OP.CV_MOP_CLOSE, 1);
            #endregion

            Contour<Point> contours = cannyEdges.FindContours(
                CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, //was CV_CHAIN_APPROX_SIMPLE
                RETR_TYPE.CV_RETR_TREE);

            ContourNode tree = new ContourNode(contours);

            Dictionary<Card, Point> cardlocs = new Dictionary<Card, Point>();
            foreach (KeyValuePair<Card, CardContour> pair in GiveCards(tree))
            {
                ContourNode node = pair.Value.Node;
                Card card = pair.Key;

                PointF fcenter = node.Contour.GetMinAreaRect().center;
                Point center = new Point((int)fcenter.X, (int)fcenter.Y);

                cardlocs.Add(card, center);
            }

            #region draw
            #if DEBUG
            TreeViz.VizualizeTree(tree);
            ContourAnalyzer.DrawContours(tree, table);
            //ImageViewer.Show(table);
            #endif
            #endregion

            return cardlocs;
        }
        public static void DrawContours(ContourNode node, Image<Bgr, Byte> canvas, System.Drawing.Color color)
        {
            Bgr _color = new Bgr(System.Drawing.Color.Red);

            foreach (ContourNode child in node.Children)
            {
                canvas.DrawPolyline(child.Contour.ToArray(), true, _color, 1);

                if (node.Shape != null)
                {
                    MCvFont font = new MCvFont(FONT.CV_FONT_HERSHEY_PLAIN, 1, 1);
                    canvas.Draw(child.Shape + child.Color.ToString(),
                        ref font,
                        child.Contour[0],
                        new Bgr(System.Drawing.Color.Red)
                        );
                }

                DrawContours(child, canvas, color);
            }
        }
        public static void DrawContours(ContourNode node, Image<Bgr, Byte> canvas)
        {
            #region color
            Random rnd = new Random(node.Contour.Ptr.ToInt32());
            Bgr _color = new Bgr(rnd.Next(255), rnd.Next(255), rnd.Next(255));
            #endregion

            foreach (ContourNode child in node.Children)
            {
                canvas.DrawPolyline(child.Contour.ToArray(), true, _color, 1);

                if (node.Shape != null)
                {
                    MCvFont font = new MCvFont(FONT.CV_FONT_HERSHEY_PLAIN, 1, 1);
                    canvas.Draw(child.Shape + child.Color.ToString(),
                        ref font,
                        child.Contour[0],
                        new Bgr(System.Drawing.Color.Red)
                        );
                }

                DrawContours(child, canvas);
            }
        }
        private static CardColor RecognizeColor(ContourNode node, Settings settings)
        {
            Image<Bgr, Byte> image = node.Image;
            image.ROI = node.Contour.BoundingRectangle;

            Image<Bgr, Byte> debugBest = null;
            Point bestpos = FindBestColoredPixel(image, out debugBest);
            Bgr bgr = image[bestpos];

            #region classification
            CardColor classification = classifier.Classify(bgr);
            CardColor verdict = classification;
            if (verdict == CardColor.Other)
            {
                if (!isGray(bgr, 10))
                {
                    verdict = ClassifyBgr2(bgr);
                }
                else
                {
                    verdict = CardColor.White;
                }
            }
            #endregion
            #if DEBUG
            //save for training:
            writer.Write((int)bgr.Blue, (int)bgr.Green, (int)bgr.Red);
            #endif

            #region debug
            if (settings.debuglevel >= 4)
            {
                ImageViewer.Show(debugBest, verdict.ToString());
            }
            else if (settings.debuglevel >= 2 && verdict == CardColor.Other)
            {
                ImageViewer.Show(debugBest, verdict.ToString());
            }
            #endregion
            return verdict;
        }
 private static void AssignImage(ContourNode node, Image<Bgr, Byte> image, bool setROI)
 {
     node.Image = ExtractContourImage(image, node.Contour, out node.AttentionMask);
     if (setROI)
     {
         node.Image.ROI = node.Contour.BoundingRectangle;
         node.AttentionMask.ROI = node.Contour.BoundingRectangle;
     }
 }
 private static void FilterTree(ContourNode tree)
 {
     FilterNode(tree);
     foreach (ContourNode child in tree.Children)
     {
         //FilterNode(child);
         FilterTree(child);
     }
 }
        private static bool isDashed(ContourNode inner)
        {
            Image<Bgr, Byte> im = inner.Image.Clone();
            Rectangle oldroi = inner.Image.ROI;

            #region calc ROI
            double scale = 0.33;
            Point center = new Point(oldroi.X + oldroi.Width / 2, oldroi.Y + oldroi.Height / 2);
            Size size = new Size((int)(oldroi.Size.Width * scale), (int)(oldroi.Size.Height * scale));
            MCvBox2D box = new MCvBox2D(center, size, 0);
            Rectangle newroi = box.MinAreaRect();
            #endregion
            im.ROI = newroi;

            Image<Bgr, float> laplace = im.Laplace(1);

            double[] mins, maxs;
            Point[] minlocs, maxlocs;
            laplace.MinMax(out mins, out maxs, out minlocs, out maxlocs);

            return (maxs[0] > 15);
        }
        private static Fill DetermineFill(ContourNode cardNode)
        {
            Fill fill = Fill.Other;

            try
            {
                ContourNode outer = cardNode.Children[0];
                try
                {
                    ContourNode inner = outer.Children[0];

                    double bgrDist = ColorDistance(cardNode.averageBgr, inner.averageBgr);
                    double hsvDist = ColorDistance(cardNode.averageHsv, inner.averageHsv);

                    if (hsvDist < 20)
                    {
                        fill = Fill.Open;
                    }
                    else if (hsvDist > 100)
                    {
                        fill = Fill.Solid;
                    }
                    else if (isDashed(inner))
                    {
                        fill = Fill.Dashed;
                    }

                    outer.Fill = fill;
                    inner.Fill = fill;
                }
                catch (ArgumentOutOfRangeException)
                {
                    //The inner-node has nu children, which indicates a solid node, as being solid doesnt make edges
                    fill = Fill.Solid;
                }
            }
            catch (ArgumentOutOfRangeException e)
            {
                throw new VisionException("Could not distinguish shape from card", e, cardNode.Image);
            }
            return fill;
        }
        private static void FilterNode(ContourNode node)
        {
            ContourNode card = node.FindParent(Shape.Card, null, null);

            int minArea = 1000;
            if (card != null)
            {
                minArea = (int)card.Contour.Area / 20;
            }

            for (int i = 0; i < node.Children.Count; i++)
            {
                ContourNode child = node.Children[i];
                if (child.Contour.Area < minArea)
                {
                    node.Children.Remove(child);
                }
            }
        }
        /// <summary>
        /// LocateCards works by analyzing the contours in the image. 
        /// For instance, the Diamond in Set is a polygon with exactly 4 vertices. 
        /// The Oval has no such features yet. 
        /// The Squiggle is not convex, but concave and has edges in a 'right bend', instead of only 'left bends'
        /// 
        /// All these shapes are inside the contour of a (white) card, which is a rounded square. 
        /// Cards may also be the (only) exterior boundaries
        /// </summary>
        /// <param name="table">An image displaying the table with the Set cards</param>
        /// <returns>A dict locating which cards are present where in the image</returns>
        public Dictionary<Card, Point> LocateCards(Image<Bgr, Byte> table, Settings settings)
        {
            classifier = new BgrHsvClassifier();
            classifier.Train();

            #region process image
            //Convert the image to grayscale and filter out the noise
            Image<Gray, Byte> gray = table.Convert<Gray, Byte>();

            Gray cannyThreshold = new Gray(50); //180
            Gray cannyThresholdLinking = new Gray(30); //120
            Gray circleAccumulatorThreshold = new Gray(100); //120
            #region old
                    Image<Gray, Byte> cannyEdges = gray.Canny(cannyThreshold, cannyThresholdLinking);
            if (settings.debuglevel >= 3)
            {
                ImageViewer.Show(cannyEdges, "cannyEdges before Closing");
            }
            #endregion
            //#region new
            //Image<Gray, Byte> thresholded = new Image<Gray, byte>(gray.Size);
            //CvInvoke.cvAdaptiveThreshold(gray.Ptr, thresholded.Ptr,
            //    255,
            //    ADAPTIVE_THRESHOLD_TYPE.CV_ADAPTIVE_THRESH_GAUSSIAN_C,
            //    THRESH.CV_THRESH_BINARY_INV, 9, 5);
            //StructuringElementEx el1 = new StructuringElementEx(3, 3, 1, 1, CV_ELEMENT_SHAPE.CV_SHAPE_RECT);
            //thresholded = thresholded.Erode(1);//thresholded.MorphologyEx(el1, CV_MORPH_OP.CV_MOP_CLOSE, 1);//
            //Image<Gray, Byte> cannyEdges = thresholded;//.Canny(new Gray(1), new Gray(10));
            //#endregion

            //StructuringElementEx el = new StructuringElementEx(5, 5, 2, 2, CV_ELEMENT_SHAPE.CV_SHAPE_RECT);
            StructuringElementEx el = new StructuringElementEx(3, 3, 1, 1, CV_ELEMENT_SHAPE.CV_SHAPE_RECT);
            cannyEdges = cannyEdges.MorphologyEx(el, CV_MORPH_OP.CV_MOP_CLOSE, 1);
            if (settings.debuglevel >= 3)
            {
                ImageViewer.Show(cannyEdges, "cannyEdges after Closing");
            }
            #endregion

            Contour<Point> contours = cannyEdges.FindContours(
                CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, //was CV_CHAIN_APPROX_SIMPLE
                RETR_TYPE.CV_RETR_TREE);

            ContourNode tree = new ContourNode(contours);

            FilterTree(tree);
            #region debug
            if (settings.debuglevel >= 3)
            {
                var debug = table.Clone();
                DrawContours(tree, debug);
                ImageViewer.Show(debug, "Contours after filtering");
            }
            #endregion

            AssignShapes(tree);
            AssignImages(tree, table, true);
            #region debug
            if (settings.debuglevel >= 3)
            {
                var debug1 = table.Clone();
                DrawContours(tree, debug1);
                ImageViewer.Show(debug1);
            }
            #endregion

            FilterTree(tree);

            #region debug
            if (settings.debuglevel >= 3)
            {
                var debug2 = table.Clone();
                DrawContours(tree, debug2);
                ImageViewer.Show(debug2);
            }
            #endregion

            AssignColors(tree, table, settings);

            #region debug
            if (settings.debuglevel >= 3)
            {
                TreeViz.VizualizeTree(tree);
            }
            #endregion

            AssignFills(tree);

            Dictionary<Card, Point> cardlocs = new Dictionary<Card, Point>();
            foreach (KeyValuePair<Card, ContourNode> pair in GiveCards(tree))
            {
                ContourNode node = pair.Value;
                Card card = pair.Key;

                PointF fcenter = node.Contour.GetMinAreaRect().center;
                Point center = new Point((int)fcenter.X, (int)fcenter.Y);

                cardlocs.Add(card, center);
            }

            #region debug
            if (settings.debuglevel >= 1)
            {
                TreeViz.VizualizeTree(tree);
            }
            if (settings.debuglevel >= 2)
            {
                DrawContours(tree, table);
                ImageViewer.Show(table);
            }
            #endregion
            return cardlocs;
        }
 public static IEnumerable<KeyValuePair<Card, ContourNode>> GiveCards(ContourNode tree)
 {
     if (tree.Shape == Shape.Card)
     {
         yield return AnalyzeNode(tree);
     }
     else
     {
         foreach (ContourNode child in tree.Children)
         {
             foreach (KeyValuePair<Card, ContourNode> pair in GiveCards(child))
             {
                 yield return pair;
             }
         }
     }
 }
Exemple #16
0
        private bool IsCard(ContourNode node)
        {
            bool area = node.Contour.Area > 5000;
            MCvBox2D bounds = node.Contour.GetMinAreaRect();
            float ratio =
                ((bounds.size.Width / bounds.size.Height)
                +
                (bounds.size.Height / bounds.size.Width));
            //ratio is supposed to be 2.16667

            bool ratioOK = (ratio > 2.0 && ratio < 2.3);
            //#if DEBUG
            //if (area)
            //{
            //    ShowContour(node.Contour, new Image<Bgr, Byte>(900, 900), "CARD");
            //}
            //else
            //{
            //    ShowContour(node.Contour, new Image<Bgr, Byte>(900, 900), "");
            //}
            //#endif
            return ratioOK && area;
        }
Exemple #17
0
        private bool IsOval(ContourNode node)
        {
            bool inCard = node.Parent != null &&
                   (node.Parent.Shape == Shape.Card ||
                       node.Parent.Shape == Shape.Oval);
            if (inCard)
            {
                #region sharp
                //An oval doesn't have sharp angles
                bool sharp = false;
                bool vertices = false;

                using (MemStorage storage = new MemStorage())
                {
                    Contour<Point> currentContour = node.Contour.ApproxPoly(node.Contour.Perimeter * 0.01, storage);
                    vertices = currentContour.Total > 4;

                    Point[] points = currentContour.ToArray();
                    List<LineSegment2D> edges = new List<LineSegment2D>(PointCollection.PolyLine(points, true));

            #if DEBUG
                    #region draw
                    //Image<Bgr, Byte> debug = new Image<Bgr, Byte>(800, 800);
                    //debug.DrawPolyline(points, true, new Bgr(0, 0, 255), 1);
                    #endregion draw
            #endif
                    for (int i = 0; i < edges.Count; i++)
                    {
                        double angle = edges[(i + 1) % edges.Count].GetExteriorAngleDegree(edges[i]);
                        if (Math.Abs(angle) > 60)
                        {
                            sharp = true;
                        }

                        //MCvFont font = new MCvFont(FONT.CV_FONT_HERSHEY_PLAIN, 1, 1);
                        //debug.Draw(((int)angle).ToString(), ref font, edges[i].P2, new Bgr(0, 0, 255));
                    }
                    //ImageViewer.Show(debug, "Sharp:" + sharp.ToString());
                }
                #endregion

                return !sharp && vertices;
            }
            else
            {
                return false;
            }
        }
        private static void AssignAverageColors(ContourNode node, Settings settings)
        {
            //Then get the average color of the card (should be white or gray)
            Bgr avgBgr = new Bgr();
            MCvScalar scr1 = new MCvScalar();
            node.Image.AvgSdv(out avgBgr, out scr1, node.AttentionMask);

            node.averageBgr = avgBgr;

            Hsv avgHsv = new Hsv();
            MCvScalar scr2 = new MCvScalar();
            node.Image.Convert<Hsv, Byte>().AvgSdv(out avgHsv, out scr2, node.AttentionMask);

            node.averageBgr = avgBgr;
            node.averageHsv = avgHsv;
        }
 private static void AssignFills(ContourNode tree)
 {
     AssignFill(tree);
     foreach (ContourNode child in tree.Children)
     {
         AssignFills(child);
     }
 }
 private static void AssignFill(ContourNode node)
 {
     if (node.Shape == Shape.Card)
     {
         node.Fill = DetermineFill(node);
     }
 }
 private static void AssignColors(ContourNode tree, Image<Bgr, Byte> image, Settings settings)
 {
     AssignColor(tree, image, settings);
     foreach (ContourNode child in tree.Children)
     {
         //AssignColor(child, image, settings);
         AssignColors(child, image, settings);
     }
 }
        private static void AssignColor(ContourNode node, Image<Bgr, Byte> image, Settings settings)
        {
            if (node.Contour.Area > 100
                && ((node.Shape == Shape.Squiggle)
                    || (node.Shape == Shape.Diamond)
                    || (node.Shape == Shape.Oval)))
            {
                //CardColor color = RecognizeColor(image, node);
                //CardColor color = RecognizeColor(node);
                CardColor color = RecognizeColor(node, settings);
                node.Color = color;

                AssignAverageColors(node, settings);
            }
            else if (node.Shape == Shape.Card)
            {
                AssignAverageColors(node, settings);
            }
        }
Exemple #23
0
 public CardContour(ContourNode node)
 {
     Node = node;
 }
 private static void AssignImages(ContourNode tree, Image<Bgr, Byte> image, bool setROI)
 {
     AssignImage(tree, image, setROI);
     foreach (ContourNode child in tree.Children)
     {
         AssignImages(child, image, setROI);
     }
 }
Exemple #25
0
        private bool IsDiamond(ContourNode node)
        {
            bool inCard = node.Parent != null &&
                   (node.Parent.Shape == Shape.Card ||
                       node.Parent.Shape == Shape.Diamond);
            if (inCard)
            {
                using (MemStorage storage = new MemStorage())
                {
                    Contour<Point> currentContour = node.Contour.ApproxPoly(node.Contour.Perimeter * 0.02, storage);
                    Point[] points = currentContour.ToArray();
                    List<LineSegment2D> contourEdges = new List<LineSegment2D>(PointCollection.PolyLine(points, true));

                    bool sides4 = currentContour.Total == 4;

                    //Image<Bgr, Byte> debug = new Image<Bgr, byte>(700, 700);
                    //debug.DrawPolyline(points, true, new Bgr(0, 255, 0), 1);
                    //ImageViewer.Show(debug, "Sides:" + currentContour.Total.ToString());

                    return sides4;
                }
            }
            else
            {
                return false;
            }
        }
 private static void AssignShape(ContourNode node)
 {
     if (IsCard(node))
     {
         node.Shape = Shape.Card;
     }
     else if (IsSquiggle(node))
     {
         node.Shape = Shape.Squiggle;
     }
     else if (IsOval(node))
     {
         node.Shape = Shape.Oval;
     }
     else if (IsDiamond(node))
     {
         node.Shape = Shape.Diamond;
     }
     else
     {
         node.Shape = Shape.Other;
     }
 }
Exemple #27
0
        private bool IsSquiggle(ContourNode node)
        {
            bool inCard = node.Parent != null &&
                (node.Parent.Shape == Shape.Card ||
                    node.Parent.Shape == Shape.Squiggle);
            bool convex = true;

            if (inCard)
            {
            #if DEBUG
                //ShowContour(node.Contour, new Image<Bgr, Byte>(900, 900), "Squiggle");
            #endif
                using (MemStorage storage = new MemStorage())
                {
                    Contour<Point> currentContour = node.Contour.ApproxPoly(node.Contour.Perimeter * 0.01, storage);
                    Point[] points = currentContour.ToArray();
                    List<LineSegment2D> edges = new List<LineSegment2D>(PointCollection.PolyLine(points, true));

            #if DEBUG
                    #region draw
                    //Image<Bgr, Byte> debug = new Image<Bgr, Byte>(800, 800);
                    //debug.DrawPolyline(points, true, new Bgr(0, 0, 255), 1);
                    //ImageViewer.Show(debug);
                    #endregion draw
            #endif
                    //List<double> angles = new List<double>(edges.Count);
                    for (int i = 0; i < edges.Count; i++)
                    {
                        double angle = edges[(i + 1) % edges.Count].GetExteriorAngleDegree(edges[i]);
                        //angles.Add(angle);
                        if (angle < 0)
                        {
                            convex = false;
                            break;
                        }

                        //MCvFont font = new MCvFont(FONT.CV_FONT_HERSHEY_PLAIN, 1, 1);
                        //debug.Draw(((int)angle).ToString(), ref font, edges[i].P2 ,new Bgr(0, 0, 255));
                    }
                    //angles.Sort();
                    //if (angles[0] < 45)
                    //{
                    //    convex = false;
                    //}
                    //ImageViewer.Show(debug);
                }
            }
            return !convex && inCard;
        }
 private static void AssignShapes(ContourNode tree)
 {
     AssignShape(tree);
     foreach (ContourNode child in tree.Children)
     {
         //AssignShape(child);
         AssignShapes(child); //TODO: should be enabled
     }
 }
Exemple #29
0
 private Image<Bgr, Byte> RemoveCardColor(ContourNode node, Image<Bgr, Byte> removeFrom)
 {
     ContourNode parentCard = node.FindParent(Shape.Card, null, null);
     if (parentCard != null)
     {
         //Image<Bgr, Byte> eroded = removeFrom.Erode(1);
         //Image<Bgr, Byte> thresholded = eroded.ThresholdBinary(parentCard.averageBgr, new Bgr(255,255,255));
         //return thresholded;
         Image<Gray, Byte> gray = removeFrom.Convert<Gray, Byte>();
         Image<Bgr, Byte> multiplier = new Image<Bgr, byte>(new Image<Gray, byte>[] { gray, gray, gray });
         Image<Bgr, Byte> mul = removeFrom.Mul(multiplier, 0.015);
         Image<Bgr, Byte> threshed = mul.ThresholdToZeroInv(new Bgr(254, 254, 254));
         Image<Gray, Byte> mask = threshed.Convert<Gray, Byte>();
         Image<Bgr, Byte> result = mul.And(new Bgr(255, 255, 255), mask);
         result = result.Mul(multiplier, 0.005);
         return result;
     }
     else
     {
         return removeFrom;
     }
 }
Exemple #30
0
 public void FillTree(ContourNode tree)
 {
     List<ContourNode> _children = new List<ContourNode>();
     Children = _children;
 }