using System; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Media; using System.Windows.Media.Imaging; using Microsoft.Win32; using AutoFocusTool.Imaging; namespace AutoFocusTool { // 抓帧显示 / 实时预览 / 曝光增益 / 光源 / 存图 public partial class MainWindow { private byte[] _lastFrame; // 最近一帧原始 BGR /// 抓一帧并刷新显示+清晰度分。返回是否成功。 private bool GrabAndShow() { if (_camera == null) { Log("无相机。"); return false; } int r = _camera.GrabRgb(); if (r != 0) { Log($"抓帧失败(code={r})"); return false; } byte[] buf = _camera.GetSourceBuffer(); _lastFrame = buf; // 中心 ROI(画面中央 50%) var roi = CenterRoi(_camWidth, _camHeight, 0.5); double score = Sharpness.Compute(buf, _camWidth, _camHeight, roi); var bmp = ImageConverter.ToBitmapSource(buf, _camWidth, _camHeight); Dispatcher.Invoke(() => { ImgPreview.Source = bmp; TxtScore.Text = $"清晰度: {score:F1}"; DrawRoiOverlay(roi); }); return true; } private static System.Drawing.Rectangle CenterRoi(int w, int h, double frac) { int rw = (int)(w * frac), rh = (int)(h * frac); return new System.Drawing.Rectangle((w - rw) / 2, (h - rh) / 2, rw, rh); } // ── 抓一帧 ── private void BtnGrab_Click(object sender, RoutedEventArgs e) { Task.Run(() => GrabAndShow()); } /// 刷新"当前各参数值"显示(曝光/增益/Z/水平)。在UI线程调用。 private void UpdateParamDisplay() { void Upd() { int exp = _camera?.Exposure ?? ParseInt(TxtExposure.Text, 0); string gain = $"R{ParseInt(TxtGainR.Text, 0)} G{ParseInt(TxtGainG.Text, 0)} B{ParseInt(TxtGainB.Text, 0)}"; TxtCurValues.Text = $"当前值:曝光 {exp}×100µs({exp / 10.0}ms) | 增益 {gain} | Z {_lastZ} | 水平 {_lastH} | well {(_curWell > 0 ? _curWell.ToString() : "--")}"; } if (Dispatcher.CheckAccess()) Upd(); else Dispatcher.BeginInvoke((Action)Upd); } // ── 实时预览 ── private void TglLive_Click(object sender, RoutedEventArgs e) { if (TglLive.IsChecked == true) { _liveCts = new CancellationTokenSource(); var token = _liveCts.Token; Log("实时预览开始。"); Task.Run(async () => { int frames = 0; var sw = System.Diagnostics.Stopwatch.StartNew(); while (!token.IsCancellationRequested) { if (!_busy) GrabAndShow(); frames++; if (sw.ElapsedMilliseconds >= 1000) { double fps = frames * 1000.0 / sw.ElapsedMilliseconds; Dispatcher.Invoke(() => TxtFps.Text = $"帧率: {fps:F1} fps"); frames = 0; sw.Restart(); } await Task.Delay(30, token).ContinueWith(_ => { }); } }, token); } else { _liveCts?.Cancel(); Log("实时预览停止。"); } } // ── 曝光 ── private void BtnSetExp_Click(object sender, RoutedEventArgs e) { if (_camera == null) return; int exp = ParseInt(TxtExposure.Text, 400); int r = _camera.SetExposure(exp); Log($"设置曝光 {exp}×100µs = {exp / 10.0}ms (code={r})"); UpdateParamDisplay(); // P0-3: 设曝光后传感器需时间生效,等待+丢弃旧帧,再抓显示当前曝光的画面 if (TglLive.IsChecked != true) Task.Run(() => { Thread.Sleep(Math.Max(200, exp / 5)); // 至少2×曝光时间(单位100µs) if (_camera != null) _camera.GrabRgb(); // 丢弃旧曝光的帧 GrabAndShow(); }); } // ── 增益 ── private void BtnSetGain_Click(object sender, RoutedEventArgs e) { if (_camera == null) return; byte rr = (byte)ParseInt(TxtGainR.Text, 25); byte gg = (byte)ParseInt(TxtGainG.Text, 14); byte bb = (byte)ParseInt(TxtGainB.Text, 25); int r = _camera.SetGain(rr, gg, bb); Log($"设置增益 R{rr} G{gg} B{bb} (code={r})"); UpdateParamDisplay(); if (TglLive.IsChecked != true) Task.Run(() => GrabAndShow()); } // ── 光源 ── private void BtnLedOn_Click(object sender, RoutedEventArgs e) { if (_motor == null) return; Task.Run(() => { _busy = true; bool ok = _motor.OpenLED(); _busy = false; Log($"开光源 {(ok ? "OK" : "失败")}"); }); } private void BtnLedOff_Click(object sender, RoutedEventArgs e) { if (_motor == null) return; Task.Run(() => { _busy = true; bool ok = _motor.CloseLED(); _busy = false; Log($"关光源 {(ok ? "OK" : "失败")}"); }); } // ── 存图 ── private void BtnSaveImg_Click(object sender, RoutedEventArgs e) { if (_lastFrame == null) { Log("还没有图像。"); return; } var dlg = new SaveFileDialog { Filter = "BMP 图片|*.bmp", FileName = $"house{_current?.HouseSn}_{DateTime.Now:yyyyMMdd_HHmmss}.bmp" }; if (dlg.ShowDialog() == true) { try { ImageConverter.SaveBmp(_lastFrame, _camWidth, _camHeight, dlg.FileName); Log($"已存图: {dlg.FileName}"); } catch (Exception ex) { Log($"存图失败: {ex.Message}"); } } } } }