getting closer

This commit is contained in:
Boki 2026-02-13 13:14:09 -05:00
parent 40f013d07e
commit bb8e75a2f5
2 changed files with 70 additions and 10 deletions

View file

@ -95,11 +95,21 @@ public class MinimapConfig
public int CanvasSize { get; set; } = 4000;
// Template matching: search radius around current position estimate (pixels)
public int MatchSearchRadius { get; set; } = 50;
public int MatchSearchRadius { get; set; } = 100;
// Template matching: minimum correlation confidence to accept a match
public double MatchConfidence { get; set; } = 0.3;
// Wall confidence (canvas-level): per-pixel counters to filter transient noise
public int ConfidenceInc { get; set; } = 3;
public int ConfidenceDec { get; set; } = 1;
public int ConfidenceThreshold { get; set; } = 8;
public int ConfidenceMax { get; set; } = 30;
public int WarmupFrames { get; set; } = 5;
// Frame dedup: min changed pixels to process a frame (skip near-identical minimap frames)
public int FrameChangeThreshold { get; set; } = 50;
// Stuck detection
public double StuckThreshold { get; set; } = 2.0;
public int StuckFrameCount { get; set; } = 5;

View file

@ -7,8 +7,10 @@ public class WorldMap : IDisposable
{
private readonly MinimapConfig _config;
private readonly Mat _canvas;
private readonly Mat _confidence; // CV_16SC1: per-pixel wall confidence counter
private MapPosition _position;
private int _frameCount;
private Mat? _prevWallMask; // for frame deduplication
public MapPosition Position => _position;
@ -16,6 +18,7 @@ public class WorldMap : IDisposable
{
_config = config;
_canvas = new Mat(config.CanvasSize, config.CanvasSize, MatType.CV_8UC1, Scalar.Black);
_confidence = new Mat(config.CanvasSize, config.CanvasSize, MatType.CV_16SC1, Scalar.Black);
_position = new MapPosition(config.CanvasSize / 2.0, config.CanvasSize / 2.0);
}
@ -27,19 +30,37 @@ public class WorldMap : IDisposable
{
_frameCount++;
// First frame: just stitch at center
if (_frameCount <= 1)
// Frame deduplication: skip if minimap hasn't scrolled yet
if (_prevWallMask != null && _frameCount > 1)
{
Stitch(classifiedMat, _position);
using var xor = new Mat();
Cv2.BitwiseXor(wallMask, _prevWallMask, xor);
var changedPixels = Cv2.CountNonZero(xor);
if (changedPixels < _config.FrameChangeThreshold)
{
Log.Debug("Frame dedup: {Changed} changed pixels, skipping", changedPixels);
return _position;
}
}
// Store current wall mask for next frame's dedup check
_prevWallMask?.Dispose();
_prevWallMask = wallMask.Clone();
// Warmup: stitch at center with boosted confidence to bootstrap canvas
if (_frameCount <= _config.WarmupFrames)
{
StitchWithConfidence(classifiedMat, _position, boosted: true);
return _position;
}
// Match wallMask against canvas to find best position
var matched = MatchPosition(wallMask, _position);
if (matched != null)
_position = matched;
if (matched == null)
return _position; // skip stitching entirely on failed match
Stitch(classifiedMat, _position);
_position = matched;
StitchWithConfidence(classifiedMat, _position, boosted: false);
return _position;
}
@ -100,7 +121,7 @@ public class WorldMap : IDisposable
return new MapPosition(matchX, matchY);
}
private void Stitch(Mat classifiedMat, MapPosition position)
private void StitchWithConfidence(Mat classifiedMat, MapPosition position, bool boosted)
{
var halfSize = _config.CaptureSize / 2;
var canvasX = (int)Math.Round(position.X) - halfSize;
@ -121,14 +142,38 @@ public class WorldMap : IDisposable
var srcRoi = new Mat(classifiedMat, srcRect);
var dstRoi = new Mat(_canvas, dstRect);
var confRoi = new Mat(_confidence, dstRect);
var confInc = (short)_config.ConfidenceInc;
var confDec = (short)_config.ConfidenceDec;
var confThreshold = (short)_config.ConfidenceThreshold;
var confMax = (short)_config.ConfidenceMax;
// Paste wall pixels (walls always win)
for (var row = 0; row < h; row++)
for (var col = 0; col < w; col++)
{
var srcVal = srcRoi.At<byte>(row, col);
var conf = confRoi.At<short>(row, col);
if (srcVal == (byte)MapCell.Wall)
dstRoi.Set(row, col, srcVal);
{
if (boosted)
conf = confThreshold; // warmup: immediately at threshold
else
conf = Math.Min((short)(conf + confInc), confMax);
}
else
{
// Pixel is in visible area but not wall — decay confidence
conf = Math.Max((short)(conf - confDec), (short)0);
}
confRoi.Set(row, col, conf);
if (conf >= confThreshold)
dstRoi.Set(row, col, (byte)MapCell.Wall);
else if (dstRoi.At<byte>(row, col) == (byte)MapCell.Wall)
dstRoi.Set(row, col, (byte)MapCell.Explored); // lost confidence, downgrade
}
// Mark explored area: circle around player, only overwrite Unknown
@ -243,6 +288,9 @@ public class WorldMap : IDisposable
public void Reset()
{
_canvas.SetTo(Scalar.Black);
_confidence.SetTo(Scalar.Black);
_prevWallMask?.Dispose();
_prevWallMask = null;
_position = new MapPosition(_config.CanvasSize / 2.0, _config.CanvasSize / 2.0);
_frameCount = 0;
}
@ -250,5 +298,7 @@ public class WorldMap : IDisposable
public void Dispose()
{
_canvas.Dispose();
_confidence.Dispose();
_prevWallMask?.Dispose();
}
}