适配新版本激活石化古树识别树脂 (#2174)

This commit is contained in:
FishmanTheMurloc
2025-09-11 23:31:02 +08:00
committed by GitHub
parent 1ad31c64d2
commit feed609fb1
10 changed files with 101 additions and 88 deletions

View File

@@ -1102,7 +1102,7 @@ public class AutoDomainTask : ISoloTask
{
// 自动刷干树脂
// 识别树脂状况
var resinStatus = ResinStatus.RecogniseFromRegion(ra3);
var resinStatus = ResinStatus.RecogniseFromRegion(ra3, TaskContext.Instance().SystemInfo, OcrFactory.Paddle);
resinStatus.Print(Logger);
if (resinStatus is { CondensedResinCount: <= 0, OriginalResinCount: < 20 })

View File

@@ -1,10 +1,11 @@
using System;
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.GameTask.AutoFight.Assets;
using BetterGenshinImpact.GameTask.Model;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.Helpers;
using Microsoft.Extensions.Logging;
using OpenCvSharp;
using System;
namespace BetterGenshinImpact.GameTask.AutoDomain.Model;
@@ -18,35 +19,43 @@ public class ResinStatus
/// <summary>
/// 脆弱树脂60
/// </summary>
public int FragileResinCount { get; set; } = 0;
public int FragileResinCount { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
/// <summary>
/// 浓缩树脂(40
/// 浓缩树脂(60
/// </summary>
public int CondensedResinCount { get; set; } = 0;
/// <summary>
/// 须臾树脂60壶内购买
/// </summary>
public int TransientResinCount { get; set; } = 0;
public int TransientResinCount { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public static ResinStatus RecogniseFromRegion(ImageRegion region)
public static ResinStatus RecogniseFromRegion(ImageRegion region, ISystemInfo systemInfo, IOcrService ocrService)
{
var status = new ResinStatus();
// 1. 原粹树脂 起点 w-(256+100) ~ w-256
var captureArea = TaskContext.Instance().SystemInfo.ScaleMax1080PCaptureRect;
var assetScale = TaskContext.Instance().SystemInfo.AssetScale;
var originalResinTopIconRa = AutoFightAssets.Instance.OriginalResinTopIconRa;
var originalResinRes = region.Find(originalResinTopIconRa);
// 1. 原粹树脂
var assetScale = systemInfo.AssetScale;
var originalResinTopIconRa = new RecognitionObject
{
Name = "OriginalResinTopIcon",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage("AutoFight", "original_resin_top_icon.png", systemInfo),
DrawOnWindow = false
}.InitTemplate();
using ImageRegion crop1 = region.DeriveCrop(new Rect((int)(1300 * assetScale), (int)(25 * assetScale), (int)(160 * assetScale), (int)(50 * assetScale))); // 数字位数的不同导致了水平方向上宽泛的区域
//Cv2.ImShow("test", crop1.SrcMat);
//Cv2.WaitKey();
var originalResinRes = crop1.Find(originalResinTopIconRa);
if (originalResinRes.IsEmpty())
{
throw new Exception("未找到原粹树脂图标");
}
var originalResinCountRect = new Rect(originalResinRes.Right + 30, (int)(37 * assetScale),
captureArea.Width - (originalResinRes.Right + 30) - (int)(190 * assetScale), (int)(21 * assetScale));
string cnt1 = OcrFactory.Paddle.OcrWithoutDetector(region.DeriveCrop(originalResinCountRect).SrcMat);
var originalResinCountRect = new Rect(crop1.X + originalResinRes.Right + (int)(25 * assetScale), (int)(37 * assetScale), (int)(110 * assetScale)/* 考虑最长的“200/200” */, (int)(24 * assetScale));
using ImageRegion originalResinCountRegion = region.DeriveCrop(originalResinCountRect);
string cnt1 = ocrService.OcrWithoutDetector(originalResinCountRegion.SrcMat);
var match = System.Text.RegularExpressions.Regex.Match(cnt1, @"(\d+)\s*[/17]\s*(2|20|200)");
if (match.Success)
{
@@ -55,12 +64,22 @@ public class ResinStatus
}
// 2. 浓缩树脂
var condensedResinRes = region.Find(AutoFightAssets.Instance.CondensedResinTopIconRa);
int startX = crop1.X + originalResinRes.Left - (int)(180 * assetScale); // 从原粹树脂图标位置起算
var condensedResinTopIconRa = new RecognitionObject
{
Name = "CondensedResinTopIcon",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage("AutoFight", "condensed_resin_top_icon.png", systemInfo),
DrawOnWindow = false
}.InitTemplate();
using ImageRegion crop2 = region.DeriveCrop(new Rect(startX, (int)(25 * assetScale), (int)(90 * assetScale), (int)(50 * assetScale)));
var condensedResinRes = crop2.Find(condensedResinTopIconRa);
if (condensedResinRes.IsExist())
{
// 找出 icon 的位置 + 25 ~ icon 的位置+45 就是浓缩树脂的数字数字宽20
var condensedResinCountRect = new Rect(condensedResinRes.Right + (int)(25 * assetScale), condensedResinRes.Y, (int)(20 * assetScale), condensedResinRes.Height);
string cnt40 = OcrFactory.Paddle.OcrWithoutDetector(region.DeriveCrop(condensedResinCountRect).SrcMat);
var condensedResinCountRect = new Rect(crop2.X + condensedResinRes.Right + (int)(20 * assetScale), (int)(37 * assetScale), (int)(70 * assetScale), (int)(24 * assetScale));
using ImageRegion countRegion = region.DeriveCrop(condensedResinCountRect);
string cnt40 = ocrService.OcrWithoutDetector(countRegion.SrcMat);
status.CondensedResinCount = StringUtils.TryExtractPositiveInt(cnt40, 0);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

View File

@@ -1,4 +1,4 @@
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.GameTask.Model;
using OpenCvSharp;
using System.Collections.Generic;
@@ -23,15 +23,9 @@ public class AutoFightAssets : BaseAssets<AutoFightAssets>
public RecognitionObject ArtifactAreaRa;
public RecognitionObject ExitRa;
public RecognitionObject ClickAnyCloseTipRa;
public RecognitionObject UseCondensedResinRa;
// 树脂状态
public RecognitionObject CondensedResinCountRa;
public RecognitionObject FragileResinCountRa;
// 自动秘境
// public RecognitionObject LockIconRa; // 锁定辅助图标
public RecognitionObject CondensedResinTopIconRa;
public RecognitionObject OriginalResinTopIconRa;
public Dictionary<string, string> AvatarCostumeMap;
@@ -44,7 +38,7 @@ public class AutoFightAssets : BaseAssets<AutoFightAssets>
// 小道具位置
public Rect GadgetRect;
public RecognitionObject AbnormalIconRa;
private AutoFightAssets()
@@ -59,7 +53,7 @@ public class AutoFightAssets : BaseAssets<AutoFightAssets>
(int)(41 * AssetScale), (int)(18 * AssetScale));
QRect = new Rect(CaptureRect.Width - (int)(157 * AssetScale), CaptureRect.Height - (int)(165 * AssetScale),
(int)(110 * AssetScale), (int)(110 * AssetScale));
ZCooldownRect = new Rect(CaptureRect.Width - (int)(130 * AssetScale), (int)(814 * AssetScale),
ZCooldownRect = new Rect(CaptureRect.Width - (int)(130 * AssetScale), (int)(814 * AssetScale),
(int)(60 * AssetScale), (int)(24 * AssetScale));
// 小道具位置 1920-133,800,60,50
GadgetRect = new Rect(CaptureRect.Width - (int)(133 * AssetScale), (int)(800 * AssetScale),
@@ -205,7 +199,7 @@ public class AutoFightAssets : BaseAssets<AutoFightAssets>
Name = "ArtifactArea",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage("AutoFight", "artifact_flower_logo.png"),
RegionOfInterest = new Rect(CaptureRect.Width / 2,0,CaptureRect.Width / 2, CaptureRect.Height),
RegionOfInterest = new Rect(CaptureRect.Width / 2, 0, CaptureRect.Width / 2, CaptureRect.Height),
DrawOnWindow = false
}.InitTemplate();
@@ -219,15 +213,6 @@ public class AutoFightAssets : BaseAssets<AutoFightAssets>
DrawOnWindow = false
}.InitTemplate();
UseCondensedResinRa = new RecognitionObject
{
Name = "UseCondensedResin",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage("AutoFight", "use_condensed_resin.png"),
RegionOfInterest = new Rect(0, CaptureRect.Height / 2, CaptureRect.Width / 2, CaptureRect.Height / 2),
DrawOnWindow = false
}.InitTemplate();
ExitRa = new RecognitionObject
{
Name = "Exit",
@@ -237,23 +222,6 @@ public class AutoFightAssets : BaseAssets<AutoFightAssets>
DrawOnWindow = false
}.InitTemplate();
CondensedResinCountRa = new RecognitionObject
{
Name = "CondensedResinCount",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage("AutoFight", "condensed_resin_count.png"),
RegionOfInterest = new Rect(CaptureRect.Width / 2, CaptureRect.Height / 3 * 2, CaptureRect.Width / 2, CaptureRect.Height / 3),
DrawOnWindow = false
}.InitTemplate();
FragileResinCountRa = new RecognitionObject
{
Name = "FragileResinCount",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage("AutoFight", "fragile_resin_count.png"),
RegionOfInterest = new Rect(CaptureRect.Width / 2, CaptureRect.Height / 3 * 2, CaptureRect.Width / 2, CaptureRect.Height / 3),
DrawOnWindow = false
}.InitTemplate();
// 自动秘境
// LockIconRa = new RecognitionObject
// {
@@ -263,30 +231,14 @@ public class AutoFightAssets : BaseAssets<AutoFightAssets>
// RegionOfInterest = new Rect(CaptureRect.Width - (int)(215 * AssetScale), 0, (int)(215 * AssetScale), (int)(80 * AssetScale)),
// DrawOnWindow = false
// }.InitTemplate();
CondensedResinTopIconRa = new RecognitionObject
{
Name = "CondensedResinTopIcon",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage("AutoFight", "condensed_resin_top_icon.png"),
RegionOfInterest = new Rect((int)(1270 * AssetScale), (int)(25 * AssetScale), (int)(520 * AssetScale), (int)(45 * AssetScale)),
DrawOnWindow = false
}.InitTemplate();
OriginalResinTopIconRa = new RecognitionObject
{
Name = "OriginalResinTopIcon",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage("AutoFight", "original_resin_top_icon.png"),
RegionOfInterest = new Rect(CaptureRect.Width - (int)(450 * AssetScale), (int)(25 * AssetScale), (int)(265 * AssetScale), (int)(45 * AssetScale)),
DrawOnWindow = false
}.InitTemplate();
AbnormalIconRa = new RecognitionObject
{
Name = "AbnormalIcon",
RecognitionType = RecognitionTypes.TemplateMatch,
TemplateImageMat = GameTaskManager.LoadAssetImage("AutoFight", "abnormal_icon.png"),
RegionOfInterest = new Rect(0,(int)(CaptureRect.Height*0.08), (int)(CaptureRect.Width*0.04), (int)(CaptureRect.Height*0.07)),
RegionOfInterest = new Rect(0, (int)(CaptureRect.Height * 0.08), (int)(CaptureRect.Width * 0.04), (int)(CaptureRect.Height * 0.07)),
DrawOnWindow = false
}.InitTemplate();
}
}

View File

@@ -1,8 +1,8 @@
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.GameTask.Model;
using OpenCvSharp;
namespace BetterGenshinImpact.GameTask.AutoFishing.Assets;
namespace BetterGenshinImpact.GameTask.AutoOpenChest.Assets;
public class AutoOpenChestAssets : BaseAssets<AutoOpenChestAssets>
{
@@ -14,7 +14,7 @@ public class AutoOpenChestAssets : BaseAssets<AutoOpenChestAssets>
#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑添加 "required" 修饰符或声明为可为 null。
private AutoOpenChestAssets() : base()
{
Initialization(this.systemInfo);
Initialization(systemInfo);
}
protected AutoOpenChestAssets(ISystemInfo systemInfo) : base(systemInfo)

View File

@@ -1,8 +1,6 @@
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask;
using BetterGenshinImpact.GameTask.AutoFishing.Assets;
using BetterGenshinImpact.GameTask.Common.BgiVision;
using BetterGenshinImpact.GameTask.AutoOpenChest.Assets;
using BetterGenshinImpact.GameTask.Model.Area;
using Microsoft.Extensions.Logging;
using System;
@@ -11,8 +9,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using static BetterGenshinImpact.GameTask.Common.TaskControl;
using static TorchSharp.torch.distributions.constraints;
namespace GameTask.AutoOpenChest;
namespace BetterGenshinImpact.GameTask.AutoOpenChest;
/// <summary>
/// 识别宝箱图标,走向宝箱并开启。
@@ -72,7 +69,7 @@ public class AutoOpenChestTask : ISoloTask
}
else
{
var gap = (ra.Width / 2) - chestIcon.X;
var gap = ra.Width / 2 - chestIcon.X;
int rate = 2;
Simulation.SendInput.Mouse.MoveMouseBy(gap / rate, 0);
}

View File

@@ -1,5 +1,6 @@
using BetterGenshinImpact.Core.BgiVision;
using BetterGenshinImpact.Core.Recognition;
using BetterGenshinImpact.Core.Recognition.OCR;
using BetterGenshinImpact.Core.Simulator;
using BetterGenshinImpact.Core.Simulator.Extensions;
using BetterGenshinImpact.GameTask.AutoArtifactSalvage;
@@ -398,7 +399,7 @@ public class AutoStygianOnslaughtTask : ISoloTask
{
// 自动刷干树脂
// 识别树脂状况
var resinStatus = ResinStatus.RecogniseFromRegion(ra3);
var resinStatus = ResinStatus.RecogniseFromRegion(ra3, TaskContext.Instance().SystemInfo, OcrFactory.Paddle);
resinStatus.Print(_logger);
if (resinStatus is { CondensedResinCount: <= 0, OriginalResinCount: < 20 })

View File

@@ -0,0 +1,44 @@
using BetterGenshinImpact.GameTask.AutoDomain.Model;
using BetterGenshinImpact.GameTask.Model.Area;
using BetterGenshinImpact.GameTask.Model.Area.Converter;
using BetterGenshinImpact.UnitTest.CoreTests.RecognitionTests.OCRTests;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoDomainTests
{
[Collection("Init Collection")]
public class ResinStatusTests
{
private readonly PaddleFixture paddle;
public ResinStatusTests(PaddleFixture paddle)
{
this.paddle = paddle;
}
[Theory]
[InlineData(@"AutoDomain\SelectRevitalization.png", 21, 0, 2, 1)]
/// <summary>
/// 测试识别四种树脂数量,数量应正确
/// </summary>
public void RecogniseFromRegion_ResinStatusShouldBeRight(string screenshot1080p, int originalResinCount, int fragileResinCount, int condensedResinCount, int transientResinCount)
{
//
Mat mat = new Mat(@$"..\..\..\Assets\{screenshot1080p}");
var imageRegion = new ImageRegion(mat, 0, 0, converter: new ScaleConverter(1d));
FakeSystemInfo systemInfo = new FakeSystemInfo(new Vanara.PInvoke.RECT(0, 0, mat.Width, mat.Height), 1);
//
var result = ResinStatus.RecogniseFromRegion(imageRegion, systemInfo, this.paddle.Get()); // todoSystem.Exception : 未找到原粹树脂图标
//
Assert.Equal(originalResinCount, result.OriginalResinCount);
Assert.Equal(condensedResinCount, result.CondensedResinCount);
}
}
}

View File

@@ -1,4 +1,4 @@
using BetterGenshinImpact.GameTask.Model;
using BetterGenshinImpact.GameTask.Model;
using BetterGenshinImpact.GameTask.Model.Area;
using OpenCvSharp;
using System;
@@ -9,7 +9,7 @@ using System.Text;
using System.Threading.Tasks;
using Vanara.PInvoke;
namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
namespace BetterGenshinImpact.UnitTest.GameTaskTests
{
internal class FakeSystemInfo : ISystemInfo
{
@@ -29,7 +29,7 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.AutoFishingTests
public RECT GameScreenSize { get; }
public double AssetScale { get; }
public double AssetScale { get; } = 1;
public double ZoomOutMax1080PRatio { get; }

View File

@@ -27,8 +27,8 @@ namespace BetterGenshinImpact.UnitTest.GameTaskTests.GetGridIconsTests
yield return new object[] { @"GetGridIcons\FoodGrid.png", 8, true, new[] { ("苹果", 0), ("日落果", 0), ("星蕈", 0), ("泡泡桔", 0), ("烛伞蘑菇", 0), ("宝石闪闪", 4), ("咚咚", 4), ("枫达", 2),
("雾凇秋分", 4), ("蒙德土豆饼", 3), /*("爪爪土豆饼", 3), ("北地苹果焖肉", 3), ("四方和平", 3), ("盛世太平", 3), ("三彩团子", 3), ("夏祭游鱼", 3)*/} };
// todo 爪爪土豆饼被吃掉了没进训练集。。。
yield return new object[] { @"GetGridIcons\FoodGrid_Attack.png", 8, false, new[] { ("果果软糖", 3), ("方块戏法", 3), ("「缥雨一滴」", 3), ("繁弦急管", 3), ("繁弦急管", 3), ("「簇火赞歌」", 3), ("轻策家常菜", 3), ("冒险家蛋堡", 3), ("冒险家蛋堡", 3), ("测绘员蛋堡", 3), ("串串三味", 3), ("连心面", 3), ("摩拉急速来", 3), ("双果清露", 3), ("双果清露", 3), ("双果清露", 3), ("四喜圆满", 3), ("祝圣交响乐", 3), ("满足沙拉", 2), ("满足沙拉", 2), ("至高的智慧(生活)", 2), ("摇·滚·鸡!", 2), ("岩港三鲜", 2), ("鲜鱼萝卜", 2), ("炸萝卜丸子", 2), ("炸萝卜丸子", 2), ("杏仁豆腐", 2), ("杏仁豆腐", 2), ("「美梦」", 2), ("凉拌薄荷", 2), ("炸肉排三明治", 2), ("唯一的真相", 2) } };
yield return new object[] { @"GetGridIcons\MaterialGrid_TreesAndBaits.png", 8, false, new[] { ("松木", 1), ("却砂木", 1), ("竹节", 1), ("垂香木", 1), ("杉木", 1), ("梦见木", 1), ("枫木", 1), ("孔雀木", 1), ("御伽木", 1), ("辉木", 1), ("业果木", 1), ("证悟木", 1), ("木", 1), ("黑铜号角", 1), ("悬铃木", 1), ("白梣木", 1), ("香柏木", 1), ("白栗栎木", 1), ("灰灰楼林木", 1), ("燃爆木", 1), ("布匹", 0), ("红色染料", 0), ("黄色染料", 0), ("蓝色染料", 0), ("果酿饵", 2), ("赤糜饵", 2), ("蠕虫假饵", 2), ("飞蝇假饵", 2), ("甘露饵", 2), ("酸桔饵", 2), ("维护机关频闪诱饵", 2), ("澄晶果粒饵", 2) } };
yield return new object[] { @"GetGridIcons\FoodGrid_Attack.png", 8, false, new[] { ("果果软糖", 3), ("方块戏法", 3), ("「缥雨一滴」", 3), ("繁弦急管", 3), ("繁弦急管", 3), ("「簇火赞歌」", 3), ("轻策家常菜", 3), ("冒险家蛋堡", 3), ("冒险家蛋堡", 3), ("测绘员蛋堡", 3), ("串串三味", 3), ("连心面", 3), ("摩拉急速来", 3), ("双果清露", 3), ("双果清露", 3), ("双果清露", 3), ("四喜圆满", 3), ("祝圣交响乐", 3), ("满足沙拉", 2), ("满足沙拉", 2), ("至高的智慧(生活)", 2), ("摇·滚·鸡!", 2), ("岩港三鲜", 2), ("鲜鱼萝卜", 2), ("炸萝卜丸子", 2), ("炸萝卜丸子", 2), ("杏仁豆腐", 2), ("杏仁豆腐", 2), ("「美梦」", 2), ("凉拌薄荷", 2), ("炸肉排三明治", 2), ("唯一的真相", 2) } };
yield return new object[] { @"GetGridIcons\MaterialGrid_TreesAndBaits.png", 8, false, new[] { ("松木", 1), ("却砂木", 1), ("竹节", 1), ("垂香木", 1), ("杉木", 1), ("梦见木", 1), ("枫木", 1), ("孔雀木", 1), ("御伽木", 1), ("辉木", 1), ("业果木", 1), ("证悟木", 1), ("刺葵木", 1), ("柽木", 1), ("悬铃木", 1), ("白梣木", 1), ("香柏木", 1), ("白栗栎木", 1), ("灰灰楼林木", 1), ("燃爆木", 1), ("布匹", 0), ("红色染料", 0), ("黄色染料", 0), ("蓝色染料", 0), ("果酿饵", 2), ("赤糜饵", 2), ("蠕虫假饵", 2), ("飞蝇假饵", 2), ("甘露饵", 2), ("酸桔饵", 2), ("维护机关频闪诱饵", 2), ("澄晶果粒饵", 2) } };
// string.Join(", ",result.Select(s=>$"(\"{s.Item1}\", {s.Item2})"))
}