From dc04c224739f61ba46a07060487f1da8ee1c42c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=BE=89=E9=B8=AD=E8=9B=8B?= Date: Wed, 24 Sep 2025 01:09:40 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=B5=93=E7=BC=A9=E6=A0=91?= =?UTF-8?q?=E8=84=82=E8=AF=86=E5=88=AB=E8=8C=83=E5=9B=B4=EF=BC=8C=E8=A7=A3?= =?UTF-8?q?=E5=86=B3V4=E6=A8=A1=E5=9E=8B=E6=97=A0=E6=B3=95=E5=87=86?= =?UTF-8?q?=E7=A1=AE=E8=AF=86=E5=88=AB=E6=B5=93=E7=BC=A9=E6=A0=91=E8=84=82?= =?UTF-8?q?=E4=B8=AA=E6=95=B0=E7=9A=84=E9=97=AE=E9=A2=98=20#2185?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GameTask/AutoDomain/Model/ResinStatus.cs | 3 +- .../GameTask/AutoPick/AutoPickTrigger.cs | 2 +- .../GameTask/AutoPick/TextRectExtractor.cs | 59 ++++++++++++++++--- Test/BetterGenshinImpact.UnitTest/Assets | 2 +- .../OCRTests/PaddleFixture.cs | 6 +- .../AutoDomainTests/ResinStatusTests.cs | 8 ++- 6 files changed, 63 insertions(+), 17 deletions(-) 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") { //