From 07fe46c5965580ee2403f7a657978c1032514233 Mon Sep 17 00:00:00 2001 From: Boki Date: Fri, 13 Feb 2026 13:48:31 -0500 Subject: [PATCH] minimap working much better --- src/Poe2Trade.Navigation/MinimapCapture.cs | 56 ++++------------------ src/Poe2Trade.Navigation/WorldMap.cs | 26 ++++------ 2 files changed, 18 insertions(+), 64 deletions(-) diff --git a/src/Poe2Trade.Navigation/MinimapCapture.cs b/src/Poe2Trade.Navigation/MinimapCapture.cs index 8289e4d..2ce8f82 100644 --- a/src/Poe2Trade.Navigation/MinimapCapture.cs +++ b/src/Poe2Trade.Navigation/MinimapCapture.cs @@ -9,7 +9,6 @@ public class MinimapCapture : IDisposable { private readonly MinimapConfig _config; private readonly IScreenCapture _backend; - private readonly Queue _frameBuffer = new(); public MinimapCapture(MinimapConfig config) { @@ -68,28 +67,25 @@ public class MinimapCapture : IDisposable var playerOffset = FindCentroid(playerMask); // --- 3. Wall mask: bright OR saturated → structure lines --- - using var rawWallMask = BuildWallMask(satChan, valueChan, playerMask); + var wallMask = BuildWallMask(satChan, valueChan, playerMask); - // --- 4. Build classified mat (walls only — explored is tracked by WorldMap) --- + // --- 4. Build classified mat (walls only — for stitching) --- var classified = new Mat(_config.CaptureSize, _config.CaptureSize, MatType.CV_8UC1, Scalar.Black); - classified.SetTo(new Scalar((byte)MapCell.Wall), rawWallMask); + classified.SetTo(new Scalar((byte)MapCell.Wall), wallMask); - // --- 5. Temporal smoothing: majority vote on walls --- - var smoothed = TemporalSmooth(classified); // classified goes into ring buffer + // Raw walls used for everything: dedup, matching, and stitching. + // Temporal smoothing kills walls during movement (minimap scrolls between frames, + // 3/5 vote fails). The confidence system is the long-term noise filter instead. - // --- 6. Extract smoothed wall mask for tracking (filters transient noise) --- - var stableWallMask = new Mat(); - Cv2.Compare(smoothed, new Scalar((byte)MapCell.Wall), stableWallMask, CmpType.EQ); - - // --- 7. Gray for optical flow tracking (player zeroed) --- + // --- 5. Gray for optical flow tracking (player zeroed) --- var grayForCorr = new Mat(); Cv2.CvtColor(bgr, grayForCorr, ColorConversionCodes.BGR2GRAY); grayForCorr.SetTo(Scalar.Black, playerMask); return new MinimapFrame( GrayMat: grayForCorr, - WallMask: stableWallMask, - ClassifiedMat: smoothed, + WallMask: wallMask, // raw — for matching + dedup + ClassifiedMat: classified, // raw — for stitching (confidence filters noise) PlayerOffset: playerOffset, Timestamp: DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() ); @@ -157,38 +153,6 @@ public class MinimapCapture : IDisposable } } - private Mat TemporalSmooth(Mat classified) - { - // Add raw frame to ring buffer (takes ownership) - _frameBuffer.Enqueue(classified); - while (_frameBuffer.Count > _config.TemporalFrameCount) - _frameBuffer.Dequeue().Dispose(); - - // Not enough frames yet — return as-is - if (_frameBuffer.Count < 2) - return classified.Clone(); - - var size = classified.Size(); - using var wallCount = new Mat(size, MatType.CV_8UC1, Scalar.Black); - - foreach (var frame in _frameBuffer) - { - using var isWall = new Mat(); - Cv2.Compare(frame, new Scalar((byte)MapCell.Wall), isWall, CmpType.EQ); - Cv2.Add(wallCount, new Scalar(1), wallCount, isWall); - } - - // Apply vote threshold - using var wallPass = new Mat(); - Cv2.Threshold(wallCount, wallPass, - _config.WallTemporalThreshold - 0.5, 255, ThresholdTypes.Binary); - - var result = new Mat(size, MatType.CV_8UC1, Scalar.Black); - result.SetTo(new Scalar((byte)MapCell.Wall), wallPass); - - return result; - } - /// /// Capture a single frame and return the requested pipeline stage as PNG bytes. /// @@ -294,7 +258,5 @@ public class MinimapCapture : IDisposable public void Dispose() { _backend.Dispose(); - while (_frameBuffer.Count > 0) - _frameBuffer.Dequeue().Dispose(); } } diff --git a/src/Poe2Trade.Navigation/WorldMap.cs b/src/Poe2Trade.Navigation/WorldMap.cs index 746079a..56a01a9 100644 --- a/src/Poe2Trade.Navigation/WorldMap.cs +++ b/src/Poe2Trade.Navigation/WorldMap.cs @@ -173,35 +173,27 @@ public class WorldMap : IDisposable 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; + // Only increment confidence for wall pixels. Don't decay on non-wall pixels because + // temporal smoothing (3/5 vote) kills walls during movement — the minimap scrolls so + // wall pixels shift across frames and fail the vote. "Not wall" in the smoothed mat + // during movement means "couldn't confirm" not "definitely not a wall". for (var row = 0; row < h; row++) for (var col = 0; col < w; col++) { var srcVal = srcRoi.At(row, col); + if (srcVal != (byte)MapCell.Wall) continue; + var conf = confRoi.At(row, col); - - if (srcVal == (byte)MapCell.Wall) - { - if (boosted) - conf = confMax; // warmup: max confidence so walls survive initial movement - 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); - } - + conf = boosted + ? confMax + : Math.Min((short)(conf + confInc), confMax); confRoi.Set(row, col, conf); if (conf >= confThreshold) dstRoi.Set(row, col, (byte)MapCell.Wall); - else if (dstRoi.At(row, col) == (byte)MapCell.Wall) - dstRoi.Set(row, col, (byte)MapCell.Explored); // lost confidence, downgrade } // Mark explored area: circle around player, only overwrite Unknown