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 ---
|
// --- 3. Wall mask: bright OR saturated → structure lines ---
|
||||||
using var wallMask = BuildWallMask(satChan, valueChan, playerMask);
|
using var wallMask = BuildWallMask(satChan, valueChan, playerMask);
|
||||||
|
|
||||||
// --- 4. Explored mask: brightness above fog-of-war, minus walls/player ---
|
// --- 4. Build classified mat (walls only — explored is tracked by WorldMap) ---
|
||||||
using var exploredMask = BuildExploredMask(valueChan, wallMask, playerMask);
|
|
||||||
|
|
||||||
// --- 5. Build raw classified mat ---
|
|
||||||
var classified = new Mat(_config.CaptureSize, _config.CaptureSize, MatType.CV_8UC1, Scalar.Black);
|
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);
|
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
|
var smoothed = TemporalSmooth(classified); // classified goes into ring buffer
|
||||||
|
|
||||||
// --- 7. Gray for phase correlation (player zeroed — it stays centered, walls shift with map) ---
|
// --- 7. Gray for phase correlation (player zeroed — it stays centered, walls shift with map) ---
|
||||||
|
|
@ -118,23 +114,6 @@ public class MinimapCapture : IDisposable
|
||||||
return wallMask;
|
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)
|
private static void FilterSmallComponents(Mat mask, int minArea)
|
||||||
{
|
{
|
||||||
if (minArea <= 0) return;
|
if (minArea <= 0) return;
|
||||||
|
|
@ -172,31 +151,21 @@ public class MinimapCapture : IDisposable
|
||||||
|
|
||||||
var size = classified.Size();
|
var size = classified.Size();
|
||||||
using var wallCount = new Mat(size, MatType.CV_8UC1, Scalar.Black);
|
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)
|
foreach (var frame in _frameBuffer)
|
||||||
{
|
{
|
||||||
using var isWall = new Mat();
|
using var isWall = new Mat();
|
||||||
Cv2.Compare(frame, new Scalar((byte)MapCell.Wall), isWall, CmpType.EQ);
|
Cv2.Compare(frame, new Scalar((byte)MapCell.Wall), isWall, CmpType.EQ);
|
||||||
Cv2.Add(wallCount, new Scalar(1), wallCount, isWall);
|
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();
|
using var wallPass = new Mat();
|
||||||
Cv2.Threshold(wallCount, wallPass,
|
Cv2.Threshold(wallCount, wallPass,
|
||||||
_config.WallTemporalThreshold - 0.5, 255, ThresholdTypes.Binary);
|
_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);
|
var result = new Mat(size, MatType.CV_8UC1, Scalar.Black);
|
||||||
result.SetTo(new Scalar((byte)MapCell.Wall), wallPass);
|
result.SetTo(new Scalar((byte)MapCell.Wall), wallPass);
|
||||||
result.SetTo(new Scalar((byte)MapCell.Explored), exploredPass);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -236,12 +205,8 @@ public class MinimapCapture : IDisposable
|
||||||
using var wallMask = BuildWallMask(satChan, valueChan, playerMask);
|
using var wallMask = BuildWallMask(satChan, valueChan, playerMask);
|
||||||
if (stage == MinimapDebugStage.Walls) return EncodePng(wallMask);
|
if (stage == MinimapDebugStage.Walls) return EncodePng(wallMask);
|
||||||
|
|
||||||
using var exploredMask = BuildExploredMask(valueChan, wallMask, playerMask);
|
// Classified (walls + player only — explored is tracked by WorldMap)
|
||||||
if (stage == MinimapDebugStage.Explored) return EncodePng(exploredMask);
|
|
||||||
|
|
||||||
// Classified
|
|
||||||
using var classified = new Mat(_config.CaptureSize, _config.CaptureSize, MatType.CV_8UC3, Scalar.Black);
|
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(26, 45, 61), wallMask);
|
||||||
classified.SetTo(new Scalar(0, 165, 255), playerMask);
|
classified.SetTo(new Scalar(0, 165, 255), playerMask);
|
||||||
return EncodePng(classified);
|
return EncodePng(classified);
|
||||||
|
|
@ -275,25 +240,22 @@ public class MinimapCapture : IDisposable
|
||||||
Cv2.InRange(hsv, _config.PlayerLoHSV, _config.PlayerHiHSV, playerMask);
|
Cv2.InRange(hsv, _config.PlayerLoHSV, _config.PlayerHiHSV, playerMask);
|
||||||
|
|
||||||
using var wallMask = BuildWallMask(satChan, valueChan, 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);
|
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(26, 45, 61), wallMask); // brown
|
||||||
classified.SetTo(new Scalar(0, 165, 255), playerMask); // orange
|
classified.SetTo(new Scalar(0, 165, 255), playerMask); // orange
|
||||||
|
|
||||||
Cv2.ImWrite(Path.Combine(dir, "01-raw.png"), bgr);
|
Cv2.ImWrite(Path.Combine(dir, "01-raw.png"), bgr);
|
||||||
Cv2.ImWrite(Path.Combine(dir, "02-walls.png"), wallMask);
|
Cv2.ImWrite(Path.Combine(dir, "02-walls.png"), wallMask);
|
||||||
Cv2.ImWrite(Path.Combine(dir, "03-explored.png"), exploredMask);
|
Cv2.ImWrite(Path.Combine(dir, "03-player.png"), playerMask);
|
||||||
Cv2.ImWrite(Path.Combine(dir, "04-player.png"), playerMask);
|
Cv2.ImWrite(Path.Combine(dir, "04-classified.png"), classified);
|
||||||
Cv2.ImWrite(Path.Combine(dir, "05-classified.png"), classified);
|
|
||||||
|
|
||||||
// HSV channels
|
// HSV channels
|
||||||
var channels = Cv2.Split(hsv);
|
var channels = Cv2.Split(hsv);
|
||||||
Cv2.ImWrite(Path.Combine(dir, "06-hue.png"), channels[0]);
|
Cv2.ImWrite(Path.Combine(dir, "05-hue.png"), channels[0]);
|
||||||
Cv2.ImWrite(Path.Combine(dir, "07-sat.png"), channels[1]);
|
Cv2.ImWrite(Path.Combine(dir, "06-sat.png"), channels[1]);
|
||||||
Cv2.ImWrite(Path.Combine(dir, "08-val.png"), channels[2]);
|
Cv2.ImWrite(Path.Combine(dir, "07-val.png"), channels[2]);
|
||||||
foreach (var c in channels) c.Dispose();
|
foreach (var c in channels) c.Dispose();
|
||||||
|
|
||||||
Log.Information("Debug minimap images saved to {Dir}", Path.GetFullPath(dir));
|
Log.Information("Debug minimap images saved to {Dir}", Path.GetFullPath(dir));
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ public enum MinimapDebugStage
|
||||||
WorldMap,
|
WorldMap,
|
||||||
Raw,
|
Raw,
|
||||||
Walls,
|
Walls,
|
||||||
Explored,
|
|
||||||
Player,
|
Player,
|
||||||
Classified,
|
Classified,
|
||||||
Hue,
|
Hue,
|
||||||
|
|
@ -73,16 +72,15 @@ public class MinimapConfig
|
||||||
public int WallMinValue { get; set; } = 200;
|
public int WallMinValue { get; set; } = 200;
|
||||||
public int WallMinSat { get; set; } = 150;
|
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)
|
// Connected components: minimum area to keep (kills speckle)
|
||||||
public int WallMinArea { get; set; } = 30;
|
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 TemporalFrameCount { get; set; } = 5;
|
||||||
public int WallTemporalThreshold { get; set; } = 3;
|
public int WallTemporalThreshold { get; set; } = 3;
|
||||||
public int ExploredTemporalThreshold { get; set; } = 2;
|
|
||||||
|
|
||||||
// Capture rate (~30 fps)
|
// Capture rate (~30 fps)
|
||||||
public int CaptureIntervalMs { get; set; } = 33;
|
public int CaptureIntervalMs { get; set; } = 33;
|
||||||
|
|
|
||||||
|
|
@ -36,21 +36,34 @@ 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);
|
||||||
|
|
||||||
// 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 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.Wall)
|
||||||
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;
|
|
||||||
|
|
||||||
dstRoi.Set(row, col, srcVal);
|
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