Esempio n. 1
0
        /// <summary>
        /// Draw All the Contours On An Image
        /// </summary>
        internal void FireImageEvent(object sender, Guid cardId, Guid imageId, ImageType imageType, double angle, float X, float Y,
                                     VectorOfVectorOfPoint contours, CannyParam cannyParameter = null, Func <Size, Rectangle, bool> contourFilter = null)
        {
            if (ImageEvent != null)
            {
                using (Mat rotatedImage = GetRotatedImage(angle))
                {
                    for (int i = 0; i < contours.Size; i++)
                    {
                        RotatedRect rotatedRect = CvInvoke.MinAreaRect(contours[i]);
                        Rectangle   box         = rotatedRect.MinAreaRect();

                        if (contourFilter == null || (contourFilter != null && contourFilter(rotatedImage.Size, box)))
                        {
                            CvInvoke.DrawContours(rotatedImage, contours, i, yellow);
                            CvInvoke.Rectangle(rotatedImage, box, green, thickness: 2);

                            //if (tagged)
                            //{
                            //    CvInvoke.PutText(rotatedImage, string.Format("HR: {0:0.00} AR: {0:0.00}", heightRatio, aspectRatio),
                            //        new Point((int)box.X + (box.Width / 2), (int)box.Y + (box.Height / 2)),
                            //        FontFace.HersheyPlain, fontScale: 1, color: black, thickness: 2);
                            //}
                        }
                    }

                    Image.FireImageEvent(null, cardId, imageId, imageType, rotatedImage, angle, X, Y, cannyParameter: cannyParameter);
                }
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Get the Canny Image Of the Gray Image
        /// </summary>
        public Mat GetCannyImage(Func <Mat> getCannyBase, double angle, CannyParam cannyParameter)
        {
            if (cannyParameter == null)
            {
                throw new ArgumentNullException("cannyParameter");
            }

            if (_cannyImage.image == null || _cannyImage.angle != angle || !_cannyImage.cannyParam.Equals(cannyParameter))
            {
                if (_cannyImage.image != null)
                {
                    _cannyImage.image.Dispose();
                    _cannyImage.image = null;
                }

                using (Mat grayImage = getCannyBase())
                {
                    using (Mat cannyImage = new Mat())
                    {
                        CvInvoke.Canny(grayImage, cannyImage,
                                       threshold1: cannyParameter.Threshold1,
                                       threshold2: cannyParameter.Threshold2,
                                       apertureSize: cannyParameter.Aperture, l2Gradient: cannyParameter.L2Graident);

                        _cannyImage.image      = cannyImage.Clone();
                        _cannyImage.angle      = angle;
                        _cannyImage.cannyParam = cannyParameter;
                    }
                }
            }

            return(_cannyImage.image.Clone());
        }
Esempio n. 3
0
 /// <summary>
 /// Fire the Image Event And Send a Contoured Image
 /// </summary>
 protected void FireImageEvent(object sender, Guid cardId, Guid imageId, ImageType imageType,
                               double angle, float X, float Y, CannyParam cannyParameter = null, ThresholdParm thresholdParmeter = null)
 {
     if (ImageEvent != null)
     {
         using (var image = GetContouredImage(angle, cannyParameter))
         {
             Image.FireImageEvent(null, cardId, imageId, imageType, image, angle, X, Y, cannyParameter);
         }
     }
 }
Esempio n. 4
0
        /// <summary>
        /// Fire An Image Event
        /// </summary>
        /// <param name="image">Image To Event</param>
        protected static void FireImageEvent(object sender, Guid cardId, Guid imageId,
                                             ImageType imageType,
                                             Mat image,
                                             double angle,
                                             float X,
                                             float Y,
                                             CannyParam cannyParameter       = null,
                                             ThresholdParm thresholdParmeter = null)
        {
            ImageEventArgs eventArgs = new ImageEventArgs(cardId, imageId, imageType, image, angle, X, Y,
                                                          cannyParameter: cannyParameter, thresholdParamter: thresholdParmeter);

            if (ImageEvent != null)
            {
                ImageEvent(sender: sender, data: eventArgs);
            }
        }
Esempio n. 5
0
 public ImageEventArgs(Guid cardId, Guid imageId, ImageType imageType,
                       Mat image,
                       double angle,
                       float x,
                       float y,
                       CannyParam cannyParameter       = null,
                       ThresholdParm thresholdParamter = null)
 {
     CardId             = cardId;
     ImageId            = imageId;
     ImageType          = imageType;
     Image              = image.Clone();
     Angle              = angle;
     X                  = x;
     Y                  = y;
     CannyParameters    = cannyParameter;
     ThresholdParamters = thresholdParamter;
 }
Esempio n. 6
0
        /// <summary>
        /// Get Original Title Image With Contour Lines Drawn
        /// </summary>
        public Mat GetContouredImage(double angle, CannyParam cannyParameter, float minArea = 5000.0F)
        {
            if (cannyParameter == null)
            {
                throw new ArgumentNullException("cannyParameter");
            }

            if (_contouredImage.image == null || !_contouredImage.cannyParam.Equals(cannyParameter))
            {
                if (_contouredImage.image != null)
                {
                    _contouredImage.image.Dispose();
                    _contouredImage.image = null;
                }

                _contouredImage.image = GetGreyImage(angle);

                using (Mat titleCannyImage = GetCannyImage(() => { return(GetGreyImage(angle)); }, angle, cannyParameter))
                {
                    using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
                    {
                        CvInvoke.FindContours(titleCannyImage, contours, hierarchy: null, mode: RetrType.List, method: ChainApproxMethod.ChainApproxNone);

                        for (int i = 1; i < contours.Size; i++)
                        {
                            RotatedRect rotatedRect = CvInvoke.MinAreaRect(contours[i]);
                            float       area        = rotatedRect.Size.Width * rotatedRect.Size.Height;

                            if (area > minArea)
                            {
                                CvInvoke.DrawContours(_contouredImage.image, contours, i, yellow, thickness: 2);
                                Rectangle box = rotatedRect.MinAreaRect();
                                CvInvoke.Rectangle(_contouredImage.image, box, green, thickness: 2);
                            }
                        }

                        _contouredImage.cannyParam = cannyParameter;
                    }
                }
            }

            return(_contouredImage.image.Clone());
        }
Esempio n. 7
0
        /// <summary>
        /// Fire Image Event With Rectangles Drawn On Image
        /// </summary>
        internal void FireImageEvent(object sender, Guid cardId, Guid imageId, ImageType imageType, double angle, float X, float Y,
                                     IEnumerable <Rectangle> rectangles, MCvScalar color, int thickness = 1, CannyParam cannyParameter = null, Action <Mat> postFunc = null)
        {
            if (ImageEvent != null)
            {
                using (Mat rotatedImage = GetRotatedImage(angle))
                {
                    foreach (Rectangle rectangle in rectangles)
                    {
                        CvInvoke.Rectangle(rotatedImage, rectangle, color, thickness: thickness);
                    }

                    if (postFunc != null)
                    {
                        postFunc(rotatedImage);
                    }



                    Image.FireImageEvent(null, cardId, imageId, imageType, rotatedImage, angle, X, Y, cannyParameter: cannyParameter);
                }
            }
        }
Esempio n. 8
0
        /// <summary>
        /// Get a filtered image that is good for OCR
        /// </summary>
        public Mat GetFilteredImage(MTGCardFrame cardTitleType, double angle, ThresholdParm thresoldParameter, CannyParam cannyParameter)
        {
            if (_filteredImage.image == null || _filteredImage.cannyParam != cannyParameter || _filteredImage.angle != angle)
            {
                using (Mat titleImage = GetImage())
                {
                    Size titleSize = titleImage.Size;

                    int minX = titleImage.Width;
                    int maxX = titleImage.Height;
                    int minY = 0;
                    int maxY = 0;

                    // Get the Canny Image To Make A Contour Set For the Letters
                    using (Mat titleCannyImage = GetCannyImage(() => { return(GetThresholdImage(angle, thresoldParameter)); }, angle, cannyParameter))
                    {
                        using (Mat maskedImage = new Mat(titleSize, DepthType.Cv8U, 1))
                        {
                            // White/black out the mask.  Legacy cards are white letters on color, the new style is black letters in a colored title box.
                            maskedImage.SetTo(cardTitleType == MTGCardFrame.M15 ? black : white);

                            using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
                            {
                                CvInvoke.FindContours(titleCannyImage, contours, hierarchy: null, mode: retrType, method: chainApproxMethod);

                                for (int i = 1; i < contours.Size; i++)
                                {
                                    using (VectorOfPoint contour = contours[i])
                                    {
                                        Rectangle rect = CvInvoke.BoundingRectangle(contour);

                                        double relativeY      = (double)rect.Y / (double)titleSize.Height;
                                        double relativeX      = (double)rect.X / (double)titleSize.Width;
                                        double relativeBottom = ((double)rect.Y + (double)rect.Height) / (double)titleSize.Height;
                                        double relativeRight  = ((double)rect.X + (double)rect.Width) / (double)titleSize.Width;
                                        double relativeWidth  = (double)rect.Width / (double)titleSize.Width;
                                        double relativeHeight = (double)rect.Height / (double)titleSize.Height;

                                        // The Characters Start In the 10 % of the Title Image, and Complete Within the
                                        if (((relativeY > .10) && (relativeY < .80) &&
                                             (relativeBottom > .30) && (relativeBottom < .95) &&
                                             (relativeRight > .025) && (relativeX < .95)) &&
                                            (relativeWidth < .09) && (relativeHeight > .07))
                                        {
                                            // Make a mask of rectangles that represent the title.
                                            CvInvoke.Rectangle(maskedImage, rect, new MCvScalar(255, 255, 255), -1);

                                            // Keep Track Of the Border Area Which Comprises The Letters
                                            minX = Math.Min(rect.X, minX);
                                            minY = Math.Min(rect.Y, minY);
                                            maxX = Math.Max(rect.X + rect.Width, maxX);
                                            maxY = Math.Max(rect.Y + rect.Height, maxY);
                                        }
                                    }
                                }
                            }

                            // The result is the same depth/channels as the thresold
                            using (Mat result = GetThresholdImage(angle, thresoldParameter))
                            {
                                // Clear the result
                                result.SetTo(cardTitleType == MTGCardFrame.M15 ? black : white);

                                // Use the Mask To Take Just The Title From the thresoldImage
                                GetThresholdImage(angle, thresoldParameter).CopyTo(result, maskedImage);

                                _filteredImage.image      = result.Clone();
                                _filteredImage.cannyParam = cannyParameter;
                                _filteredImage.angle      = angle;
                            }
                        }
                    }
                }
            }

            return(_filteredImage.image.Clone());
        }
Esempio n. 9
0
        //public IEnumerable<Mat> FindSetIcons()
        //{
        //    List<Mat> results = new List<Mat>();

        //    using (var contours = new VectorOfVectorOfPoint())
        //    {
        //        foreach (var angle in CardImage.IconAngles())
        //        {
        //            foreach (var cannyParameter in CardImage.CannyIconParameters())
        //            {
        //                Debug.WriteLine("Find Set Icon Image Angle: {0} Canny: {1} ...", angle, cannyParameter);

        //                using (Mat cannyImage = GetCannyImage(() => { return GetGreyImage(angle); }, angle, cannyParameter))
        //                {
        //                    // Find All the Contours
        //                    CvInvoke.FindContours(cannyImage, contours, hierarchy: null, mode: RetrType.List, method: ChainApproxMethod.ChainApproxNone);

        //                    List<Rectangle> rectangleList = new List<Rectangle>();

        //                    // Create a List of Rectangles From the Contours
        //                    for (int idx = 0; idx < contours.Size; idx++)
        //                    {
        //                        Rectangle rectangle = CvInvoke.MinAreaRect(contours[idx]).MinAreaRect();
        //                        rectangleList.Add(rectangle);
        //                    }

        //                    // Only add those which fall in the Icon "Zone"
        //                    rectangleList = rectangleList.Where(r => CardExpansionSymbolFilter.ExpansionSymbolFilterPass1(_cardFrame, cannyImage.Size, r)).ToList();

        //                    // Output Contoured Image For Debugging
        //                    this.FireImageEvent(null, _cardId, _cardId, ImageType.CardContoured, angle: angle, X: 0, Y: 0,
        //                        rectangles: rectangleList, color: blue, thickness: 1, cannyParameter: cannyParameter, postFunc: new Action<Mat>((image) =>
        //                        {
        //                            // After Drawing the Rectangles, Add The Margin Lines
        //                            CvInvoke.Line(image, new Point((int)(image.Width * CardExpansionSymbolFilter.LeftMargin[_cardFrame]), 0),
        //                                new Point((int)(image.Width * CardExpansionSymbolFilter.LeftMargin[_cardFrame]), image.Height), white, thickness: 1);

        //                            CvInvoke.Line(image, new Point((int)(image.Width * CardExpansionSymbolFilter.RightMargin[_cardFrame]), 0),
        //                                new Point((int)(image.Width * CardExpansionSymbolFilter.RightMargin[_cardFrame]), image.Height), white, thickness: 1);

        //                            CvInvoke.Line(image, new Point(0, (int)(image.Height * CardExpansionSymbolFilter.TopMargin[_cardFrame])),
        //                                new Point(image.Width, (int)(image.Height * CardExpansionSymbolFilter.TopMargin[_cardFrame])), white, thickness: 1);

        //                            CvInvoke.Line(image, new Point(0, (int)(image.Height * CardExpansionSymbolFilter.BottomMargin[_cardFrame])),
        //                                new Point(image.Width, (int)(image.Height * CardExpansionSymbolFilter.BottomMargin[_cardFrame])), white, thickness: 1);

        //                        }));

        //                    // Merge all possible rectangles that could be icons together
        //                    var intersectedRectangles = MergedRectangles(rectangleList.ToArray())
        //                        .Distinct()
        //                        .Except(rectangleList);

        //                    rectangleList.AddRange(intersectedRectangles);

        //                    // Make a second pass at filtering to reduce the rectangles into only those
        //                    // that could possibly be icons based on aspect ratio, size, etc...
        //                    rectangleList = rectangleList.Where(r => CardExpansionSymbolFilter.ExpansionSymbolFilterPass2(_cardFrame, cannyImage.Size, r)).ToList();

        //                    // Sort Each Rectangle From Biggest To Smallest
        //                    rectangleList.Sort((r1, r2) =>
        //                    {
        //                        float area1 = r1.Width * r1.Height;
        //                        float area2 = r2.Width * r2.Height;

        //                        return area2.CompareTo(area1);
        //                    });

        //                    // Skip mat creation if there are no rectangles
        //                    if (rectangleList.Count > 0)
        //                    {
        //                        using (Mat image = GetGreyImage(angle))
        //                        {
        //                            foreach (Rectangle rectangle in rectangleList)
        //                            {
        //                                using (Mat cropped = new Mat(image, rectangle))
        //                                {
        //                                    // Output cropped image for debugging.
        //                                    Image.FireImageEvent(this, _cardId, _cardId, ImageType.SetIcon,
        //                                        cropped, angle: angle, X: rectangle.X + (rectangle.Width / 2), Y: rectangle.Y + (rectangle.Height / 2),
        //                                        cannyParameter: cannyParameter);

        //                                    results.Add(cropped.Clone());
        //                                }
        //                            }
        //                        }
        //                    }
        //                }
        //            }
        //        }
        //    }

        //    // Sort to biggest first, this make sure FirstDefault() take the most likely one
        //    results.Sort((m1, m2) =>
        //    {
        //        float area1 = m1.Size.Height * m1.Size.Width;
        //        float area2 = m2.Size.Height * m2.Size.Width;

        //        return area2.CompareTo(area1);
        //    });

        //    return results;
        //}

        /// <summary>
        /// Try To Find The Title In the Image
        /// </summary>
        /// <param name="cardImage">Image</param>
        /// <param name="countor">Contour To Test</param>
        /// <param name="result">Resulting Title Image</param>
        /// <returns>True If Title Is Found</returns>
        private static bool TryFindTitle(Guid cardId, Guid cardTitleId,
                                         Mat cardImage, double angle, CannyParam cannyParameter,
                                         VectorOfPoint countor, out Mat result, out MTGCardFrame cardTitleType)
        {
            result        = null;
            cardTitleType = MTGCardFrame.M15;

            RotatedRect rotatedRect = CvInvoke.MinAreaRect(countor);

            // Prevent Divide By Zero
            if (rotatedRect.Size.Height == 0)
            {
                return(false);
            }

            float width           = rotatedRect.Size.Width;
            float height          = rotatedRect.Size.Height;
            float heightRatio     = rotatedRect.Size.Height / (float)cardImage.Size.Height;
            float relativeCenterY = rotatedRect.Center.Y / (float)cardImage.Size.Height;

            Rectangle box = rotatedRect.MinAreaRect();

            // Prevent Divide By Zero
            if (box.Size.Width == 0)
            {
                return(false);
            }

            float widthRatio   = (float)box.Size.Width / (float)cardImage.Size.Width;
            float aspectRatio  = (float)box.Size.Height / (float)box.Size.Width;
            float area         = (float)box.Size.Height * (float)box.Size.Width;
            float imageArea    = (float)cardImage.Size.Height * (float)cardImage.Size.Width;
            float relativeArea = area / imageArea;

            // Title bar should have a height
            if (height < 1.0F)
            {
                return(false);
            }

            // Box Should Be Inside the Image
            if ((box.Y < 0) || (box.X < 0) || ((box.X + box.Width) > cardImage.Size.Width) || ((box.Y + box.Height) > cardImage.Size.Height))
            {
                return(false);
            }


            // Name bar should center in the top 15% of the image, this is the new style cards with the name "boxed" in the image
            if (relativeCenterY < .15F)
            {
                // Title Bar Should Be Wider Than 80% of the Image Width
                if (widthRatio < .80F)
                {
                    return(false);
                }

                using (Mat cropped = new Mat(cardImage, box))
                {
                    Image.FireImageEvent(null, cardId, cardId, ImageType.TitleCropped, cropped, angle: angle, X: box.X, Y: box.Y, cannyParameter: cannyParameter);
                }

                // Title Bar Should Be 6% of the Card Height
                if (heightRatio < .048 || heightRatio > .077)
                {
                    return(false);
                }

                Debug.WriteLine("Title Contour ({14}) - Center: {0}/{1} Relative Center: ({9}%)/({10}%) Width: {2} ({11}%) Height: {3} ({12}%) Area: {4} ({13}%) : AspectRatio: {5}, Angle: {6} Image Size: {7}/{8}",
                                rotatedRect.Center.X, rotatedRect.Center.Y,
                                rotatedRect.Size.Width, rotatedRect.Size.Height,
                                area, aspectRatio, rotatedRect.Angle,
                                cardImage.Size.Width, cardImage.Size.Height,
                                (rotatedRect.Center.X / cardImage.Size.Width) * 100.0,
                                relativeCenterY * 100.0,
                                widthRatio * 100.0,
                                heightRatio * 100.0,
                                relativeArea * 100.0,
                                cardTitleId);

                using (Mat cropped = new Mat(cardImage, box))
                {
                    Image.FireImageEvent(null, cardId, cardTitleId, ImageType.TitleCropped, cropped, angle: angle, X: box.X, Y: box.Y, cannyParameter: cannyParameter);

                    result = cropped.Clone();
                    return(true);
                }
            }
            else if (relativeCenterY < .50F)
            {
                // Assume that this card is the older style card with the name above the photo, but not "boxed"
                //
                cardTitleType = MTGCardFrame.Original;

                // Using the Aspect Ratio Find the Art Box
                if (aspectRatio < .75F || aspectRatio > .85F)
                {
                    return(false);
                }

                // The art relative area to the card should be above 35%
                if (relativeArea < .35F)
                {
                    return(false);
                }

                Debug.WriteLine("Title Contour ({14}) - Center: {0}/{1} Relative Center: ({9}%)/({10}%) Width: {2} ({11}%) Height: {3} ({12}%) Area: {4} ({13}%) : AspectRatio: {5}, Angle: {6} Image Size: {7}/{8}",
                                rotatedRect.Center.X, rotatedRect.Center.Y,
                                rotatedRect.Size.Width, rotatedRect.Size.Height,
                                area, aspectRatio, rotatedRect.Angle,
                                cardImage.Size.Width, cardImage.Size.Height,
                                (rotatedRect.Center.X / cardImage.Size.Width) * 100.0,
                                relativeCenterY * 100.0,
                                widthRatio * 100.0,
                                heightRatio * 100.0,
                                relativeArea * 100.0,
                                cardTitleId);

                int borderHeight = (int)((double)box.Y * .45);

                // Create a box that is as wide as the art, and directly above the art to
                // the top of the card
                Rectangle titleBox = new Rectangle(box.X, borderHeight, box.Width, box.Y - borderHeight);

                heightRatio = titleBox.Size.Height / (float)cardImage.Size.Height;

                // Title Bar Should Be 6% of the Card Height
                if (heightRatio < .050 || heightRatio > .065)
                {
                    return(false);
                }

                using (Mat cropped = new Mat(cardImage, titleBox))
                {
                    Image.FireImageEvent(null, cardId, cardId, ImageType.TitleCropped, cropped, angle: angle, X: titleBox.X, Y: titleBox.Y, cannyParameter: cannyParameter);

                    result = cropped.Clone();
                    return(true);
                }
            }

            return(false);
        }