diff --git a/BetterGenshinImpact/GameTask/AutoDomain/Model/ResinStatus.cs b/BetterGenshinImpact/GameTask/AutoDomain/Model/ResinStatus.cs index 54bf7bea..2f5c27da 100644 --- a/BetterGenshinImpact/GameTask/AutoDomain/Model/ResinStatus.cs +++ b/BetterGenshinImpact/GameTask/AutoDomain/Model/ResinStatus.cs @@ -6,6 +6,7 @@ using BetterGenshinImpact.Helpers; using Microsoft.Extensions.Logging; using OpenCvSharp; using System; +using BetterGenshinImpact.GameTask.AutoPick; namespace BetterGenshinImpact.GameTask.AutoDomain.Model; @@ -77,7 +78,7 @@ public class ResinStatus if (condensedResinRes.IsExist()) { // 找出 icon 的位置 + 25 ~ icon 的位置+45 就是浓缩树脂的数字,数字宽20 - var condensedResinCountRect = new Rect(crop2.X + condensedResinRes.Right + (int)(20 * assetScale), (int)(37 * assetScale), (int)(70 * assetScale), (int)(24 * assetScale)); + var condensedResinCountRect = new Rect(crop2.X + condensedResinRes.Right + (int)(20 * assetScale), crop2.Y + condensedResinRes.Y, (int)(30 * assetScale), condensedResinRes.Height); using ImageRegion countRegion = region.DeriveCrop(condensedResinCountRect); using Mat threshold = countRegion.CacheGreyMat.Threshold(180, 255, ThresholdTypes.Binary); using Mat bitwiseNot = new Mat(); diff --git a/BetterGenshinImpact/GameTask/AutoPick/AutoPickTrigger.cs b/BetterGenshinImpact/GameTask/AutoPick/AutoPickTrigger.cs index f77cfd72..443e86f7 100644 --- a/BetterGenshinImpact/GameTask/AutoPick/AutoPickTrigger.cs +++ b/BetterGenshinImpact/GameTask/AutoPick/AutoPickTrigger.cs @@ -246,7 +246,7 @@ public partial class AutoPickTrigger : ITaskTrigger else { var textMat = new Mat(content.CaptureRectArea.SrcMat, textRect); - var boundingRect = TextRectExtractor.GetTextBoundingRect(textMat, out var bin); + var boundingRect = TextRectExtractor.GetTextBoundingRect(textMat); // 如果找到有效区域 if (boundingRect.X <20 && boundingRect.Width > 5 && boundingRect.Height > 5) { diff --git a/BetterGenshinImpact/GameTask/AutoPick/TextRectExtractor.cs b/BetterGenshinImpact/GameTask/AutoPick/TextRectExtractor.cs index ac15db89..3b201af0 100644 --- a/BetterGenshinImpact/GameTask/AutoPick/TextRectExtractor.cs +++ b/BetterGenshinImpact/GameTask/AutoPick/TextRectExtractor.cs @@ -11,12 +11,17 @@ public static class TextRectExtractor /// 从图片中提取文字范围(假定文字从最左边贴边开始,向右连续) /// 结果矩形固定 x=0,y=0,h=原图高度,只计算连续文字宽度。 /// - public static Rect GetTextBoundingRect(Mat textMat, out Mat bin) + /// 文字图片 + /// 二值化阈值 + /// 二值化阈值 + /// + public static Rect GetTextBoundingRect(Mat textMat, double min = 160, double max = 255) { // 转换为灰度图 - Mat gray = new Mat(); + Mat gray; if (textMat.Channels() == 3) { + gray = new Mat(); Cv2.CvtColor(textMat, gray, ColorConversionCodes.BGR2GRAY); } else @@ -25,8 +30,8 @@ public static class TextRectExtractor } // 使用阈值160进行二值化处理 - bin = new Mat(); - Cv2.Threshold(gray, bin, 160, 255, ThresholdTypes.Binary); + using var bin = new Mat(); + Cv2.Threshold(gray, bin, min, max, ThresholdTypes.Binary); // 形态学操作:先腐蚀后膨胀,去除噪点并保持文字完整 Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3)); @@ -37,7 +42,46 @@ public static class TextRectExtractor return ProjectionRect(textMat, bin); } - private static Rect ProjectionRect(Mat textMat, Mat bin) + public static Rect GetNumberBoundingRect(Mat textMat, double min = 180, double max = 255) + { + // 转换为灰度图 + Mat gray; + if (textMat.Channels() == 3) + { + gray = new Mat(); + Cv2.CvtColor(textMat, gray, ColorConversionCodes.BGR2GRAY); + } + else + { + gray = textMat.Clone(); + } + + // 使用阈值160进行二值化处理 + using var bin = new Mat(); + Cv2.Threshold(gray, bin, min, max, ThresholdTypes.Binary); + + // 形态学操作:直接膨胀 + Cv2.Dilate(bin, bin, new Mat(), iterations: 2); + gray.Dispose(); + var rect = ProjectionRect(textMat, bin); + // 数字后面加点宽度 + if (rect != new Rect()) + { + var newWidth = rect.Width + 10; + rect.Width = newWidth > textMat.Width ? textMat.Width : newWidth; + } + + return rect; + } + + /// + /// 投影, 获取连续文字的边界矩形 + /// + /// + /// + /// 允许的最大连续空列数 + /// + public static Rect ProjectionRect(Mat textMat, Mat bin, int maxGap = 30) { // 投影:对行做 ReduceSum,得到 1 x width 的列和 using var projection = new Mat(); @@ -45,7 +89,6 @@ public static class TextRectExtractor int width = projection.Cols; projection.GetArray(out int[] colSums); - int maxGap = 30; // 允许的最大连续空列数 int gapCount = 0; int lastNonEmpty = -1; @@ -66,7 +109,7 @@ public static class TextRectExtractor } } } - + if (lastNonEmpty == -1) { // 没有检测到文字 @@ -76,4 +119,4 @@ public static class TextRectExtractor Rect boundingRect = new Rect(0, 0, lastNonEmpty, textMat.Height); return boundingRect; } -} +} \ No newline at end of file diff --git a/Test/BetterGenshinImpact.UnitTest/Assets b/Test/BetterGenshinImpact.UnitTest/Assets index 3df550ad..e6446663 160000 --- a/Test/BetterGenshinImpact.UnitTest/Assets +++ b/Test/BetterGenshinImpact.UnitTest/Assets @@ -1 +1 @@ -Subproject commit 3df550ad83bd7c2f512d2fa8795f6284c13ddd96 +Subproject commit e644666398474a60e0d8a245dcf269c5c52d9dea diff --git a/Test/BetterGenshinImpact.UnitTest/CoreTests/RecognitionTests/OCRTests/PaddleFixture.cs b/Test/BetterGenshinImpact.UnitTest/CoreTests/RecognitionTests/OCRTests/PaddleFixture.cs index fd661a16..ec61fe32 100644 --- a/Test/BetterGenshinImpact.UnitTest/CoreTests/RecognitionTests/OCRTests/PaddleFixture.cs +++ b/Test/BetterGenshinImpact.UnitTest/CoreTests/RecognitionTests/OCRTests/PaddleFixture.cs @@ -11,19 +11,19 @@ namespace BetterGenshinImpact.UnitTest.CoreTests.RecognitionTests.OCRTests public PaddleOcrService Get(string cultureInfoName = "zh-Hans", string version = "V5") { - return _paddleOcrServices.GetOrAdd(cultureInfoName, name => + return _paddleOcrServices.GetOrAdd(cultureInfoName + "_" + version, _ => { lock (_paddleOcrServices) { if (version == "V5") { return new PaddleOcrService(new BgiOnnxFactory(new FakeLogger()), - PaddleOcrService.PaddleOcrModelType.FromCultureInfo(new CultureInfo(name)) ?? PaddleOcrService.PaddleOcrModelType.V5); + PaddleOcrService.PaddleOcrModelType.FromCultureInfo(new CultureInfo(cultureInfoName)) ?? PaddleOcrService.PaddleOcrModelType.V5); } else if (version == "V4") { return new PaddleOcrService(new BgiOnnxFactory(new FakeLogger()), - PaddleOcrService.PaddleOcrModelType.FromCultureInfoV4(new CultureInfo(name)) ?? PaddleOcrService.PaddleOcrModelType.V4); + PaddleOcrService.PaddleOcrModelType.FromCultureInfoV4(new CultureInfo(cultureInfoName)) ?? PaddleOcrService.PaddleOcrModelType.V4); } else { diff --git a/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoDomainTests/ResinStatusTests.cs b/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoDomainTests/ResinStatusTests.cs index 06d49f58..d44f7109 100644 --- a/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoDomainTests/ResinStatusTests.cs +++ b/Test/BetterGenshinImpact.UnitTest/GameTaskTests/AutoDomainTests/ResinStatusTests.cs @@ -21,12 +21,14 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoDomainTests this.paddle = paddle; } - [Theory] - [InlineData(@"AutoDomain\SelectRevitalization.png", 21, 0, 2, 1)] - [InlineData(@"AutoDomain\SelectRevitalizationOcrV4.png", 11, 0, 1, 149, "V4")] /// /// 测试识别四种树脂数量,数量应正确 /// + [Theory] + [InlineData(@"AutoDomain\SelectRevitalization.png", 21, 0, 2, 1)] + [InlineData(@"AutoDomain\SelectRevitalizationOcrV4.png", 11, 0, 1, 149, "V4")] + [InlineData(@"AutoDomain\SelectRevitalizationOcrV4_NSSZ1.png", 20, 1, 1, 0, "V4")] + [InlineData(@"AutoDomain\SelectRevitalizationOcrV4_NSSZ1.png", 20, 1, 1, 0)] public void RecogniseFromRegion_ResinStatusShouldBeRight(string screenshot1080p, int originalResinCount, int fragileResinCount, int condensedResinCount, int transientResinCount, string ocrVersion = "V5") { //