Calibrate.cs 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. using System;
  2. using System.Linq;
  3. using System.Threading;
  4. using System.Collections.Generic;
  5. using AutoFocusTool.Devices;
  6. using AutoFocusTool.Serial;
  7. using AutoFocusTool.Imaging;
  8. using AutoFocusTool.Calib;
  9. using SerialCamera = AutoFocusTool.Camera.Camera;
  10. namespace AutoFocusTool
  11. {
  12. /// <summary>
  13. /// 自动标定(连机):用 CalibrationEngine 对舱室的若干well做
  14. /// 居中→曝光→对焦,结果写 calibration.json。
  15. /// 用法:Calibrate.exe [houseSn=9] [wellFrom=1] [wellTo=2]
  16. /// </summary>
  17. internal class Calibrate
  18. {
  19. const int W = 2592, H = 1944; // MVC2000原生分辨率(参考图实测2592x1944),1600x1200会裁成中央块
  20. const string OutDir = @"C:\claudeFile\TL\AutoFocusTool\calib";
  21. const string JsonPath = @"C:\claudeFile\TL\AutoFocusTool\calibration.json";
  22. static void Main(string[] args)
  23. {
  24. Console.OutputEncoding = System.Text.Encoding.UTF8;
  25. void L(string m) => Console.WriteLine($"{DateTime.Now:HH:mm:ss} {m}");
  26. System.IO.Directory.CreateDirectory(OutDir);
  27. int houseSn = args.Length > 0 && int.TryParse(args[0], out int hs) ? hs : 9;
  28. int wFrom = args.Length > 1 && int.TryParse(args[1], out int a) ? a : 1;
  29. int wTo = args.Length > 2 && int.TryParse(args[2], out int b) ? b : 2;
  30. L($"==== 自动标定 舱{houseSn} well{wFrom}~{wTo} ====");
  31. var scanner = new DeviceScanner { Log = _ => { } };
  32. var houses = scanner.ScanAll();
  33. var h = houses.FirstOrDefault(x => x.HouseSn == houseSn && x.HasCamera);
  34. if (h == null) { L("未找到该舱或无相机"); return; }
  35. using var motor = new HouseMotor(h.PortName) { Log = _ => { }, MotorDelayMs = 1500 };
  36. motor.Open();
  37. using var cam = new SerialCamera(h.CcdIndex, W, H, 120);
  38. if (cam.Init() != 0) { L("相机初始化失败"); return; }
  39. cam.SetOpMode(0);
  40. motor.OpenLED();
  41. Thread.Sleep(300);
  42. var engine = new CalibrationEngine(motor, cam)
  43. {
  44. Log = L,
  45. OnFrame = b => { /* 命令行下不显示,GUI会用 */ },
  46. DebugSave = (b, name) => { try { Diag.SaveBmp(b, W, H, $@"C:\claudeFile\TL\AutoFocusTool\hscan\{name}.bmp"); } catch { } },
  47. };
  48. var houseCalib = new HouseCalib { House = houseSn, Port = h.PortName, CcdIndex = h.CcdIndex, CcdSn = h.CcdSn };
  49. int saveCounter = 0;
  50. for (int well = wFrom; well <= wTo; well++)
  51. {
  52. int hpos = motor.ReadWellHorizontalPos(well);
  53. int zZero = motor.ReadWellFocusZero(well);
  54. L($"\n── well{well} EEPROM: 水平={hpos} Z零点={zZero} ──");
  55. if (hpos < 0) { L("读well水平位置失败,跳过"); continue; }
  56. if (zZero < 0) zZero = 0;
  57. // 存关键帧:标定完well后存最终图
  58. engine.OnFrame = bb => { };
  59. var wc = engine.CalibrateWell(well, hpos, zZero);
  60. houseCalib.Wells.Add(wc);
  61. // 存最清晰层终图
  62. cam.SetExposure(wc.Exposure);
  63. motor.HorizontalMoveTo(wc.HorizontalPulse);
  64. motor.VerticalMoveTo(wc.FocusZ);
  65. Thread.Sleep(150); cam.GrabRgb();
  66. Diag.SaveBmp(cam.GetSourceBuffer(), W, H, $"{OutDir}\\cal_h{houseSn}_w{well}_FINAL.bmp");
  67. saveCounter++;
  68. }
  69. motor.CloseLED();
  70. // 写档案(合并已有)
  71. var file = new CalibrationFile { TlSn = $"house{houseSn}", Date = DateTime.Now.ToString("yyyy-MM-dd HH:mm") };
  72. file.Houses.Add(houseCalib);
  73. file.Save(JsonPath);
  74. L($"\n==== 标定完成,写入 {JsonPath} ====");
  75. foreach (var w in houseCalib.Wells)
  76. L($" well{w.Well}: 水平={w.HorizontalPulse} 曝光={w.Exposure} 对焦Z={w.FocusZ} 偏移={w.CenterOffsetPct:F1}% 峰比={w.PeakRatio:F2} {w.Note}");
  77. }
  78. }
  79. }