using BetterGenshinImpact.Core.Recognition.OCR; using BetterGenshinImpact.Core.Simulator; using BetterGenshinImpact.GameTask.AutoArtifactSalvage; using BetterGenshinImpact.GameTask.Common.Job; using BetterGenshinImpact.GameTask.Model.Area; using BetterGenshinImpact.GameTask.Model.GameUI; using BetterGenshinImpact.Helpers.Extensions; using Fischless.WindowsInput; using Microsoft.Extensions.Logging; using OpenCvSharp; using OpenCvSharp.Extensions; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using static BetterGenshinImpact.GameTask.Common.TaskControl; namespace BetterGenshinImpact.GameTask.GetGridIcons; /// /// 获取Grid界面的物品图标 /// public class GetGridIconsTask : ISoloTask { private readonly ILogger logger = App.GetLogger(); private readonly InputSimulator input = Simulation.SendInput; private CancellationToken ct; public string Name => "获取Grid界面物品图标独立任务"; private readonly int? maxNumToGet; private readonly GridScreenName gridScreenName; private readonly bool starAsSuffix; public GetGridIconsTask(GridScreenName gridScreenName, bool starAsSuffix, int? maxNumToGet = null) { this.gridScreenName = gridScreenName; this.starAsSuffix = starAsSuffix; this.maxNumToGet = maxNumToGet; } public async Task Start(CancellationToken ct) { this.ct = ct; switch (this.gridScreenName) { case GridScreenName.Weapons: case GridScreenName.Artifacts: case GridScreenName.CharacterDevelopmentItems: case GridScreenName.Food: case GridScreenName.Materials: case GridScreenName.Gadget: case GridScreenName.Quest: case GridScreenName.PreciousItems: case GridScreenName.Furnishings: await new ReturnMainUiTask().Start(ct); await AutoArtifactSalvageTask.OpenBag(this.gridScreenName, this.input, this.logger, this.ct); break; default: logger.LogInformation("{name}暂不支持自动打开,请提前手动打开界面", gridScreenName.GetDescription()); break; } using var ra0 = CaptureToRectArea(); GridScreenParams gridParams = GridScreenParams.Templates[this.gridScreenName]; Rect gridRoi = gridParams.GetRect(ra0); int count = this.maxNumToGet ?? int.MaxValue; string directory = Path.Combine(AppContext.BaseDirectory, "log/gridIcons", DateTime.Now.ToString("yyyyMMddHHmmss")); Directory.CreateDirectory(directory); GridScreen gridScreen = new GridScreen(gridRoi, gridParams, this.logger, this.ct); HashSet fileNames = new HashSet(); await foreach (ImageRegion itemRegion in gridScreen) { itemRegion.Click(); await Delay(300, ct); using var ra1 = CaptureToRectArea(); 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; string itemStar = ""; if (this.starAsSuffix) { 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 { using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None)) { itemRegion.SrcMat.ToBitmap().Save(fs, System.Drawing.Imaging.ImageFormat.Png); } logger.LogInformation("图片保存成功:{Text}", fileName); } catch (Exception e) { logger.LogError(e, "图片保存失败:{Text}", fileName); } }); saveThread.IsBackground = true; // 设置为后台线程 saveThread.Start(); } else { logger.LogInformation("重复的物品:{Text}", fileName); } count--; if (count <= 0) { logger.LogInformation("检查次数已耗尽"); break; } } } /// /// OCR检测★字符很不稳定,因此用cv /// 非常简陋的色彩检测,请传入聚焦的图像,勿带入可能的干扰 /// /// /// 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; } }