调整浓缩树脂识别范围,解决V4模型无法准确识别浓缩树脂个数的问题 #2185

This commit is contained in:
辉鸭蛋
2025-09-24 01:09:40 +08:00
parent e779aab20b
commit dc04c22473
6 changed files with 63 additions and 17 deletions

View File

@@ -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();

View File

@@ -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)
{

View File

@@ -11,12 +11,17 @@ public static class TextRectExtractor
/// 从图片中提取文字范围(假定文字从最左边贴边开始,向右连续)
/// 结果矩形固定 x=0,y=0,h=原图高度,只计算连续文字宽度。
/// </summary>
public static Rect GetTextBoundingRect(Mat textMat, out Mat bin)
/// <param name="textMat">文字图片</param>
/// <param name="min">二值化阈值</param>
/// <param name="max">二值化阈值</param>
/// <returns></returns>
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;
}
/// <summary>
/// 投影, 获取连续文字的边界矩形
/// </summary>
/// <param name="textMat"></param>
/// <param name="bin"></param>
/// <param name="maxGap">允许的最大连续空列数</param>
/// <returns></returns>
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;
}
}
}

View File

@@ -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<BgiOnnxFactory>()),
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<BgiOnnxFactory>()),
PaddleOcrService.PaddleOcrModelType.FromCultureInfoV4(new CultureInfo(name)) ?? PaddleOcrService.PaddleOcrModelType.V4);
PaddleOcrService.PaddleOcrModelType.FromCultureInfoV4(new CultureInfo(cultureInfoName)) ?? PaddleOcrService.PaddleOcrModelType.V4);
}
else
{

View File

@@ -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")]
/// <summary>
/// 测试识别四种树脂数量,数量应正确
/// </summary>
[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")
{
//