/// <summary> /// Initilize a new instance of the CardImage class /// </summary> /// <param name="image">Card Image</param> /// <param name="cardTitleImage">Card Title Image (Sub Image of image)</param> public CardImage(Guid cardId, Mat image, CardTitleImage cardTitleImage) : base(image) { _cardId = cardId; _cardTitleImage = cardTitleImage; _angle = cardTitleImage.Angle; _cardFrame = cardTitleImage.CardFrame; }
/// <summary> /// Dipose Of Images /// </summary> protected override void DisposeImages() { base.DisposeImages(); if (_cardTitleImage != null) { _cardTitleImage.Dispose(); _cardTitleImage = null; } }
/// <summary> /// Search the Card Image For The Title And Result It As a Wrapped Class (CardTitleImage) /// </summary> internal static CardTitleImage FindBestTitleImage(Guid cardId, Image cardImage, float X, float Y, IEnumerable <CannyParam> cannyParameters, IEnumerable <double> angles) { List <CardTitleImage> potentialCardTitleImages = new List <CardTitleImage>(); ContourCache contourCache = new ContourCache(); using (var contours = new VectorOfVectorOfPoint()) { foreach (var angle in angles) { foreach (var cannyParameter in cannyParameters) { Debug.WriteLine(string.Format("Find Title Image Canny {0} Angle {1}...", cannyParameter, angle)); using (Mat cannyImage = cardImage.GetCannyImage(() => { return(cardImage.GetGreyImage(angle)); }, angle, cannyParameter)) { CvInvoke.FindContours(cannyImage, contours, hierarchy: null, mode: RetrType.List, method: ChainApproxMethod.ChainApproxNone); var sortedContours = new List <VectorOfPoint>(); for (int idx = 0; idx < contours.Size; idx++) { sortedContours.Add(contours[idx]); } double optimalAspectRatio = ((MaxTitleAspectRatio - MinTitleAspectRatio) / 2.0) + MinTitleAspectRatio; // Sort Each Contour By Delta From the Optiomal Aspect Ratio, Smallest First sortedContours.Sort((c1, c2) => { RotatedRect rotatedRect1 = CvInvoke.MinAreaRect(c1); double aspectRatio1 = (double)(rotatedRect1.Size.Height / rotatedRect1.Size.Width); RotatedRect rotatedRect2 = CvInvoke.MinAreaRect(c2); double aspectRatio2 = (double)(rotatedRect2.Size.Height / rotatedRect2.Size.Width); double diff1 = Math.Abs(aspectRatio1 - optimalAspectRatio); double diff2 = Math.Abs(aspectRatio2 - optimalAspectRatio); return(diff1.CompareTo(diff2)); }); using (Mat rotatedImage = cardImage.GetRotatedImage(angle)) { cardImage.FireImageEvent(null, cardId, cardId, ImageType.CardContoured, angle: angle, X: X, Y: Y, contours: contours, cannyParameter: cannyParameter); // Find The Contour That Matchs What A Title Should Look Like foreach (VectorOfPoint countour in sortedContours) { // Keep a list of contours that have been tested and don't test those again // that allows us to go through a bunch of test thresholds to determine // if they will yield new contours. if (contourCache.Contains(countour, angle)) { continue; } // Store all the rotated rects that have been examined contourCache.Add(countour, angle); Mat result = null; MTGCardFrame cardTitleType; try { Guid cardTitleId = Guid.NewGuid(); if (TryFindTitle(cardId, cardTitleId, rotatedImage, angle, cannyParameter, countour, result: out result, cardTitleType: out cardTitleType)) { // Wrap the Mat Found Image image = new Image(result); // Get the MTG card with the minimum Levenshtein distance for the text parsed // from the image, this will be the closet MTG card to the image var levenshteinDistanceResults = CardTitleImage.GetLowLevenshteinDistanceCards(cardId, cardTitleId, X, Y, cardTitleType, image); // Create a class from the card title image, the card title type, and the minimum levenshtien distance cards CardTitleImage cardTitleImage = new CardTitleImage(cardId, cardTitleId, result, cardTitleType, levenshteinDistanceResults, angle); // Direct Match Short Circuit if (levenshteinDistanceResults.Any(ldr => ldr.Distance < LevenshteinDistanceShortCircuit)) { return(cardTitleImage); } // Add it to the potential card titles potentialCardTitleImages.Add(cardTitleImage); } } finally { if (result != null) { result.Dispose(); } } } } } } } } if (potentialCardTitleImages.Count == 0) { return(null); } // Find The minimum number of character changes (the result of the Levenshtein Distance) alogorithm double minLevenshteinResults = potentialCardTitleImages.Min(cti => cti.LevenshteinResults.Min(lr => lr.PercentDistance)); // Find all unique cards that have this distance var potentialCardNames = potentialCardTitleImages.SelectMany(cti => cti.LevenshteinResults) .Where(lr => lr.PercentDistance == minLevenshteinResults) .Select(lr => lr.Card.Name) .Distinct() .ToArray(); if (potentialCardNames.Length == 0) { // There are no cards that match the image return(null); } else if (potentialCardNames.Length > 1) { // Inconclusive Results // There is more than one card with a low Levenshtein score that matches the images presented. return(null); } else { // Winner Winner Chicken Dinner string name = potentialCardNames[0]; return(potentialCardTitleImages.FirstOrDefault(cti => cti.LevenshteinResults.Any(lr => lr.Card.Name.Equals(name)))); } }
/// <summary> /// The Sureness that the name matches the image. /// </summary> public double GetNameSurity() { CardTitleImage cardTitleImage = GetCardTitleImage(); return(cardTitleImage == null ? 0 : cardTitleImage.Surity); }
/// <summary> /// Get the Name Of the Card In This Image /// </summary> public string GetName() { CardTitleImage cardTitleImage = GetCardTitleImage(); return(cardTitleImage == null ? null : cardTitleImage.Name); }