big map surf test

This commit is contained in:
辉鸭蛋
2024-03-31 14:49:03 +08:00
parent 992e1ac3eb
commit a9ff321daf
10 changed files with 266 additions and 65 deletions

View File

@@ -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();
}

View File

@@ -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>

View File

@@ -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);
}
}

View 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;
}
}

View File

@@ -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)));
}
}

View 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");
}
}
}

View File

@@ -1,5 +0,0 @@
namespace BetterGenshinImpact.GameTask.Common.Map;
public class MiniMap
{
}

View File

@@ -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)

View File

@@ -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>

View File

@@ -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) =>