mirror of
https://github.com/babalae/better-genshin-impact.git
synced 2026-04-05 11:25:20 +08:00
big map surf test
This commit is contained in:
@@ -125,19 +125,19 @@ public class KeyPointMatchTest
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
Mat matSrcRet = new Mat();
|
||||
using Mat matSrcRet = new Mat();
|
||||
using Mat matToRet = new Mat();
|
||||
KeyPoint[] keyPointsSrc, keyPointsTo;
|
||||
using (var surf = OpenCvSharp.XFeatures2D.SURF.Create(threshold, 4, 3, false, true))
|
||||
{
|
||||
surf.DetectAndCompute(matSrc, null, out keyPointsSrc, matSrcRet);
|
||||
sw.Stop();
|
||||
Debug.WriteLine($"大地图kp耗时:{sw.ElapsedMilliseconds}ms.");
|
||||
Debug.WriteLine($"模板kp耗时:{sw.ElapsedMilliseconds}ms.");
|
||||
sw.Restart();
|
||||
|
||||
surf.DetectAndCompute(matTo, null, out keyPointsTo, matToRet);
|
||||
sw.Stop();
|
||||
Debug.WriteLine($"模板kp耗时:{sw.ElapsedMilliseconds}ms.");
|
||||
Debug.WriteLine($"大地图kp耗时:{sw.ElapsedMilliseconds}ms.");
|
||||
sw.Restart();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,10 +27,6 @@
|
||||
<Resource Include="Assets\Highlighting\*.xshd" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Assets\Map\map_sd1024.png" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AvalonEdit" Version="6.3.0.90" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.2" />
|
||||
@@ -92,6 +88,27 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Assets\Map\cityMap2048Block.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Assets\Map\combined_image_2048_lim[quick].png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Assets\Map\combined_image_lim[quick].png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Assets\Map\mainMap100Block.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Assets\Map\mainMap1024Block.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Assets\Map\mainMapZoomOut1024.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Assets\Map\map_sd1024.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Assets\Model\Domain\bgi_tree.onnx">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
@@ -535,8 +552,4 @@
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Resource Include="Assets\Map\map_sd1024.png" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,5 +1,6 @@
|
||||
using OpenCvSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using Vanara.PInvoke;
|
||||
using Color = System.Windows.Media.Color;
|
||||
@@ -76,4 +77,14 @@ public static class CommonExtension
|
||||
{
|
||||
return Color.FromArgb(color.A, color.R, color.G, color.B);
|
||||
}
|
||||
|
||||
public static Point2d ToPoint2d(this Point2f p)
|
||||
{
|
||||
return new Point2d(p.X, p.Y);
|
||||
}
|
||||
|
||||
public static List<Point2d> ToPoint2d(this List<Point2f> list)
|
||||
{
|
||||
return list.ConvertAll(ToPoint2d);
|
||||
}
|
||||
}
|
||||
|
||||
102
BetterGenshinImpact/Core/Recognition/OpenCv/SurfMatcher.cs
Normal file
102
BetterGenshinImpact/Core/Recognition/OpenCv/SurfMatcher.cs
Normal file
@@ -0,0 +1,102 @@
|
||||
using OpenCvSharp;
|
||||
using OpenCvSharp.XFeatures2D;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using BetterGenshinImpact.Helpers;
|
||||
|
||||
namespace BetterGenshinImpact.Core.Recognition.OpenCv;
|
||||
|
||||
public class SurfMatcher
|
||||
{
|
||||
private readonly double _threshold;
|
||||
private readonly Mat _matToRet = new();
|
||||
private readonly KeyPoint[] _keyPointsTo;
|
||||
|
||||
public SurfMatcher(Mat matTo, double threshold = 400)
|
||||
{
|
||||
_threshold = threshold;
|
||||
using var surf = SURF.Create(_threshold, 4, 3, false, true);
|
||||
surf.DetectAndCompute(matTo, null, out _keyPointsTo, _matToRet);
|
||||
Debug.WriteLine("被匹配的图像生成初始化KeyPoint完成");
|
||||
}
|
||||
|
||||
public Point2f[]? Match(Mat matSrc)
|
||||
{
|
||||
SpeedTimer speedTimer = new();
|
||||
|
||||
using var matSrcRet = new Mat();
|
||||
KeyPoint[] keyPointsSrc;
|
||||
using (var surf = SURF.Create(_threshold, 4, 3, false, true))
|
||||
{
|
||||
surf.DetectAndCompute(matSrc, null, out keyPointsSrc, matSrcRet);
|
||||
speedTimer.Record("模板生成KeyPoint");
|
||||
}
|
||||
|
||||
using var flnMatcher = new FlannBasedMatcher();
|
||||
var matches = flnMatcher.Match(matSrcRet, _matToRet);
|
||||
//Finding the Minimum and Maximum Distance
|
||||
double minDistance = 1000; //Backward approximation
|
||||
double maxDistance = 0;
|
||||
for (int i = 0; i < matSrcRet.Rows; i++)
|
||||
{
|
||||
double distance = matches[i].Distance;
|
||||
if (distance > maxDistance)
|
||||
{
|
||||
maxDistance = distance;
|
||||
}
|
||||
|
||||
if (distance < minDistance)
|
||||
{
|
||||
minDistance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.WriteLine($"max distance : {maxDistance}");
|
||||
Debug.WriteLine($"min distance : {minDistance}");
|
||||
|
||||
var pointsSrc = new List<Point2f>();
|
||||
var pointsDst = new List<Point2f>();
|
||||
//Screening better matching points
|
||||
// var goodMatches = new List<DMatch>();
|
||||
for (int i = 0; i < matSrcRet.Rows; i++)
|
||||
{
|
||||
double distance = matches[i].Distance;
|
||||
if (distance < Math.Max(minDistance * 2, 0.02))
|
||||
{
|
||||
pointsSrc.Add(keyPointsSrc[matches[i].QueryIdx].Pt);
|
||||
pointsDst.Add(_keyPointsTo[matches[i].TrainIdx].Pt);
|
||||
//Compression of new ones with distances less than ranges DMatch
|
||||
// goodMatches.Add(matches[i]);
|
||||
}
|
||||
}
|
||||
|
||||
speedTimer.Record("FlannMatch");
|
||||
|
||||
// var outMat = new Mat();
|
||||
|
||||
// algorithm RANSAC Filter the matched results
|
||||
var pSrc = pointsSrc.ToPoint2d();
|
||||
var pDst = pointsDst.ToPoint2d();
|
||||
var outMask = new Mat();
|
||||
// If the original matching result is null, Skip the filtering step
|
||||
if (pSrc.Count > 0 && pDst.Count > 0)
|
||||
{
|
||||
var hMat = Cv2.FindHomography(pSrc, pDst, HomographyMethods.Ransac, mask: outMask);
|
||||
speedTimer.Record("FindHomography");
|
||||
|
||||
var objCorners = new Point2f[4];
|
||||
objCorners[0] = new Point2f(0, 0);
|
||||
objCorners[1] = new Point2f(0, matSrc.Rows);
|
||||
objCorners[2] = new Point2f(matSrc.Cols, matSrc.Rows);
|
||||
objCorners[3] = new Point2f(matSrc.Cols, 0);
|
||||
|
||||
var sceneCorners = Cv2.PerspectiveTransform(objCorners, hMat);
|
||||
speedTimer.Record("PerspectiveTransform");
|
||||
speedTimer.DebugPrint();
|
||||
return sceneCorners;
|
||||
}
|
||||
speedTimer.DebugPrint();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using System.Diagnostics;
|
||||
using BetterGenshinImpact.Core.Recognition.OpenCv;
|
||||
using BetterGenshinImpact.Helpers;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using OpenCvSharp;
|
||||
using Point = OpenCvSharp.Point;
|
||||
using Size = OpenCvSharp.Size;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.Common.Map;
|
||||
|
||||
public class BigMap
|
||||
{
|
||||
public static readonly Size TemplateSize = new(240, 135);
|
||||
|
||||
// 对无用部分进行裁剪(左160,上80,下96)
|
||||
public static readonly Rect TemplateSizeRoi = new Rect(20, 10, TemplateSize.Width - 20, TemplateSize.Height - 22);
|
||||
|
||||
private readonly Mat _mapSrcMat;
|
||||
|
||||
public BigMap()
|
||||
{
|
||||
var stream = ResourceHelper.GetStream(@"pack://application:,,,/Assets/Map/map_sd1024.png");
|
||||
_mapSrcMat = Mat.FromStream(stream, ImreadModes.Color);
|
||||
}
|
||||
|
||||
public Point GetMapPosition(Mat captureMat)
|
||||
{
|
||||
Cv2.CvtColor(captureMat, captureMat, ColorConversionCodes.BGRA2BGR);
|
||||
using var tar = new Mat(captureMat.Resize(TemplateSize, 0, 0, InterpolationFlags.Cubic), TemplateSizeRoi);
|
||||
var p = MatchTemplateHelper.MatchTemplate(_mapSrcMat, tar, TemplateMatchModes.CCoeffNormed, null, 0.2);
|
||||
Debug.WriteLine($"BigMap Match Template: {p}");
|
||||
return p;
|
||||
}
|
||||
|
||||
public void GetMapPositionAndDraw(Mat captureMat)
|
||||
{
|
||||
var p = GetMapPosition(captureMat);
|
||||
WeakReferenceMessenger.Default.Send(new PropertyChangedMessage<object>(this, "UpdateBigMapRect", new object(),
|
||||
new System.Windows.Rect(p.X, p.Y, TemplateSizeRoi.Width, TemplateSizeRoi.Height)));
|
||||
}
|
||||
}
|
||||
118
BetterGenshinImpact/GameTask/Common/Map/EntireMap.cs
Normal file
118
BetterGenshinImpact/GameTask/Common/Map/EntireMap.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using BetterGenshinImpact.Core.Config;
|
||||
using BetterGenshinImpact.Core.Recognition.OpenCv;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using OpenCvSharp;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Point = OpenCvSharp.Point;
|
||||
using Size = OpenCvSharp.Size;
|
||||
|
||||
namespace BetterGenshinImpact.GameTask.Common.Map;
|
||||
|
||||
public class EntireMap
|
||||
{
|
||||
// 这个模板缩放大小的计算方式 https://github.com/babalae/better-genshin-impact/issues/318
|
||||
public static readonly Size TemplateSize = new(240, 135);
|
||||
|
||||
// 对无用部分进行裁剪(左160,上80,下96)
|
||||
public static readonly Rect TemplateSizeRoi = new Rect(20, 10, TemplateSize.Width - 20, TemplateSize.Height - 22);
|
||||
|
||||
/// <summary>
|
||||
/// 主要地图缩小1024的模板
|
||||
/// </summary>
|
||||
private readonly Mat _mainMap100BlockMat;
|
||||
|
||||
/// <summary>
|
||||
/// 1024区块拼接的主要地图
|
||||
/// </summary>
|
||||
private readonly Mat _mainMap1024BlockMat;
|
||||
|
||||
/// <summary>
|
||||
/// 2048城市区块拼接的主要地图
|
||||
/// </summary>
|
||||
private readonly Mat _cityMap2048BlockMat;
|
||||
|
||||
private readonly SurfMatcher _surfMatcher;
|
||||
|
||||
public EntireMap()
|
||||
{
|
||||
// 大地图模板匹配使用的模板
|
||||
_mainMap100BlockMat = new Mat(Global.Absolute(@"Assets\Map\mainMap100Block.png"));
|
||||
_mainMap1024BlockMat = new Mat(Global.Absolute(@"Assets\Map\mainMap1024Block.png"), ImreadModes.Grayscale);
|
||||
_cityMap2048BlockMat = new Mat(Global.Absolute(@"Assets\Map\cityMap2048Block.png"), ImreadModes.Grayscale);
|
||||
_surfMatcher = new SurfMatcher(_mainMap1024BlockMat);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基于模板匹配获取地图位置(100区块,缩小了10.24倍)
|
||||
/// 当前只支持大地图
|
||||
/// </summary>
|
||||
/// <param name="captureMat">彩色图像</param>
|
||||
/// <returns></returns>
|
||||
public Point GetMapPositionByMatchTemplate(Mat captureMat)
|
||||
{
|
||||
Cv2.CvtColor(captureMat, captureMat, ColorConversionCodes.BGRA2BGR);
|
||||
using var tar = new Mat(captureMat.Resize(TemplateSize, 0, 0, InterpolationFlags.Cubic), TemplateSizeRoi);
|
||||
var p = MatchTemplateHelper.MatchTemplate(_mainMap100BlockMat, tar, TemplateMatchModes.CCoeffNormed, null, 0.2);
|
||||
Debug.WriteLine($"BigMap Match Template: {p}");
|
||||
return p;
|
||||
}
|
||||
|
||||
public void GetMapPositionAndDrawByMatchTemplate(Mat captureMat)
|
||||
{
|
||||
var p = GetMapPositionByMatchTemplate(captureMat);
|
||||
WeakReferenceMessenger.Default.Send(new PropertyChangedMessage<object>(this, "UpdateBigMapRect", new object(),
|
||||
new System.Windows.Rect(p.X, p.Y, TemplateSizeRoi.Width, TemplateSizeRoi.Height)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 基于Surf匹配获取地图位置(1024区块)
|
||||
/// 支持大地图和小地图
|
||||
/// </summary>
|
||||
/// <param name="captureGreyMat">灰度图</param>
|
||||
/// <returns></returns>
|
||||
public Rect GetMapPositionBySurf(Mat captureGreyMat)
|
||||
{
|
||||
var pArray = _surfMatcher.Match(captureGreyMat);
|
||||
if (pArray == null || pArray.Length < 4)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
return Cv2.BoundingRect(pArray);
|
||||
}
|
||||
|
||||
// public static Point GetIntersection(Point2f[] points)
|
||||
// {
|
||||
// double a1 = (points[0].Y - points[2].Y) / (double)(points[0].X - points[2].X);
|
||||
// double b1 = points[0].Y - a1 * points[0].X;
|
||||
//
|
||||
// double a2 = (points[1].Y - points[3].Y) / (double)(points[1].X - points[3].X);
|
||||
// double b2 = points[1].Y - a2 * points[1].X;
|
||||
//
|
||||
// if (Math.Abs(a1 - a2) < double.Epsilon)
|
||||
// {
|
||||
// // 不相交
|
||||
// throw new InvalidOperationException();
|
||||
// }
|
||||
//
|
||||
// double x = (b2 - b1) / (a1 - a2);
|
||||
// double y = a1 * x + b1;
|
||||
// return new Point((int)x, (int)y);
|
||||
// }
|
||||
|
||||
public void GetMapPositionAndDrawBySurf(Mat captureGreyMat)
|
||||
{
|
||||
try
|
||||
{
|
||||
var rect = GetMapPositionBySurf(captureGreyMat);
|
||||
WeakReferenceMessenger.Default.Send(new PropertyChangedMessage<object>(this, "UpdateBigMapRect", new object(),
|
||||
new System.Windows.Rect(rect.X / 10.24, rect.Y / 10.24, rect.Width / 10.24, rect.Height / 10.24)));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Debug.WriteLine("Surf Match Failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
namespace BetterGenshinImpact.GameTask.Common.Map;
|
||||
|
||||
public class MiniMap
|
||||
{
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using BetterGenshinImpact.GameTask.Common.Map;
|
||||
using BetterGenshinImpact.View.Drawable;
|
||||
using BetterGenshinImpact.View.Drawable;
|
||||
using OpenCvSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -27,7 +26,7 @@ public class TestTrigger : ITaskTrigger
|
||||
|
||||
// private readonly YoloV8 _predictor = new(Global.Absolute("Assets\\Model\\Domain\\bgi_tree.onnx"));
|
||||
|
||||
private readonly BigMap _bigMap = new();
|
||||
// private readonly EntireMap _bigMap = new();
|
||||
|
||||
public TestTrigger()
|
||||
{
|
||||
@@ -93,7 +92,7 @@ public class TestTrigger : ITaskTrigger
|
||||
// Debug.WriteLine("没找到");
|
||||
//}
|
||||
|
||||
_bigMap.GetMapPositionAndDraw(content.CaptureRectArea.SrcMat);
|
||||
// _bigMap.GetMapPositionAndDrawBySurf(content.CaptureRectArea.SrcGreyMat);
|
||||
}
|
||||
|
||||
// private void Detect(CaptureContent content)
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Canvas Grid.Row="1">
|
||||
<ui:Image Source="pack://application:,,,/Assets/Map/map_sd1024.png" Stretch="Uniform" />
|
||||
<ui:Image Source="{Binding MapPath, Mode=OneWay}" Stretch="Uniform" />
|
||||
<Rectangle Canvas.Left="{Binding BigMapRect.Left}"
|
||||
Canvas.Top="{Binding BigMapRect.Top}"
|
||||
Width="{Binding BigMapRect.Width}"
|
||||
@@ -41,4 +41,4 @@
|
||||
</ui:TitleBar>
|
||||
|
||||
</Grid>
|
||||
</ui:FluentWindow>
|
||||
</ui:FluentWindow>
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.Windows;
|
||||
using System;
|
||||
using System.Windows;
|
||||
using BetterGenshinImpact.Core.Config;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
@@ -10,6 +12,9 @@ public partial class MapViewerViewModel : ObservableObject
|
||||
[ObservableProperty]
|
||||
private Rect _bigMapRect = new(0, 0, 0, 0);
|
||||
|
||||
[ObservableProperty]
|
||||
private string _mapPath = Global.Absolute(@"Assets\Map\mainMap100Block.png");
|
||||
|
||||
public MapViewerViewModel()
|
||||
{
|
||||
WeakReferenceMessenger.Default.Register<PropertyChangedMessage<object>>(this, (sender, msg) =>
|
||||
|
||||
Reference in New Issue
Block a user