switching
This commit is contained in:
parent
490fb8bdba
commit
ec1f6274e3
5 changed files with 246 additions and 69 deletions
|
|
@ -10,6 +10,10 @@ public class MinimapCapture : IDisposable
|
|||
private readonly MinimapConfig _config;
|
||||
private readonly IScreenCapture _backend;
|
||||
private readonly WallColorTracker _colorTracker;
|
||||
private MinimapMode _detectedMode = MinimapMode.Overlay;
|
||||
|
||||
public MinimapMode DetectedMode => _detectedMode;
|
||||
public event Action<MinimapMode>? ModeChanged;
|
||||
|
||||
public MinimapCapture(MinimapConfig config)
|
||||
{
|
||||
|
|
@ -49,9 +53,17 @@ public class MinimapCapture : IDisposable
|
|||
|
||||
public MinimapFrame? CaptureFrame()
|
||||
{
|
||||
var region = _config.CaptureRegion;
|
||||
using var bgr = _backend.CaptureRegion(region);
|
||||
// Auto-detect minimap mode every frame (just a 5x5 pixel check, negligible cost)
|
||||
var detected = DetectMinimapMode();
|
||||
if (detected != _detectedMode)
|
||||
{
|
||||
_detectedMode = detected;
|
||||
Log.Information("Minimap mode switched to {Mode}", _detectedMode);
|
||||
ResetAdaptation();
|
||||
ModeChanged?.Invoke(_detectedMode);
|
||||
}
|
||||
|
||||
using var bgr = CaptureAndNormalize(_detectedMode);
|
||||
if (bgr == null || bgr.Empty())
|
||||
return null;
|
||||
|
||||
|
|
@ -66,19 +78,52 @@ public class MinimapCapture : IDisposable
|
|||
// Wall mask: target #A2AEE5 blue-lavender structure lines (range adapts per-map)
|
||||
var wallMask = BuildWallMask(hsv, playerMask, sample: true);
|
||||
|
||||
// Fog of war: broad blue range minus walls minus player
|
||||
using var fogMask = BuildFogMask(hsv, wallMask, playerMask);
|
||||
// Build classified mat (use actual frame size — differs between overlay and corner)
|
||||
var frameSize = bgr.Width;
|
||||
var classified = new Mat(frameSize, frameSize, MatType.CV_8UC1, Scalar.Black);
|
||||
|
||||
// Build classified mat (fog first, walls override)
|
||||
var classified = new Mat(_config.CaptureSize, _config.CaptureSize, MatType.CV_8UC1, Scalar.Black);
|
||||
classified.SetTo(new Scalar((byte)MapCell.Fog), fogMask);
|
||||
classified.SetTo(new Scalar((byte)MapCell.Wall), wallMask);
|
||||
if (_detectedMode == MinimapMode.Corner)
|
||||
{
|
||||
// Corner minimap is clean — skip fog detection
|
||||
classified.SetTo(new Scalar((byte)MapCell.Wall), wallMask);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Overlay: fog detection needed (broad blue minus walls minus player)
|
||||
using var fogMask = BuildFogMask(hsv, wallMask, playerMask);
|
||||
classified.SetTo(new Scalar((byte)MapCell.Fog), fogMask);
|
||||
classified.SetTo(new Scalar((byte)MapCell.Wall), wallMask);
|
||||
}
|
||||
|
||||
// Gray for correlation tracking (player zeroed)
|
||||
var grayForCorr = new Mat();
|
||||
Cv2.CvtColor(bgr, grayForCorr, ColorConversionCodes.BGR2GRAY);
|
||||
grayForCorr.SetTo(Scalar.Black, playerMask);
|
||||
|
||||
// Corner mode: rescale classified + wall mask to match overlay scale
|
||||
// Uses nearest-neighbor so discrete cell values (0-3) stay crisp
|
||||
if (_detectedMode == MinimapMode.Corner && Math.Abs(_config.CornerScale - 1.0) > 0.01)
|
||||
{
|
||||
var scaledSize = (int)Math.Round(frameSize * _config.CornerScale);
|
||||
var scaledClassified = new Mat();
|
||||
Cv2.Resize(classified, scaledClassified, new Size(scaledSize, scaledSize),
|
||||
interpolation: InterpolationFlags.Nearest);
|
||||
classified.Dispose();
|
||||
classified = scaledClassified;
|
||||
|
||||
var scaledWalls = new Mat();
|
||||
Cv2.Resize(wallMask, scaledWalls, new Size(scaledSize, scaledSize),
|
||||
interpolation: InterpolationFlags.Nearest);
|
||||
wallMask.Dispose();
|
||||
wallMask = scaledWalls;
|
||||
|
||||
var scaledGray = new Mat();
|
||||
Cv2.Resize(grayForCorr, scaledGray, new Size(scaledSize, scaledSize),
|
||||
interpolation: InterpolationFlags.Linear);
|
||||
grayForCorr.Dispose();
|
||||
grayForCorr = scaledGray;
|
||||
}
|
||||
|
||||
return new MinimapFrame(
|
||||
GrayMat: grayForCorr,
|
||||
WallMask: wallMask,
|
||||
|
|
@ -88,6 +133,43 @@ public class MinimapCapture : IDisposable
|
|||
);
|
||||
}
|
||||
|
||||
private Mat? CaptureAndNormalize(MinimapMode mode)
|
||||
{
|
||||
var region = mode == MinimapMode.Overlay
|
||||
? _config.OverlayRegion
|
||||
: _config.CornerRegion;
|
||||
|
||||
return _backend.CaptureRegion(region);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detect minimap mode by sampling a small patch at the corner minimap center.
|
||||
/// If the pixel is close to #DE581B (orange player dot), corner minimap is active.
|
||||
/// </summary>
|
||||
private MinimapMode DetectMinimapMode()
|
||||
{
|
||||
// Capture a tiny 5x5 region at the corner center
|
||||
var cx = _config.CornerCenterX;
|
||||
var cy = _config.CornerCenterY;
|
||||
var probe = new Poe2Trade.Core.Region(cx - 2, cy - 2, 5, 5);
|
||||
using var patch = _backend.CaptureRegion(probe);
|
||||
if (patch == null || patch.Empty())
|
||||
return _detectedMode; // keep current on failure
|
||||
|
||||
// Average the BGR values of the patch
|
||||
var mean = Cv2.Mean(patch);
|
||||
var b = mean.Val0;
|
||||
var g = mean.Val1;
|
||||
var r = mean.Val2;
|
||||
|
||||
// #DE581B → R=222, G=88, B=27 — check if close (tolerance ~60 per channel)
|
||||
const int tol = 60;
|
||||
if (Math.Abs(r - 222) < tol && Math.Abs(g - 88) < tol && Math.Abs(b - 27) < tol)
|
||||
return MinimapMode.Corner;
|
||||
|
||||
return MinimapMode.Overlay;
|
||||
}
|
||||
|
||||
private Mat BuildWallMask(Mat hsv, Mat playerMask, bool sample = false)
|
||||
{
|
||||
// Use adapted range if available (narrows per-map), otherwise broad default
|
||||
|
|
@ -163,8 +245,7 @@ public class MinimapCapture : IDisposable
|
|||
/// </summary>
|
||||
public byte[]? CaptureStage(MinimapDebugStage stage)
|
||||
{
|
||||
var region = _config.CaptureRegion;
|
||||
using var bgr = _backend.CaptureRegion(region);
|
||||
using var bgr = CaptureAndNormalize(_detectedMode);
|
||||
if (bgr == null || bgr.Empty()) return null;
|
||||
|
||||
if (stage == MinimapDebugStage.Raw) return EncodePng(bgr);
|
||||
|
|
@ -192,7 +273,7 @@ public class MinimapCapture : IDisposable
|
|||
if (stage == MinimapDebugStage.Fog) return EncodePng(fogMask);
|
||||
|
||||
// Classified (walls + fog + player — explored is tracked by WorldMap)
|
||||
using var classified = new Mat(_config.CaptureSize, _config.CaptureSize, MatType.CV_8UC3, Scalar.Black);
|
||||
using var classified = new Mat(bgr.Height, bgr.Width, MatType.CV_8UC3, Scalar.Black);
|
||||
classified.SetTo(new Scalar(180, 140, 70), fogMask); // light blue for fog
|
||||
classified.SetTo(new Scalar(26, 45, 61), wallMask); // brown for walls
|
||||
classified.SetTo(new Scalar(0, 165, 255), playerMask); // orange for player
|
||||
|
|
@ -211,8 +292,7 @@ public class MinimapCapture : IDisposable
|
|||
public void SaveDebugCapture(string dir = "debug-minimap")
|
||||
{
|
||||
Directory.CreateDirectory(dir);
|
||||
var region = _config.CaptureRegion;
|
||||
using var bgr = _backend.CaptureRegion(region);
|
||||
using var bgr = CaptureAndNormalize(_detectedMode);
|
||||
if (bgr == null || bgr.Empty()) return;
|
||||
|
||||
using var hsv = new Mat();
|
||||
|
|
@ -224,7 +304,7 @@ public class MinimapCapture : IDisposable
|
|||
using var wallMask = BuildWallMask(hsv, playerMask);
|
||||
|
||||
// Colorized classified (walls + player)
|
||||
using var classified = new Mat(_config.CaptureSize, _config.CaptureSize, MatType.CV_8UC3, Scalar.Black);
|
||||
using var classified = new Mat(bgr.Height, bgr.Width, MatType.CV_8UC3, Scalar.Black);
|
||||
classified.SetTo(new Scalar(26, 45, 61), wallMask); // brown
|
||||
classified.SetTo(new Scalar(0, 165, 255), playerMask); // orange
|
||||
|
||||
|
|
@ -249,8 +329,8 @@ public class MinimapCapture : IDisposable
|
|||
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;
|
||||
var cx = moments.M10 / moments.M00 - mask.Width / 2.0;
|
||||
var cy = moments.M01 / moments.M00 - mask.Height / 2.0;
|
||||
return new Point2d(cx, cy);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue