work on well of souls and yolo detection

This commit is contained in:
Boki 2026-02-20 16:40:50 -05:00
parent 3456e0d62a
commit 40d30115bf
41 changed files with 3031 additions and 148 deletions

View file

@ -1,6 +1,10 @@
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using Poe2Trade.Core;
using OpenCvSharp.Extensions;
using Serilog;
using Region = Poe2Trade.Core.Region;
namespace Poe2Trade.Screen;
@ -178,6 +182,144 @@ public class ScreenReader : IScreenReader
return Task.CompletedTask;
}
// -- Nameplate diff OCR --
public Bitmap CaptureRawBitmap() => ScreenCapture.CaptureOrLoad(null, null);
public Task<OcrResponse> NameplateDiffOcr(Bitmap reference, Bitmap current)
{
int w = Math.Min(reference.Width, current.Width);
int h = Math.Min(reference.Height, current.Height);
var refData = reference.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
var curData = current.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] refPx = new byte[refData.Stride * h];
byte[] curPx = new byte[curData.Stride * h];
Marshal.Copy(refData.Scan0, refPx, 0, refPx.Length);
Marshal.Copy(curData.Scan0, curPx, 0, curPx.Length);
int stride = refData.Stride;
reference.UnlockBits(refData);
current.UnlockBits(curData);
// Build a binary mask of pixels that got significantly brighter (nameplates are bright text)
const int brightThresh = 30;
bool[] mask = new bool[w * h];
Parallel.For(0, h, y =>
{
int rowOff = y * stride;
for (int x = 0; x < w; x++)
{
int i = rowOff + x * 4;
int brighter = (curPx[i] - refPx[i]) + (curPx[i + 1] - refPx[i + 1]) + (curPx[i + 2] - refPx[i + 2]);
if (brighter > brightThresh)
mask[y * w + x] = true;
}
});
// Find connected clusters via row-scan: collect bounding boxes of bright regions
var boxes = FindBrightClusters(mask, w, h, minWidth: 40, minHeight: 10, maxGap: 8);
Log.Information("NameplateDiff: found {Count} bright clusters", boxes.Count);
if (boxes.Count == 0)
return Task.FromResult(new OcrResponse { Text = "", Lines = [] });
// OCR each cluster crop, accumulate results with screen-space coordinates
var allLines = new List<OcrLine>();
var allText = new List<string>();
foreach (var box in boxes)
{
// Pad the crop slightly
int pad = 4;
int cx = Math.Max(0, box.X - pad);
int cy = Math.Max(0, box.Y - pad);
int cw = Math.Min(w - cx, box.Width + pad * 2);
int ch = Math.Min(h - cy, box.Height + pad * 2);
using var crop = current.Clone(new Rectangle(cx, cy, cw, ch), PixelFormat.Format32bppArgb);
var ocrResult = _pythonBridge.OcrFromBitmap(crop);
// Offset word coordinates to screen space
foreach (var line in ocrResult.Lines)
{
foreach (var word in line.Words)
{
word.X += cx;
word.Y += cy;
}
allLines.Add(line);
allText.Add(line.Text);
}
}
return Task.FromResult(new OcrResponse
{
Text = string.Join("\n", allText),
Lines = allLines,
});
}
private static List<Rectangle> FindBrightClusters(bool[] mask, int w, int h, int minWidth, int minHeight, int maxGap)
{
// Row density
int[] rowCounts = new int[h];
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++)
if (mask[y * w + x]) rowCounts[y]++;
// Find horizontal bands of bright rows
int rowThresh = 3;
var bands = new List<(int Top, int Bottom)>();
int bandStart = -1, lastActive = -1;
for (int y = 0; y < h; y++)
{
if (rowCounts[y] >= rowThresh)
{
if (bandStart < 0) bandStart = y;
lastActive = y;
}
else if (bandStart >= 0 && y - lastActive > maxGap)
{
if (lastActive - bandStart + 1 >= minHeight)
bands.Add((bandStart, lastActive));
bandStart = -1;
}
}
if (bandStart >= 0 && lastActive - bandStart + 1 >= minHeight)
bands.Add((bandStart, lastActive));
// For each band, find column extents to get individual nameplate boxes
var boxes = new List<Rectangle>();
foreach (var (top, bottom) in bands)
{
int[] colCounts = new int[w];
for (int y = top; y <= bottom; y++)
for (int x = 0; x < w; x++)
if (mask[y * w + x]) colCounts[x]++;
int colThresh = 1;
int colStart = -1, lastCol = -1;
for (int x = 0; x < w; x++)
{
if (colCounts[x] >= colThresh)
{
if (colStart < 0) colStart = x;
lastCol = x;
}
else if (colStart >= 0 && x - lastCol > maxGap)
{
if (lastCol - colStart + 1 >= minWidth)
boxes.Add(new Rectangle(colStart, top, lastCol - colStart + 1, bottom - top + 1));
colStart = -1;
}
}
if (colStart >= 0 && lastCol - colStart + 1 >= minWidth)
boxes.Add(new Rectangle(colStart, top, lastCol - colStart + 1, bottom - top + 1));
}
return boxes;
}
public void Dispose() => _pythonBridge.Dispose();
// -- OCR text matching --