mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-05-29 10:25:50 +08:00
* GetFishBarRect方法添加更复杂的算法,并为其配备独立的单元测试,和分离难度较大的测试用例(未熟练时两侧出现黄色动态折线的情况);GetFishBoxArea行为去掉拉条框初始位置必须位于屏幕中轴线的条件,并添加其后续Fishing行为的单元测试来验证可行性;EnterFishingMode行为使用结束时间来代替Sleep,并添加整体超时时间;添加一个鱼咬钩的假阳性测试用例仅供娱乐 * 补充GetFishBarRect算法,使通过遗漏的测试"20250314002439020_Fishing_Succeeded.png" * 拉条增加1秒未检测持续时间以应对瞬间丢失拉条框的情况;新增一个检查提竿结果的行为;新增一个检查开始钓一条鱼的初始状态的方法,以应对行为状态错配的情况;一些行为将Sleep优化为DateTime;修改上述改动对应的单元测试 * 解决合并冲突剩余问题,删掉ImageRegion的Bitmap构造函数重载 * 提供给测试用例初始化的 SystemInfo、TaskContext 方法,使用 InitForTest 即可 * InitForTest * 和鸭蛋昨夜的提交撞车了。。。抽象了ISystemInto供单元测试实例化Fake类;给BaseAssets类定义了成员字段systemInfo(我想,既然都是图片模板数据集,如此定义是合理的),供继承类AutoFishingAssets使用,并定义了其在单元测试的派生类;添加了一个900p的选取鱼饵测试用例;blackboard改为负责携带AutoFishingAssets,并将其实例化时机挪到独立任务的Start方法中,避免由于TaskContext尚未初始化导致获取到的SystemInfo为空 * 一个特殊的测试用例:抛竿的瞬间、开始检测咬杆时遇到了假阳性 * Revert "InitForTest" This reverts commit225e9783a7. * Revert "提供给测试用例初始化的 SystemInfo、TaskContext 方法,使用 InitForTest 即可" This reverts commit610c57263a. * 为始终没有找到落点的情况添加计数,在第3次时直接退出,并添加此情况的单元测试 --------- Co-authored-by: 辉鸭蛋 <huiyadanli@gmail.com>
105 lines
4.5 KiB
C#
105 lines
4.5 KiB
C#
using OpenCvSharp;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Diagnostics;
|
||
using System.Linq;
|
||
|
||
namespace BetterGenshinImpact.GameTask.AutoFishing
|
||
{
|
||
public class AutoFishingImageRecognition
|
||
{
|
||
/// <summary>
|
||
/// 钓鱼条矩形识别
|
||
/// </summary>
|
||
/// <param name="src"></param>
|
||
/// <returns></returns>
|
||
public static List<Rect>? GetFishBarRect(Mat src)
|
||
{
|
||
try
|
||
{
|
||
// 拉条框的黄色是:RGB 255, 255, 192 ~ HSV 43, 63, 255
|
||
// var testPixel = rgbMat.At<Vec3b>(105, 968);
|
||
using Mat rgbMat = src.CvtColor(ColorConversionCodes.BGR2HSV_FULL);
|
||
|
||
var lowYellow = new Scalar(43 - 3, 63 - 20, 255 - 10);
|
||
var highYellow = new Scalar(43 + 3, 63 + 40, 255);
|
||
using Mat mask = rgbMat.InRange(lowYellow, highYellow);
|
||
|
||
Cv2.Threshold(mask, mask, 0, 255, ThresholdTypes.Binary); //二值化
|
||
|
||
Cv2.FindContours(mask, out var contours, out _, RetrievalModes.External,
|
||
ContourApproximationModes.ApproxSimple, null);
|
||
if (contours.Length > 0)
|
||
{
|
||
contours = contours.Where(c => Cv2.MinAreaRect(c).Angle % 45 <= 1).ToArray(); // 剔除倾斜的;箭头边缘是45度角,在游标靠近两侧箭头时,箭头的最小外接是45度的
|
||
List<Rect> boxes = contours.Select(Cv2.BoundingRect).ToList();
|
||
Rect widest = boxes.OrderBy(b => b.Width).LastOrDefault(); // 取最宽的一根当作基准
|
||
if (widest == default)
|
||
{
|
||
return null;
|
||
}
|
||
boxes = boxes.Where(r => Math.Abs((widest.Y + widest.Height / 2) - (r.Y + r.Height / 2)) < widest.Height / 5) // 保持一条水平线
|
||
.Where(r => Math.Abs(widest.Height - r.Height) < (widest.Height / 3) && r.Width > (widest.Height / 4)).ToList(); // 剔除高度差异太大的,和宽度太小的
|
||
return boxes;
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.WriteLine(e);
|
||
}
|
||
|
||
return null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 匹配 “鱼儿上钩拉!”文字区域
|
||
/// </summary>
|
||
/// <param name="src"></param>
|
||
/// <param name="liftingWordsAreaRect"></param>
|
||
/// <returns></returns>
|
||
public static Rect MatchFishBiteWords(Mat src, Rect liftingWordsAreaRect)
|
||
{
|
||
try
|
||
{
|
||
Cv2.CvtColor(src, src, ColorConversionCodes.BGR2RGB);
|
||
var lowPurple = new Scalar(253, 253, 253);
|
||
var highPurple = new Scalar(255, 255, 255);
|
||
Cv2.InRange(src, lowPurple, highPurple, src);
|
||
Cv2.Threshold(src, src, 0, 255, ThresholdTypes.Binary);
|
||
var kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(20, 20),
|
||
new OpenCvSharp.Point(-1, -1));
|
||
Cv2.Dilate(src, src, kernel); //膨胀
|
||
|
||
Cv2.FindContours(src, out var contours, out _, RetrievalModes.External,
|
||
ContourApproximationModes.ApproxSimple, null);
|
||
if (contours.Length > 0)
|
||
{
|
||
var boxes = contours.Select(Cv2.BoundingRect);
|
||
var rects = boxes.ToList();
|
||
if (rects.Count > 1)
|
||
{
|
||
rects.Sort((a, b) => b.Height.CompareTo(a.Height));
|
||
}
|
||
|
||
//VisionContext.Instance().DrawContent.PutRect("FishBiteTipsDebug",
|
||
// rects[0].ToWindowsRectangleOffset(liftingWordsAreaRect.X, liftingWordsAreaRect.Y)
|
||
// .ToRectDrawable());
|
||
if (rects[0].Height < src.Height
|
||
&& rects[0].Width * 1.0 / rects[0].Height >= 3 // 长宽比判断
|
||
&& liftingWordsAreaRect.Width > rects[0].Width * 3 // 文字范围3倍小于钓鱼条范围的
|
||
&& liftingWordsAreaRect.Width * 1.0 / 2 > rects[0].X // 中轴线判断左
|
||
&& liftingWordsAreaRect.Width * 1.0 / 2 < rects[0].X + rects[0].Width) // 中轴线判断右
|
||
{
|
||
return rects[0];
|
||
}
|
||
}
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
Debug.WriteLine(e);
|
||
}
|
||
|
||
return Rect.Empty;
|
||
}
|
||
}
|
||
} |