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; public int CanvasSize { get; set; } = 4000;
// Template matching: search radius around current position estimate (pixels) // 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 // Template matching: minimum correlation confidence to accept a match
public double MatchConfidence { get; set; } = 0.3; 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 // Stuck detection
public double StuckThreshold { get; set; } = 2.0; public double StuckThreshold { get; set; } = 2.0;
public int StuckFrameCount { get; set; } = 5; public int StuckFrameCount { get; set; } = 5;

View file

@ -7,8 +7,10 @@ public class WorldMap : IDisposable
{ {
private readonly MinimapConfig _config; private readonly MinimapConfig _config;
private readonly Mat _canvas; private readonly Mat _canvas;
private readonly Mat _confidence; // CV_16SC1: per-pixel wall confidence counter
private MapPosition _position; private MapPosition _position;
private int _frameCount; private int _frameCount;
private Mat? _prevWallMask; // for frame deduplication
public MapPosition Position => _position; public MapPosition Position => _position;
@ -16,6 +18,7 @@ public class WorldMap : IDisposable
{ {
_config = config; _config = config;
_canvas = new Mat(config.CanvasSize, config.CanvasSize, MatType.CV_8UC1, Scalar.Black); _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); _position = new MapPosition(config.CanvasSize / 2.0, config.CanvasSize / 2.0);
} }
@ -27,19 +30,37 @@ public class WorldMap : IDisposable
{ {
_frameCount++; _frameCount++;
// First frame: just stitch at center // Frame deduplication: skip if minimap hasn't scrolled yet
if (_frameCount <= 1) 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; return _position;
} }
// Match wallMask against canvas to find best position // Match wallMask against canvas to find best position
var matched = MatchPosition(wallMask, _position); var matched = MatchPosition(wallMask, _position);
if (matched != null) if (matched == null)
_position = matched; return _position; // skip stitching entirely on failed match
Stitch(classifiedMat, _position); _position = matched;
StitchWithConfidence(classifiedMat, _position, boosted: false);
return _position; return _position;
} }
@ -100,7 +121,7 @@ public class WorldMap : IDisposable
return new MapPosition(matchX, matchY); 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 halfSize = _config.CaptureSize / 2;
var canvasX = (int)Math.Round(position.X) - halfSize; var canvasX = (int)Math.Round(position.X) - halfSize;
@ -121,14 +142,38 @@ public class WorldMap : IDisposable
var srcRoi = new Mat(classifiedMat, srcRect); var srcRoi = new Mat(classifiedMat, srcRect);
var dstRoi = new Mat(_canvas, dstRect); 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 row = 0; row < h; row++)
for (var col = 0; col < w; col++) for (var col = 0; col < w; col++)
{ {
var srcVal = srcRoi.At<byte>(row, col); var srcVal = srcRoi.At<byte>(row, col);
var conf = confRoi.At<short>(row, col);
if (srcVal == (byte)MapCell.Wall) 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 // Mark explored area: circle around player, only overwrite Unknown
@ -243,6 +288,9 @@ public class WorldMap : IDisposable
public void Reset() public void Reset()
{ {
_canvas.SetTo(Scalar.Black); _canvas.SetTo(Scalar.Black);
_confidence.SetTo(Scalar.Black);
_prevWallMask?.Dispose();
_prevWallMask = null;
_position = new MapPosition(_config.CanvasSize / 2.0, _config.CanvasSize / 2.0); _position = new MapPosition(_config.CanvasSize / 2.0, _config.CanvasSize / 2.0);
_frameCount = 0; _frameCount = 0;
} }
@ -250,5 +298,7 @@ public class WorldMap : IDisposable
public void Dispose() public void Dispose()
{ {
_canvas.Dispose(); _canvas.Dispose();
_confidence.Dispose();
_prevWallMask?.Dispose();
} }
} }