예제 #1
0
        /// <summary>
        /// Calculates the match between the test-array and the templateArray, represented in a float from 0 to 1.
        /// </summary>
        /// <param name="test">The array to test</param>
        /// <param name="templateArray">The existing template to which the test will be compared.</param>
        /// <param name="match">match, 0 no equal pixels, 1 all pixels are identical.</param>
        /// <param name="offset">0 no shift. 1 the test is shifted one pixel to the right. -1 the test is shifted one pixel to the left.</param>
        public static void letterMatch(uint[] test, uint[] templateArray, out float match, out int offset)
        {
            match  = 0;
            offset = 0;
            // test letter and also shifted by one pixel (offset)
            for (int currentOffset = -1; currentOffset < 2; currentOffset++)
            {
                int testOffset     = currentOffset > 0 ? currentOffset : 0;
                int templateOffset = currentOffset < 0 ? -currentOffset : 0;

                uint HammingDiff  = 0;
                int  maxTestRange = Math.Min(test.Length, templateArray.Length);
                for (int y = 1; y < maxTestRange; y++)
                {
                    HammingDiff += HammingWeight.HWeight((test[y] << testOffset) ^ templateArray[y] << templateOffset);
                }
                if (test.Length > templateArray.Length)
                {
                    for (int y = maxTestRange; y < test.Length; y++)
                    {
                        HammingDiff += HammingWeight.HWeight((test[y] << testOffset));
                    }
                }
                else if (test.Length < templateArray.Length)
                {
                    for (int y = maxTestRange; y < templateArray.Length; y++)
                    {
                        HammingDiff += HammingWeight.HWeight(templateArray[y] << templateOffset);
                    }
                }
                long  total = (Math.Max(test.Length, templateArray.Length) - 1) * Math.Max(test[0], templateArray[0]);
                float newMatch;
                if (total > 10)
                {
                    newMatch = (float)(total - HammingDiff) / total;
                }
                else
                {
                    newMatch = 1 - HammingDiff / 10f;
                }

                if (newMatch > match)
                {
                    match  = newMatch;
                    offset = currentOffset;
                }
            }
        }
예제 #2
0
        public double[] DoOcr(out string OcrText, out string dinoName, out string species, out string ownerName, out string tribeName, out Sex sex, string useImageFilePath = "", bool changeForegroundWindow = true)
        {
            string finishedText = string.Empty;

            dinoName  = string.Empty;
            species   = string.Empty;
            ownerName = string.Empty;
            tribeName = string.Empty;
            sex       = Sex.Unknown;
            double[] finalValues = { 0 };
            if (ocrConfig == null)
            {
                OcrText = "Error: OCR not configured";
                return(finalValues);
            }

            Bitmap screenShotBmp;

            _ocrControl.debugPanel.Controls.Clear();
            _ocrControl.ClearLists();

            if (File.Exists(useImageFilePath))
            {
                screenShotBmp = (Bitmap)Image.FromFile(useImageFilePath);
            }
            else
            {
                // grab screen shot from ark
                screenShotBmp = Win32API.GetScreenshotOfProcess(screenCaptureApplicationName, waitBeforeScreenCapture, true);
            }
            if (screenShotBmp == null)
            {
                OcrText = "Error: no image for OCR. Is ARK running?";
                return(finalValues);
            }

            if (!CheckResolutionSupportedByOcr(screenShotBmp))
            {
                OcrText = "Error while calibrating: The game-resolution is not supported by the currently loaded OCR-configuration.\n"
                          + $"The tested image has a resolution of {screenShotBmp.Width} × {screenShotBmp.Height} px,\n"
                          + $"the resolution of the loaded ocr-config is {ocrConfig.resolutionWidth} × {ocrConfig.resolutionHeight} px.\n\n"
                          + "Load or create a ocr-config file with the resolution of the game to make it work.";
                return(finalValues);
            }

            // TODO resize image according to resize-factor. used for large screenshots
            if (ocrConfig.resize != 1 && ocrConfig.resize > 0)
            {
                Bitmap resized = new Bitmap((int)(ocrConfig.resize * ocrConfig.resolutionWidth), (int)(ocrConfig.resize * ocrConfig.resolutionHeight));
                using (var graphics = Graphics.FromImage(resized))
                {
                    graphics.CompositingMode    = CompositingMode.SourceCopy;
                    graphics.CompositingQuality = CompositingQuality.HighQuality;
                    graphics.InterpolationMode  = InterpolationMode.HighQualityBicubic;
                    graphics.SmoothingMode      = SmoothingMode.HighQuality;
                    graphics.PixelOffsetMode    = PixelOffsetMode.HighQuality;

                    using (var wrapMode = new ImageAttributes())
                    {
                        wrapMode.SetWrapMode(WrapMode.TileFlipXY);
                        graphics.DrawImage(screenShotBmp, new Rectangle(0, 0, resized.Width, resized.Height), 0, 0, screenShotBmp.Width, screenShotBmp.Height, GraphicsUnit.Pixel, wrapMode);
                    }

                    screenShotBmp.Dispose();
                    screenShotBmp = resized;
                }
            }

            if (enableOutput)
            {
                _ocrControl?.DisplayBmpInOcrControl(screenShotBmp);
            }

            finalValues    = new double[ocrConfig.labelRectangles.Length];
            finalValues[8] = -1; // set imprinting to -1 to mark it as unknown and to set a difference to a creature with 0% imprinting.

            if (changeForegroundWindow)
            {
                Win32API.SetForegroundWindow(Application.OpenForms[0].Handle);
            }

            HammingWeight.InitializeBitCounts();

            // TODO OCR performance measurement
            //var sw = new Stopwatch();
            //sw.Start();

            var whiteThreshold = Properties.Settings.Default.OCRWhiteThreshold;

            bool wild = false; // todo: set to true and find out if the creature is wild in the first loop
            int  stI  = -1;

            var labels = (OcrTemplate.OcrLabels[])Enum.GetValues(typeof(OcrTemplate.OcrLabels));

            for (int lbI = 0; lbI < labels.Length; lbI++)
            {
                stI++;
                if (lbI == 8)
                {
                    stI = 8;
                }
                var label = labels[stI];

                switch (label)
                {
                case OcrTemplate.OcrLabels.NameSpecies:
                    if (ocrConfig.RecognitionPatterns.TrainingSettings.SkipName)
                    {
                        dinoName = string.Empty;
                        continue;
                    }
                    break;

                case OcrTemplate.OcrLabels.Tribe:
                    if (ocrConfig.RecognitionPatterns.TrainingSettings.SkipTribe)
                    {
                        tribeName = string.Empty;
                        continue;
                    }

                    break;

                case OcrTemplate.OcrLabels.Owner:
                    if (ocrConfig.RecognitionPatterns.TrainingSettings.SkipOwner)
                    {
                        ownerName = string.Empty;
                        continue;
                    }
                    break;
                }

                Rectangle rec = ocrConfig.labelRectangles[lbI];

                // wild creatures don't have the xp-bar, all stats are moved one row up
                if (wild && stI < 9)
                {
                    rec.Offset(0, ocrConfig.labelRectangles[0].Top - ocrConfig.labelRectangles[1].Top);
                }

                Bitmap testbmp = SubImage(screenShotBmp, rec.X, rec.Y, rec.Width, rec.Height);
                //AddBitmapToDebug(testbmp);

                string statOcr;

                try
                {
                    if (label == OcrTemplate.OcrLabels.NameSpecies)
                    {
                        statOcr = PatternOcr.ReadImageOcr(testbmp, false, whiteThreshold, rec.X, rec.Y, _ocrControl);
                    }
                    else if (label == OcrTemplate.OcrLabels.Level)
                    {
                        statOcr = PatternOcr.ReadImageOcr(testbmp, true, whiteThreshold, rec.X, rec.Y, _ocrControl).Replace(".", ": ");
                    }
                    else if (label == OcrTemplate.OcrLabels.Tribe || label == OcrTemplate.OcrLabels.Owner)
                    {
                        statOcr = PatternOcr.ReadImageOcr(testbmp, false, whiteThreshold, rec.X, rec.Y, _ocrControl);
                    }
                    else
                    {
                        statOcr = PatternOcr.ReadImageOcr(testbmp, true, whiteThreshold, rec.X, rec.Y, _ocrControl).Trim('.'); // statValues are only numbers
                    }
                }
                catch (OperationCanceledException)
                {
                    OcrText = "Canceled";
                    return(finalValues);
                }

                if (statOcr == string.Empty &&
                    (label == OcrTemplate.OcrLabels.Health || label == OcrTemplate.OcrLabels.Imprinting || label == OcrTemplate.OcrLabels.Tribe || label == OcrTemplate.OcrLabels.Owner))
                {
                    if (wild && label == OcrTemplate.OcrLabels.Health)
                    {
                        stI--;
                        wild = false;
                    }
                    continue; // these can be missing, it's fine
                }

                finishedText += $"{(finishedText.Length == 0 ? string.Empty : "\r\n")}{label}:\t{statOcr}";

                // parse the OCR String

                var r = new Regex(@"^[_\/\\]*(.*?)[_\/\\]*$");
                statOcr = r.Replace(statOcr, "$1");

                if (label == OcrTemplate.OcrLabels.NameSpecies)
                {
                    r = new Regex(@".*?([♂♀])?[_.,-\/\\]*([^♂♀]+?)(?:[\(\[]([^\[\(\]\)]+)[\)\]]$|$)");
                }
                else if (label == OcrTemplate.OcrLabels.Owner || label == OcrTemplate.OcrLabels.Tribe)
                {
                    r = new Regex(@"(.*)");
                }
                else if (label == OcrTemplate.OcrLabels.Level)
                {
                    r = new Regex(@".*\D(\d+)");
                }
                else
                {
                    r = new Regex(@"(?:[\d.,%\/]*\/)?(\d+[\.,']?\d?)(%)?\.?"); // only the second numbers is interesting after the current weight is not shown anymore
                }

                MatchCollection mc = r.Matches(statOcr);

                if (mc.Count == 0)
                {
                    if (label == OcrTemplate.OcrLabels.NameSpecies || label == OcrTemplate.OcrLabels.Owner || label == OcrTemplate.OcrLabels.Tribe)
                    {
                        continue;
                    }
                    //if (statName == "Torpor")
                    //{
                    //    // probably it's a wild creature
                    //    // todo
                    //}
                    //else
                    //{
                    finishedText    += $"error reading stat {label}";
                    finalValues[stI] = 0;
                    continue;
                    //}
                }

                if (label == OcrTemplate.OcrLabels.NameSpecies || label == OcrTemplate.OcrLabels.Owner || label == OcrTemplate.OcrLabels.Tribe)
                {
                    if (label == OcrTemplate.OcrLabels.NameSpecies && mc[0].Groups.Count > 0)
                    {
                        if (mc[0].Groups[1].Value == "♀")
                        {
                            sex = Sex.Female;
                        }
                        else if (mc[0].Groups[1].Value == "♂")
                        {
                            sex = Sex.Male;
                        }
                        dinoName = mc[0].Groups[2].Value;
                        species  = mc[0].Groups[3].Value;
                        if (species.Length == 0)
                        {
                            species = dinoName;
                        }

                        // remove non-letter chars
                        r       = new Regex(@"[^a-zA-Z]");
                        species = r.Replace(species, string.Empty);
                        // replace capital I with lower l (common misrecognition)
                        r       = new Regex(@"(?<=[a-z])I(?=[a-z])");
                        species = r.Replace(species, "l");
                        // readd spaces before capital letters
                        //r = new Regex("(?<=[a-z])(?=[A-Z])");
                        //species = r.Replace(species, " ");

                        finishedText += $"\t→ {sex}, {species}";
                        dinoName      = RemoveUnrecognizedCharacters(dinoName);
                        species       = RemoveUnrecognizedCharacters(species);
                    }
                    else if (label == OcrTemplate.OcrLabels.Owner && mc[0].Groups.Count > 0)
                    {
                        ownerName     = mc[0].Groups[0].Value;
                        finishedText += $"\t→ {ownerName}";
                        ownerName     = RemoveUnrecognizedCharacters(ownerName);
                    }
                    else if (label == OcrTemplate.OcrLabels.Tribe && mc[0].Groups.Count > 0)
                    {
                        tribeName     = mc[0].Groups[0].Value;
                        finishedText += $"\t→ {tribeName}";
                        tribeName     = RemoveUnrecognizedCharacters(tribeName);
                    }
                    continue;
                }

                if (mc[0].Groups.Count > 2 && mc[0].Groups[2].Value == "%" && label == OcrTemplate.OcrLabels.Weight)
                {
                    // first stat with a '%' is damage, if oxygen is missing, shift all stats by one
                    finalValues[4] = finalValues[3]; // shift food to weight
                    finalValues[3] = finalValues[2]; // shift oxygen to food
                    finalValues[2] = 0;              // set oxygen (which wasn't there) to 0
                    stI++;
                }

                var splitRes = statOcr.Split('/', ',', ':');
                var ocrValue = splitRes[splitRes.Length - 1] == "%" ? splitRes[splitRes.Length - 2] : splitRes[splitRes.Length - 1];

                ocrValue = PatternOcr.RemoveNonNumeric(ocrValue);

                double.TryParse(ocrValue, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.GetCultureInfo("en-US"), out double v); // common substitutions: comma and apostrophe to dot,

                finishedText += label == OcrTemplate.OcrLabels.Level ? $"\t→ {v:F0}" : $"\t→ {v:F1}";

                // TODO: test here that the read stat name corresponds to the stat supposed to be read
                finalValues[stI] = v;

                string RemoveUnrecognizedCharacters(string s) => s.Replace("�", string.Empty);
            }

            OcrText = finishedText;

            // TODO OCR performance output
            //sw.Stop();
            //Debug.WriteLine($"OCR took {sw.ElapsedMilliseconds} ms.");

            // TODO reorder stats to match 12-stats-order

            return(finalValues);
        }