started adding navigation
This commit is contained in:
parent
32781b1462
commit
468e0a7246
20 changed files with 844 additions and 31 deletions
119
src/Poe2Trade.Navigation/MinimapCapture.cs
Normal file
119
src/Poe2Trade.Navigation/MinimapCapture.cs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Runtime.InteropServices;
|
||||
using OpenCvSharp;
|
||||
using OpenCvSharp.Extensions;
|
||||
using Serilog;
|
||||
using Region = Poe2Trade.Core.Region;
|
||||
using Point = OpenCvSharp.Point;
|
||||
using Size = OpenCvSharp.Size;
|
||||
|
||||
namespace Poe2Trade.Navigation;
|
||||
|
||||
public class MinimapCapture : IDisposable
|
||||
{
|
||||
private readonly MinimapConfig _config;
|
||||
private Mat? _circularMask;
|
||||
|
||||
[DllImport("user32.dll")]
|
||||
private static extern int GetSystemMetrics(int nIndex);
|
||||
|
||||
public MinimapCapture(MinimapConfig config)
|
||||
{
|
||||
_config = config;
|
||||
BuildCircularMask();
|
||||
}
|
||||
|
||||
private void BuildCircularMask()
|
||||
{
|
||||
var size = _config.CaptureSize;
|
||||
_circularMask = new Mat(size, size, MatType.CV_8UC1, Scalar.Black);
|
||||
var center = new Point(size / 2, size / 2);
|
||||
Cv2.Circle(_circularMask, center, _config.FogRadius, Scalar.White, -1);
|
||||
}
|
||||
|
||||
public MinimapFrame? CaptureFrame()
|
||||
{
|
||||
var region = _config.CaptureRegion;
|
||||
using var bitmap = CaptureScreen(region);
|
||||
using var bgr = BitmapConverter.ToMat(bitmap);
|
||||
|
||||
if (bgr.Empty())
|
||||
return null;
|
||||
|
||||
// Apply circular mask to ignore area outside fog-of-war circle
|
||||
using var masked = new Mat();
|
||||
Cv2.BitwiseAnd(bgr, bgr, masked, _circularMask!);
|
||||
|
||||
// Convert to HSV
|
||||
using var hsv = new Mat();
|
||||
Cv2.CvtColor(masked, hsv, ColorConversionCodes.BGR2HSV);
|
||||
|
||||
// Classify explored areas
|
||||
using var exploredMask = new Mat();
|
||||
Cv2.InRange(hsv, _config.ExploredLoHSV, _config.ExploredHiHSV, exploredMask);
|
||||
|
||||
// Classify walls: dark pixels within the circular mask
|
||||
using var valueChan = new Mat();
|
||||
Cv2.ExtractChannel(hsv, valueChan, 2); // V channel
|
||||
using var darkMask = new Mat();
|
||||
Cv2.Threshold(valueChan, darkMask, _config.WallMaxValue, 255, ThresholdTypes.BinaryInv);
|
||||
// Apply morphological close to connect wall fragments
|
||||
using var wallKernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3));
|
||||
using var wallMask = new Mat();
|
||||
Cv2.MorphologyEx(darkMask, wallMask, MorphTypes.Close, wallKernel);
|
||||
// Only within circular mask
|
||||
Cv2.BitwiseAnd(wallMask, _circularMask!, wallMask);
|
||||
|
||||
// Build classified mat: Unknown=0, Explored=1, Wall=2
|
||||
var classified = new Mat(_config.CaptureSize, _config.CaptureSize, MatType.CV_8UC1, Scalar.Black);
|
||||
classified.SetTo(new Scalar((byte)MapCell.Explored), exploredMask);
|
||||
classified.SetTo(new Scalar((byte)MapCell.Wall), wallMask);
|
||||
// Ensure only within circular mask
|
||||
Cv2.BitwiseAnd(classified, _circularMask!, classified);
|
||||
|
||||
// Detect player marker (orange X)
|
||||
using var playerMask = new Mat();
|
||||
Cv2.InRange(hsv, _config.PlayerLoHSV, _config.PlayerHiHSV, playerMask);
|
||||
var playerOffset = FindCentroid(playerMask);
|
||||
|
||||
// Convert to grayscale for phase correlation
|
||||
var gray = new Mat();
|
||||
Cv2.CvtColor(masked, gray, ColorConversionCodes.BGR2GRAY);
|
||||
// Apply circular mask to gray too
|
||||
Cv2.BitwiseAnd(gray, _circularMask!, gray);
|
||||
|
||||
return new MinimapFrame(
|
||||
GrayMat: gray,
|
||||
ClassifiedMat: classified,
|
||||
PlayerOffset: playerOffset,
|
||||
Timestamp: DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
|
||||
);
|
||||
}
|
||||
|
||||
private Point2d FindCentroid(Mat mask)
|
||||
{
|
||||
var moments = Cv2.Moments(mask, true);
|
||||
if (moments.M00 < 10) // not enough pixels
|
||||
return new Point2d(0, 0);
|
||||
|
||||
var cx = moments.M10 / moments.M00 - _config.CaptureSize / 2.0;
|
||||
var cy = moments.M01 / moments.M00 - _config.CaptureSize / 2.0;
|
||||
return new Point2d(cx, cy);
|
||||
}
|
||||
|
||||
private static Bitmap CaptureScreen(Region region)
|
||||
{
|
||||
var bitmap = new Bitmap(region.Width, region.Height, PixelFormat.Format32bppArgb);
|
||||
using var g = Graphics.FromImage(bitmap);
|
||||
g.CopyFromScreen(region.X, region.Y, 0, 0,
|
||||
new System.Drawing.Size(region.Width, region.Height),
|
||||
CopyPixelOperation.SourceCopy);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_circularMask?.Dispose();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue