|
|
@@ -21,6 +21,8 @@ namespace AutoFocusTool
|
|
|
static int Main(string[] args)
|
|
|
{
|
|
|
Console.OutputEncoding = System.Text.Encoding.UTF8;
|
|
|
+ if (args.Length > 0 && args[0] == "zcurve")
|
|
|
+ return ZCurve(args);
|
|
|
string port = args.Length > 0 ? args[0] : "COM11";
|
|
|
int camIdx = args.Length > 1 && int.TryParse(args[1], out int ci) ? ci : 2;
|
|
|
int exposure = args.Length > 2 && int.TryParse(args[2], out int ex) ? ex : 80;
|
|
|
@@ -30,7 +32,7 @@ namespace AutoFocusTool
|
|
|
void L(string m) => Console.WriteLine($"{DateTime.Now:HH:mm:ss} {m}");
|
|
|
|
|
|
string stamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
|
|
- string outDir = $@"C:\claudeFile\TL\AutoFocusTool\calib_result\well_check_{stamp}";
|
|
|
+ string outDir = $@"C:\claudeFile\TL\AutoFocusTool\TestData\well_check_{stamp}";
|
|
|
Directory.CreateDirectory(outDir);
|
|
|
|
|
|
L($"========== well 实拍检测 {port} cam#{camIdx} 曝光{exposure} Z{zPos} ==========");
|
|
|
@@ -88,5 +90,84 @@ namespace AutoFocusTool
|
|
|
L($"========== 完成,图片在: {outDir} ==========");
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Z清晰度曲线扫描:移到指定well的EEPROM水平位置,从zLo到zHi逐层抓帧算清晰度,
|
|
|
+ /// 同时记录灰度均值(验证亮度归一化是否带偏),每层存图。
|
|
|
+ /// 用法:WellSpacing.exe zcurve <COM> <相机idx> <well> [zLo=20000] [zHi=120000] [zStep=2000] [曝光=60]
|
|
|
+ /// </summary>
|
|
|
+ static int ZCurve(string[] a)
|
|
|
+ {
|
|
|
+ string port = a.Length > 1 ? a[1] : "COM11";
|
|
|
+ int camIdx = a.Length > 2 && int.TryParse(a[2], out int ci) ? ci : 2;
|
|
|
+ int well = a.Length > 3 && int.TryParse(a[3], out int wv) ? wv : 1;
|
|
|
+ int zLo = a.Length > 4 && int.TryParse(a[4], out int v4) ? v4 : 20000;
|
|
|
+ int zHi = a.Length > 5 && int.TryParse(a[5], out int v5) ? v5 : 120000;
|
|
|
+ int zStep = a.Length > 6 && int.TryParse(a[6], out int v6) ? v6 : 2000;
|
|
|
+ int exposure = a.Length > 7 && int.TryParse(a[7], out int v7) ? v7 : 60;
|
|
|
+ int camW = 2592, camH = 1944;
|
|
|
+ void L(string m) => Console.WriteLine($"{DateTime.Now:HH:mm:ss} {m}");
|
|
|
+
|
|
|
+ string stamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
|
|
+ string outDir = $@"C:\claudeFile\TL\AutoFocusTool\TestData\zcurve_w{well}_{stamp}";
|
|
|
+ Directory.CreateDirectory(outDir);
|
|
|
+ L($"==== Z清晰度曲线 well{well} {port} cam#{camIdx} Z[{zLo},{zHi}] step{zStep} 曝光{exposure} ====");
|
|
|
+ L($"存图: {outDir}");
|
|
|
+
|
|
|
+ var motor = new HouseMotor(port) { Log = null };
|
|
|
+ if (!motor.Open()) { L($"✗ 打开 {port} 失败"); return 1; }
|
|
|
+ motor.MotorDelayMs = 1500;
|
|
|
+ int sn = motor.ShakeHands();
|
|
|
+ L($"握手 houseSn={sn}");
|
|
|
+ int hpos = motor.ReadWellHorizontalPos(well);
|
|
|
+ if (hpos < 0) { L("✗ 读well水平位置失败"); motor.Close(); return 1; }
|
|
|
+
|
|
|
+ var cam = new SerialCamera(camIdx, camW, camH, exposure);
|
|
|
+ if (cam.Init() != 0) { L("✗ 相机初始化失败"); motor.Close(); return 2; }
|
|
|
+ cam.SetOpMode(0); cam.SetExposure(exposure);
|
|
|
+ motor.OpenLED(); Thread.Sleep(300);
|
|
|
+ motor.HorizontalMoveTo(hpos, 1500);
|
|
|
+ L($"已移到 well{well} 水平={hpos}");
|
|
|
+
|
|
|
+ double bestScore = -1; int bestZ = zLo;
|
|
|
+ try
|
|
|
+ {
|
|
|
+ for (int z = zLo; z <= zHi; z += zStep)
|
|
|
+ {
|
|
|
+ motor.VerticalMoveTo(z, 1500);
|
|
|
+ Thread.Sleep(150);
|
|
|
+ cam.GrabRgb(); Thread.Sleep(80); cam.GrabRgb();
|
|
|
+ byte[] buf = cam.GetSourceBuffer();
|
|
|
+
|
|
|
+ int roiW = (int)(camW * 0.4), roiH = (int)(camH * 0.4);
|
|
|
+ var roi = new System.Drawing.Rectangle((camW - roiW) / 2, (camH - roiH) / 2, roiW, roiH);
|
|
|
+ double score = Sharpness.Compute(buf, camW, camH, roi);
|
|
|
+ double mean = GrayMean(buf, camW, camH, roi);
|
|
|
+ L($"z={z,6} 归一化分={score:F5} 亮度均值={mean:F1}");
|
|
|
+ if (score > bestScore) { bestScore = score; bestZ = z; }
|
|
|
+
|
|
|
+ ImageConverter.SaveBmp(buf, camW, camH,
|
|
|
+ Path.Combine(outDir, $"z{z}_s{score:F4}.bmp"));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ finally { motor.CloseLED(); cam.Dispose(); motor.Close(); }
|
|
|
+
|
|
|
+ L($"==== 峰值 z={bestZ} 分={bestScore:F5} 图在 {outDir} ====");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>中央ROI灰度均值(BGR→灰度,行优先)。</summary>
|
|
|
+ static double GrayMean(byte[] bgr, int W, int H, System.Drawing.Rectangle roi)
|
|
|
+ {
|
|
|
+ long sum = 0; int n = 0; int stride = W * 3;
|
|
|
+ for (int y = roi.Y; y < roi.Y + roi.Height && y < H; y++)
|
|
|
+ for (int x = roi.X; x < roi.X + roi.Width && x < W; x++)
|
|
|
+ {
|
|
|
+ int p = y * stride + x * 3;
|
|
|
+ sum += (bgr[p] * 29 + bgr[p + 1] * 150 + bgr[p + 2] * 77) >> 8;
|
|
|
+ n++;
|
|
|
+ }
|
|
|
+ return n > 0 ? (double)sum / n : 0;
|
|
|
+ }
|
|
|
}
|
|
|
}
|