using System;
using System.IO;
using System.Linq;
using AutoFocusTool.Imaging;
namespace AutoFocusTool
{
///
/// 离线验证标定算法:读已存的BMP → WellDetector找圆心 → ExposureMeter评曝光。
/// 不连机器,快速验证算法对真实图是否有效。
/// 用法:CalibTest.exe [目录或文件glob]
///
internal class CalibTest
{
static void Main(string[] args)
{
Console.OutputEncoding = System.Text.Encoding.UTF8;
string dir = args.Length > 0 ? args[0] : @"C:\claudeFile\TL\AutoFocusTool\survey";
string pat = args.Length > 1 ? args[1] : "h9_w2_*.bmp";
var files = Directory.GetFiles(dir, pat).OrderBy(f => f).ToList();
Console.WriteLine($"== 离线标定算法验证: {files.Count} 张图 ({pat}) ==");
Console.WriteLine($"{"文件",-22} {"圆心X",6} {"圆心Y",6} {"半径",6} {"偏移X%",7} {"偏移Y%",7} {"完整",5} {"曝光均值",8} {"饱和%",6} {"曝光判定",8} {"清晰度",8}");
foreach (var f in files)
{
var (buf, w, h) = ReadBmp24(f);
if (buf == null) { Console.WriteLine($"{Path.GetFileName(f)}: 读取失败"); continue; }
var circle = WellDetector.Detect(buf, w, h);
var exp = ExposureMeter.Measure(buf, w, h, circle);
double sharp = Sharpness.Compute(buf, w, h,
circle.Found ? RoiOf(circle, w, h) : (System.Drawing.Rectangle?)null);
string name = Path.GetFileNameWithoutExtension(f);
if (circle.Found)
Console.WriteLine($"{name,-22} {circle.Cx,6:F0} {circle.Cy,6:F0} {circle.Radius,6:F0} {circle.OffsetXPct,7:F1} {circle.OffsetYPct,7:F1} {(circle.Complete ? "是" : "否"),5} {exp.Mean,8:F0} {exp.SaturatedPct,6:F1} {exp.State,8} {sharp,8:F4}");
else
Console.WriteLine($"{name,-22} 未检出well圆 曝光均值={exp.Mean:F0} 拒绝原因[{circle.RejectReason}] 面积={circle.AreaPct:F1}%");
}
}
// well圆内ROI(内部70%)做清晰度
static System.Drawing.Rectangle RoiOf(WellCircle c, int w, int h)
{
int r = (int)(c.Radius * 0.7);
int x = Math.Max(0, (int)c.Cx - r), y = Math.Max(0, (int)c.Cy - r);
int ww = Math.Min(2 * r, w - x), hh = Math.Min(2 * r, h - y);
return new System.Drawing.Rectangle(x, y, ww, hh);
}
/// 读24bpp BMP → (BGR字节[自顶向下], 宽, 高)。我们存图时h>0自底向上,这里翻回自顶向下。
static (byte[], int, int) ReadBmp24(string path)
{
try
{
byte[] all = File.ReadAllBytes(path);
if (all.Length < 54 || all[0] != 'B' || all[1] != 'M') return (null, 0, 0);
int off = BitConverter.ToInt32(all, 10);
int w = BitConverter.ToInt32(all, 18);
int h = BitConverter.ToInt32(all, 22);
short bpp = BitConverter.ToInt16(all, 28);
if (bpp != 24) return (null, 0, 0);
bool bottomUp = h > 0; h = Math.Abs(h);
int rowSize = ((w * 3 + 3) / 4) * 4;
var buf = new byte[w * h * 3];
for (int y = 0; y < h; y++)
{
int srcRow = off + (bottomUp ? (h - 1 - y) : y) * rowSize;
Buffer.BlockCopy(all, srcRow, buf, y * w * 3, w * 3);
}
return (buf, w, h);
}
catch { return (null, 0, 0); }
}
}
}