work on minimap
This commit is contained in:
parent
a152a5cead
commit
6bc3fb6972
3 changed files with 39 additions and 66 deletions
|
|
@ -70,15 +70,11 @@ public class MinimapCapture : IDisposable
|
|||
// --- 3. Wall mask: bright OR saturated → structure lines ---
|
||||
using var wallMask = BuildWallMask(satChan, valueChan, playerMask);
|
||||
|
||||
// --- 4. Explored mask: brightness above fog-of-war, minus walls/player ---
|
||||
using var exploredMask = BuildExploredMask(valueChan, wallMask, playerMask);
|
||||
|
||||
// --- 5. Build raw classified mat ---
|
||||
// --- 4. Build classified mat (walls only — explored is tracked by WorldMap) ---
|
||||
var classified = new Mat(_config.CaptureSize, _config.CaptureSize, MatType.CV_8UC1, Scalar.Black);
|
||||
classified.SetTo(new Scalar((byte)MapCell.Explored), exploredMask);
|
||||
classified.SetTo(new Scalar((byte)MapCell.Wall), wallMask);
|
||||
|
||||
// --- 6. Temporal smoothing: majority vote ---
|
||||
// --- 5. Temporal smoothing: majority vote on walls ---
|
||||
var smoothed = TemporalSmooth(classified); // classified goes into ring buffer
|
||||
|
||||
// --- 7. Gray for phase correlation (player zeroed — it stays centered, walls shift with map) ---
|
||||
|
|
@ -118,23 +114,6 @@ public class MinimapCapture : IDisposable
|
|||
return wallMask;
|
||||
}
|
||||
|
||||
private Mat BuildExploredMask(Mat valueChan, Mat wallMask, Mat playerMask)
|
||||
{
|
||||
// Explored = any pixel above fog-of-war darkness, minus walls and player
|
||||
var exploredMask = new Mat();
|
||||
Cv2.Threshold(valueChan, exploredMask, _config.FloorMinValue, 255, ThresholdTypes.Binary);
|
||||
|
||||
using var notWall = new Mat();
|
||||
Cv2.BitwiseNot(wallMask, notWall);
|
||||
Cv2.BitwiseAnd(exploredMask, notWall, exploredMask);
|
||||
|
||||
using var notPlayer = new Mat();
|
||||
Cv2.BitwiseNot(playerMask, notPlayer);
|
||||
Cv2.BitwiseAnd(exploredMask, notPlayer, exploredMask);
|
||||
|
||||
return exploredMask;
|
||||
}
|
||||
|
||||
private static void FilterSmallComponents(Mat mask, int minArea)
|
||||
{
|
||||
if (minArea <= 0) return;
|
||||
|
|
@ -172,31 +151,21 @@ public class MinimapCapture : IDisposable
|
|||
|
||||
var size = classified.Size();
|
||||
using var wallCount = new Mat(size, MatType.CV_8UC1, Scalar.Black);
|
||||
using var exploredCount = 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);
|
||||
|
||||
using var isExplored = new Mat();
|
||||
Cv2.Compare(frame, new Scalar((byte)MapCell.Explored), isExplored, CmpType.EQ);
|
||||
Cv2.Add(exploredCount, new Scalar(1), exploredCount, isExplored);
|
||||
}
|
||||
|
||||
// Apply vote thresholds
|
||||
// Apply vote threshold
|
||||
using var wallPass = new Mat();
|
||||
Cv2.Threshold(wallCount, wallPass,
|
||||
_config.WallTemporalThreshold - 0.5, 255, ThresholdTypes.Binary);
|
||||
using var exploredPass = new Mat();
|
||||
Cv2.Threshold(exploredCount, exploredPass,
|
||||
_config.ExploredTemporalThreshold - 0.5, 255, ThresholdTypes.Binary);
|
||||
|
||||
// Build smoothed result (explored wins over wall)
|
||||
var result = new Mat(size, MatType.CV_8UC1, Scalar.Black);
|
||||
result.SetTo(new Scalar((byte)MapCell.Wall), wallPass);
|
||||
result.SetTo(new Scalar((byte)MapCell.Explored), exploredPass);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
@ -236,12 +205,8 @@ public class MinimapCapture : IDisposable
|
|||
using var wallMask = BuildWallMask(satChan, valueChan, playerMask);
|
||||
if (stage == MinimapDebugStage.Walls) return EncodePng(wallMask);
|
||||
|
||||
using var exploredMask = BuildExploredMask(valueChan, wallMask, playerMask);
|
||||
if (stage == MinimapDebugStage.Explored) return EncodePng(exploredMask);
|
||||
|
||||
// Classified
|
||||
// Classified (walls + player only — explored is tracked by WorldMap)
|
||||
using var classified = new Mat(_config.CaptureSize, _config.CaptureSize, MatType.CV_8UC3, Scalar.Black);
|
||||
classified.SetTo(new Scalar(104, 64, 31), exploredMask);
|
||||
classified.SetTo(new Scalar(26, 45, 61), wallMask);
|
||||
classified.SetTo(new Scalar(0, 165, 255), playerMask);
|
||||
return EncodePng(classified);
|
||||
|
|
@ -275,25 +240,22 @@ public class MinimapCapture : IDisposable
|
|||
Cv2.InRange(hsv, _config.PlayerLoHSV, _config.PlayerHiHSV, playerMask);
|
||||
|
||||
using var wallMask = BuildWallMask(satChan, valueChan, playerMask);
|
||||
using var exploredMask = BuildExploredMask(valueChan, wallMask, playerMask);
|
||||
|
||||
// Colorized classified
|
||||
// Colorized classified (walls + player)
|
||||
using var classified = new Mat(_config.CaptureSize, _config.CaptureSize, MatType.CV_8UC3, Scalar.Black);
|
||||
classified.SetTo(new Scalar(104, 64, 31), exploredMask); // blue
|
||||
classified.SetTo(new Scalar(26, 45, 61), wallMask); // brown
|
||||
classified.SetTo(new Scalar(0, 165, 255), playerMask); // orange
|
||||
|
||||
Cv2.ImWrite(Path.Combine(dir, "01-raw.png"), bgr);
|
||||
Cv2.ImWrite(Path.Combine(dir, "02-walls.png"), wallMask);
|
||||
Cv2.ImWrite(Path.Combine(dir, "03-explored.png"), exploredMask);
|
||||
Cv2.ImWrite(Path.Combine(dir, "04-player.png"), playerMask);
|
||||
Cv2.ImWrite(Path.Combine(dir, "05-classified.png"), classified);
|
||||
Cv2.ImWrite(Path.Combine(dir, "03-player.png"), playerMask);
|
||||
Cv2.ImWrite(Path.Combine(dir, "04-classified.png"), classified);
|
||||
|
||||
// HSV channels
|
||||
var channels = Cv2.Split(hsv);
|
||||
Cv2.ImWrite(Path.Combine(dir, "06-hue.png"), channels[0]);
|
||||
Cv2.ImWrite(Path.Combine(dir, "07-sat.png"), channels[1]);
|
||||
Cv2.ImWrite(Path.Combine(dir, "08-val.png"), channels[2]);
|
||||
Cv2.ImWrite(Path.Combine(dir, "05-hue.png"), channels[0]);
|
||||
Cv2.ImWrite(Path.Combine(dir, "06-sat.png"), channels[1]);
|
||||
Cv2.ImWrite(Path.Combine(dir, "07-val.png"), channels[2]);
|
||||
foreach (var c in channels) c.Dispose();
|
||||
|
||||
Log.Information("Debug minimap images saved to {Dir}", Path.GetFullPath(dir));
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ public enum MinimapDebugStage
|
|||
WorldMap,
|
||||
Raw,
|
||||
Walls,
|
||||
Explored,
|
||||
Player,
|
||||
Classified,
|
||||
Hue,
|
||||
|
|
@ -73,16 +72,15 @@ public class MinimapConfig
|
|||
public int WallMinValue { get; set; } = 200;
|
||||
public int WallMinSat { get; set; } = 150;
|
||||
|
||||
// Floor detection: minimum V to distinguish explored floor from fog-of-war
|
||||
public int FloorMinValue { get; set; } = 40;
|
||||
|
||||
// Connected components: minimum area to keep (kills speckle)
|
||||
public int WallMinArea { get; set; } = 30;
|
||||
|
||||
// Temporal smoothing: majority vote over ring buffer
|
||||
// Explored radius: pixels around player position to mark as explored on world map
|
||||
public int ExploredRadius { get; set; } = 100;
|
||||
|
||||
// Temporal smoothing: majority vote over ring buffer (walls only)
|
||||
public int TemporalFrameCount { get; set; } = 5;
|
||||
public int WallTemporalThreshold { get; set; } = 3;
|
||||
public int ExploredTemporalThreshold { get; set; } = 2;
|
||||
|
||||
// Capture rate (~30 fps)
|
||||
public int CaptureIntervalMs { get; set; } = 33;
|
||||
|
|
|
|||
|
|
@ -36,21 +36,34 @@ public class WorldMap : IDisposable
|
|||
var srcRoi = new Mat(classifiedMat, srcRect);
|
||||
var dstRoi = new Mat(_canvas, dstRect);
|
||||
|
||||
// Only paste non-Unknown pixels; don't overwrite Explored with Wall
|
||||
// Paste wall pixels (walls always win)
|
||||
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);
|
||||
if (srcVal == (byte)MapCell.Unknown) continue;
|
||||
|
||||
var dstVal = dstRoi.At<byte>(row, col);
|
||||
// Don't overwrite Explored with Wall (Explored is more reliable)
|
||||
if (dstVal == (byte)MapCell.Explored && srcVal == (byte)MapCell.Wall)
|
||||
continue;
|
||||
|
||||
var srcVal = srcRoi.At<byte>(row, col);
|
||||
if (srcVal == (byte)MapCell.Wall)
|
||||
dstRoi.Set(row, col, srcVal);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark explored area: circle around player, only overwrite Unknown
|
||||
var pcx = (int)Math.Round(position.X);
|
||||
var pcy = (int)Math.Round(position.Y);
|
||||
var r = _config.ExploredRadius;
|
||||
var r2 = r * r;
|
||||
|
||||
var y0 = Math.Max(0, pcy - r);
|
||||
var y1 = Math.Min(_config.CanvasSize - 1, pcy + r);
|
||||
var x0 = Math.Max(0, pcx - r);
|
||||
var x1 = Math.Min(_config.CanvasSize - 1, pcx + r);
|
||||
|
||||
for (var y = y0; y <= y1; y++)
|
||||
for (var x = x0; x <= x1; x++)
|
||||
{
|
||||
var dx = x - pcx;
|
||||
var dy = y - pcy;
|
||||
if (dx * dx + dy * dy > r2) continue;
|
||||
if (_canvas.At<byte>(y, x) == (byte)MapCell.Unknown)
|
||||
_canvas.Set(y, x, (byte)MapCell.Explored);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue