work on pathing
This commit is contained in:
parent
7d10f1d2a9
commit
3bb0315912
10 changed files with 729 additions and 113 deletions
128
src/Poe2Trade.Navigation/IconDetector.cs
Normal file
128
src/Poe2Trade.Navigation/IconDetector.cs
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
using OpenCvSharp;
|
||||
using Serilog;
|
||||
|
||||
namespace Poe2Trade.Navigation;
|
||||
|
||||
/// <summary>
|
||||
/// Detects minimap icons (doors, checkpoints) via template matching.
|
||||
/// Loads RGBA templates once, converts to grayscale for matching.
|
||||
/// </summary>
|
||||
internal class IconDetector : IDisposable
|
||||
{
|
||||
private readonly Mat _doorTemplate;
|
||||
private readonly Mat _checkpointOffTemplate;
|
||||
private readonly Mat _checkpointOnTemplate;
|
||||
|
||||
private const double DoorThreshold = 0.65;
|
||||
private const double CheckpointThreshold = 0.60;
|
||||
|
||||
public IconDetector(string assetsDir)
|
||||
{
|
||||
_doorTemplate = LoadGray(Path.Combine(assetsDir, "door.png"));
|
||||
_checkpointOffTemplate = LoadGray(Path.Combine(assetsDir, "checkpoint-off.png"));
|
||||
_checkpointOnTemplate = LoadGray(Path.Combine(assetsDir, "checkpoint-on.png"));
|
||||
|
||||
Log.Information("IconDetector loaded: door={DW}x{DH} cpOff={OW}x{OH} cpOn={NW}x{NH}",
|
||||
_doorTemplate.Width, _doorTemplate.Height,
|
||||
_checkpointOffTemplate.Width, _checkpointOffTemplate.Height,
|
||||
_checkpointOnTemplate.Width, _checkpointOnTemplate.Height);
|
||||
}
|
||||
|
||||
private static Mat LoadGray(string path)
|
||||
{
|
||||
var bgra = Cv2.ImRead(path, ImreadModes.Unchanged);
|
||||
if (bgra.Empty())
|
||||
throw new FileNotFoundException($"Icon template not found: {path}");
|
||||
|
||||
var gray = new Mat();
|
||||
if (bgra.Channels() == 4)
|
||||
{
|
||||
using var bgr = new Mat();
|
||||
Cv2.CvtColor(bgra, bgr, ColorConversionCodes.BGRA2BGR);
|
||||
Cv2.CvtColor(bgr, gray, ColorConversionCodes.BGR2GRAY);
|
||||
}
|
||||
else if (bgra.Channels() == 3)
|
||||
{
|
||||
Cv2.CvtColor(bgra, gray, ColorConversionCodes.BGR2GRAY);
|
||||
}
|
||||
else
|
||||
{
|
||||
gray = bgra.Clone();
|
||||
}
|
||||
|
||||
bgra.Dispose();
|
||||
return gray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detect all door icons in the frame. Returns center points in frame coords.
|
||||
/// </summary>
|
||||
public List<Point> DetectDoors(Mat grayFrame)
|
||||
=> Detect(grayFrame, _doorTemplate, DoorThreshold);
|
||||
|
||||
/// <summary>
|
||||
/// Detect all inactive checkpoint icons. Returns center points in frame coords.
|
||||
/// </summary>
|
||||
public List<Point> DetectCheckpointsOff(Mat grayFrame)
|
||||
=> Detect(grayFrame, _checkpointOffTemplate, CheckpointThreshold);
|
||||
|
||||
/// <summary>
|
||||
/// Detect all active checkpoint icons. Returns center points in frame coords.
|
||||
/// </summary>
|
||||
public List<Point> DetectCheckpointsOn(Mat grayFrame)
|
||||
=> Detect(grayFrame, _checkpointOnTemplate, CheckpointThreshold);
|
||||
|
||||
/// <summary>
|
||||
/// Greedy multi-match: find best match, record center, zero out neighborhood, repeat.
|
||||
/// </summary>
|
||||
private static List<Point> Detect(Mat grayFrame, Mat template, double threshold)
|
||||
{
|
||||
var results = new List<Point>();
|
||||
|
||||
if (grayFrame.Width < template.Width || grayFrame.Height < template.Height)
|
||||
return results;
|
||||
|
||||
using var result = new Mat();
|
||||
Cv2.MatchTemplate(grayFrame, template, result, TemplateMatchModes.CCoeffNormed);
|
||||
|
||||
var tw = template.Width;
|
||||
var th = template.Height;
|
||||
|
||||
while (true)
|
||||
{
|
||||
Cv2.MinMaxLoc(result, out _, out var maxVal, out _, out var maxLoc);
|
||||
if (maxVal < threshold)
|
||||
break;
|
||||
|
||||
// Record center point of the matched region
|
||||
var centerX = maxLoc.X + tw / 2;
|
||||
var centerY = maxLoc.Y + th / 2;
|
||||
results.Add(new Point(centerX, centerY));
|
||||
|
||||
// Zero out neighborhood to prevent re-detection
|
||||
var zeroX = Math.Max(0, maxLoc.X - tw / 2);
|
||||
var zeroY = Math.Max(0, maxLoc.Y - th / 2);
|
||||
var zeroW = Math.Min(tw * 2, result.Width - zeroX);
|
||||
var zeroH = Math.Min(th * 2, result.Height - zeroY);
|
||||
if (zeroW > 0 && zeroH > 0)
|
||||
{
|
||||
using var roi = new Mat(result, new Rect(zeroX, zeroY, zeroW, zeroH));
|
||||
roi.SetTo(Scalar.Black);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the template size for doors (used to stamp wall regions).
|
||||
/// </summary>
|
||||
public Size DoorSize => new(_doorTemplate.Width, _doorTemplate.Height);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_doorTemplate.Dispose();
|
||||
_checkpointOffTemplate.Dispose();
|
||||
_checkpointOnTemplate.Dispose();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue