截取物品图标 实现使用星星作为名称后缀 (#1938)

This commit is contained in:
FishmanTheMurloc
2025-07-28 13:39:47 +08:00
committed by GitHub
parent 832958e794
commit 120b80cb70
4 changed files with 78 additions and 24 deletions

View File

@@ -14,8 +14,6 @@ using System.Linq;
using BetterGenshinImpact.GameTask.Common.Job;
using BetterGenshinImpact.Core.Recognition.OCR;
using System.IO;
using System.Drawing;
using static Vanara.PInvoke.Gdi32;
using OpenCvSharp.Extensions;
using BetterGenshinImpact.GameTask.Model.GameUI;
@@ -27,8 +25,6 @@ namespace BetterGenshinImpact.GameTask.GetGridIcons;
public class GetGridIconsTask : ISoloTask
{
private readonly ILogger logger = App.GetLogger<GetGridIconsTask>();
private readonly InputSimulator input = Simulation.SendInput;
private readonly ReturnMainUiTask _returnMainUiTask = new();
private CancellationToken ct;
@@ -38,9 +34,12 @@ public class GetGridIconsTask : ISoloTask
private readonly GridScreenName gridScreenName;
public GetGridIconsTask(GridScreenName gridScreenName, int? maxNumToGet = null)
private readonly bool starAsSuffix;
public GetGridIconsTask(GridScreenName gridScreenName, bool starAsSuffix, int? maxNumToGet = null)
{
this.gridScreenName = gridScreenName;
this.starAsSuffix = starAsSuffix;
this.maxNumToGet = maxNumToGet;
IStringLocalizer<GetGridIconsTask> stringLocalizer = App.GetService<IStringLocalizer<GetGridIconsTask>>() ?? throw new NullReferenceException();
CultureInfo cultureInfo = new CultureInfo(TaskContext.Instance().Config.OtherConfig.GameCultureInfoName);
@@ -60,7 +59,7 @@ public class GetGridIconsTask : ISoloTask
Directory.CreateDirectory(directory);
GridScreen gridScreen = new GridScreen(gridRoi, gridParams, this.logger, this.ct);
HashSet<string> itemNames = new HashSet<string>();
HashSet<string> fileNames = new HashSet<string>();
await foreach (ImageRegion itemRegion in gridScreen)
{
itemRegion.Click();
@@ -70,9 +69,17 @@ public class GetGridIconsTask : ISoloTask
using ImageRegion nameRegion = ra1.DeriveCrop(new Rect((int)(ra1.Width * 0.682), (int)(ra1.Width * 0.0625), (int)(ra1.Width * 0.256), (int)(ra1.Width * 0.03125)));
var ocrResult = OcrFactory.Paddle.OcrResult(nameRegion.SrcMat);
string itemName = ocrResult.Text;
if (itemNames.Add(itemName))
string itemStar = "";
if (this.starAsSuffix)
{
string filePath = Path.Combine(directory, $"{itemName}.png");
using ImageRegion starRegion = ra1.DeriveCrop(new Rect((int)(ra1.Width * 0.682), (int)(ra1.Width * 0.1823), (int)(ra1.Width * 0.105), (int)(ra1.Width * 0.02345)));
itemStar = String.Join(string.Empty, Enumerable.Repeat("★", GetStars(starRegion.SrcMat)));
}
string fileName = itemName + itemStar;
if (fileNames.Add(fileName))
{
string filePath = Path.Combine(directory, $"{fileName}.png");
Thread saveThread = new Thread(() =>
{
try
@@ -81,11 +88,11 @@ public class GetGridIconsTask : ISoloTask
{
itemRegion.SrcMat.ToBitmap().Save(fs, System.Drawing.Imaging.ImageFormat.Png);
}
logger.LogInformation("图片保存成功:{Text}", itemName);
logger.LogInformation("图片保存成功:{Text}", fileName);
}
catch (Exception e)
{
logger.LogError(e, "图片保存失败:{Text}", itemName);
logger.LogError(e, "图片保存失败:{Text}", fileName);
}
});
saveThread.IsBackground = true; // 设置为后台线程
@@ -93,7 +100,7 @@ public class GetGridIconsTask : ISoloTask
}
else
{
logger.LogInformation("重复的物品:{Text}", itemName);
logger.LogInformation("重复的物品:{Text}", fileName);
}
count--;
@@ -104,4 +111,19 @@ public class GetGridIconsTask : ISoloTask
}
}
}
/// <summary>
/// OCR检测★字符很不稳定因此用cv
/// 非常简陋的色彩检测,请传入聚焦的图像,勿带入可能的干扰
/// </summary>
/// <param name="mat"></param>
/// <returns></returns>
public static int GetStars(Mat mat)
{
Scalar yellowLower = new Scalar(50 - 5, 204 - 5, 255 - 5);
Scalar yellowUpper = new Scalar(50 + 5, 204 + 5, 255 + 0);
using Mat mask = mat.InRange(yellowLower, yellowUpper);
var contours = mask.FindContoursAsArray(RetrievalModes.External, ContourApproximationModes.ApproxSimple);
return contours?.Length ?? 0;
}
}

View File

@@ -2329,16 +2329,15 @@
Foreground="{ui:ThemeResource TextFillColorTertiaryBrush}"
TextWrapping="Wrap" Grid.ColumnSpan="2">
须要打开设置-启用保存截图功能,文件保存在
<Hyperlink Command="{Binding GoToGridIconsFolderCommand}"
<Hyperlink Command="{Binding GoToGetGridIconsFolderCommand}"
Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
log/gridIcons
</Hyperlink>
<LineBreak />
以下过长的内容在pr时会搬到教程里去
<LineBreak />
需要漆黑的背景以降低干扰,比如渊下宫-蛇肠之路的一个锚点,将视角竖直向上看向洞顶
<LineBreak />
诸如提纳里的耳朵太长了,他装备的物品角标目前无法正确地和正上方的物品图标进行轮廓分割,请手动规避
<LineBreak/>
<Hyperlink Command="{Binding GoToGetGridIconsUrlCommand}"
Foreground="{ui:ThemeResource TextFillColorSecondaryBrush}">
点击查看使用教程
</Hyperlink>
</ui:TextBlock>
<controls:TwoStateButton Grid.Row="0"
Grid.RowSpan="2"
@@ -2394,7 +2393,7 @@
<ui:TextBlock Grid.Row="0"
Grid.Column="0"
FontTypography="Body"
Text="使用星星作为名称后缀(待开发)"
Text="使用星星作为名称后缀"
TextWrapping="Wrap" />
<ui:TextBlock Grid.Row="1"
Grid.Column="0"
@@ -2405,7 +2404,6 @@
Grid.RowSpan="2"
Grid.Column="1"
Margin="0,0,36,0"
IsEnabled="{Binding Config.GetGridIconsConfig.StarAsSuffix, Mode=TwoWay}"
IsChecked="{Binding Config.GetGridIconsConfig.StarAsSuffix, Mode=TwoWay}" />
</Grid>
<Grid Margin="16">

View File

@@ -538,7 +538,7 @@ public partial class TaskSettingsPageViewModel : ViewModel
try
{
SwitchGetGridIconsEnabled = true;
await new TaskRunner().RunSoloTaskAsync(new GetGridIconsTask(Config.GetGridIconsConfig.GridName, Config.GetGridIconsConfig.MaxNumToGet));
await new TaskRunner().RunSoloTaskAsync(new GetGridIconsTask(Config.GetGridIconsConfig.GridName, Config.GetGridIconsConfig.StarAsSuffix, Config.GetGridIconsConfig.MaxNumToGet));
}
finally
{
@@ -547,7 +547,7 @@ public partial class TaskSettingsPageViewModel : ViewModel
}
[RelayCommand]
private void OnGoToGridIconsFolder()
private void OnGoToGetGridIconsFolder()
{
var path = Global.Absolute(@"log\gridIcons\");
if (!Directory.Exists(path))
@@ -557,6 +557,12 @@ public partial class TaskSettingsPageViewModel : ViewModel
Process.Start("explorer.exe", path);
}
[RelayCommand]
private async Task OnGoToGetGridIconsUrlAsync()
{
await Launcher.LaunchUriAsync(new Uri("https://bettergi.com/feats/task/getGridIcons.html"));
}
[RelayCommand]
private async Task OnSwitchAutoRedeemCode()
@@ -596,7 +602,5 @@ public partial class TaskSettingsPageViewModel : ViewModel
.RunSoloTaskAsync(new UseRedemptionCodeTask(codes));
SwitchAutoRedeemCodeEnabled = false;
}
}
}

View File

@@ -0,0 +1,30 @@
using BetterGenshinImpact.GameTask.GetGridIcons;
using OpenCvSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterGenshinImpact.UnitTest.GameTaskTests.GetGridIconsTests
{
public class GetGridIconsTaskTest
{
[Theory]
[InlineData(@"AutoArtifactSalvage\ArtifactAffixes.png", 5)]
/// <summary>
/// 测试获取物品的黄色星星数,结果应正确
/// </summary>
public void GetStars_ShouldBeRight(string screenshot, int num)
{
//
using Mat mat = new Mat(@$"..\..\..\Assets\{screenshot}");
//
int actual = GetGridIconsTask.GetStars(mat);
//
Assert.Equal(num, actual);
}
}
}