1、winform 应用程序,两个picturebox空间,一个用于视频呈现,一个用于抓拍呈现。
2、引用包OpenCvSharp4、OpenCvSharp4.Extensions、OpenCvSharp4.runtime.win等。
public partial class Form2 : Form{// 修改成员变量private volatile Bitmap _latestFrameBitmap;private readonly object _bitmapLock = new object();private VideoCapture _capture;private CancellationTokenSource _cts;public Form2(){InitializeComponent();}private async void Form2_Load(object sender, EventArgs e){_capture = new VideoCapture(0);if (!_capture.IsOpened()){MessageBox.Show("无法打开摄像头!");return;}_cts = new CancellationTokenSource();try{await StartCapturingAsync(_cts.Token);}catch (OperationCanceledException){// 正常取消 }catch (Exception ex){MessageBox.Show($"捕获出错: {ex.Message}");}}private async Task StartCapturingAsync(CancellationToken token){using (var frame = new Mat()){while (!token.IsCancellationRequested){_capture.Read(frame);if (frame.Empty()) continue;var newBitmap = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(frame);// 更新最新帧lock (_bitmapLock){var old = _latestFrameBitmap;_latestFrameBitmap = newBitmap;old?.Dispose();}await UpdateCameraPreviewAsync(newBitmap);await Task.Delay(30, token);}}}private async Task UpdateCameraPreviewAsync(Bitmap bitmap){if (picCamera.IsDisposed){bitmap.Dispose();return;}try{if (picCamera.InvokeRequired){picCamera.BeginInvoke(new Action(() => UpdateCamera(bitmap)));}else{UpdateCamera(bitmap);}}catch (ObjectDisposedException){bitmap.Dispose();}}private void UpdateCamera(Bitmap newBitmap){if (picCamera.IsDisposed){newBitmap.Dispose();return;}var old = picCamera.Image;picCamera.Image = newBitmap;old?.Dispose();}// 优化后的抓拍方法private async void catchBtn_Click(object sender, EventArgs e){try{Bitmap snapshot = null;// 安全获取当前帧lock (_bitmapLock){if (_latestFrameBitmap != null){snapshot = (Bitmap)_latestFrameBitmap.Clone();}}if (snapshot == null){MessageBox.Show("当前没有可用的视频帧");return;}// 异步保存防止界面卡顿await Task.Run(() => SaveSnapshot(snapshot));}catch (Exception ex){MessageBox.Show($"抓拍失败: {ex.Message}");}}private void SaveSnapshot(Bitmap bitmap){try{var fileName = GenerateUniqueFileName();using (bitmap) // 确保释放资源 {bitmap.Save(fileName, ImageFormat.Jpeg);// 显示预览(需要克隆新实例)var previewBitmap = (Bitmap)bitmap.Clone();BeginInvoke(new Action(() =>{UpdateSnapshotPreview(previewBitmap);MessageBox.Show($"图片已保存到:\n{fileName}");}));}}catch (Exception ex){BeginInvoke(new Action(() =>{MessageBox.Show($"保存失败: {ex.Message}");}));}}// 新增预览更新方法private void UpdateSnapshotPreview(Bitmap newBitmap){if (pictureBoxSnapshot.IsDisposed){newBitmap.Dispose();return;}// 处理跨线程访问if (pictureBoxSnapshot.InvokeRequired){pictureBoxSnapshot.BeginInvoke(new Action(() => UpdateSnapshotPreview(newBitmap)));return;}// 更新控件并释放旧资源var old = pictureBoxSnapshot.Image;pictureBoxSnapshot.Image = newBitmap;old?.Dispose();}private string GenerateUniqueFileName(){var docs = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmssfff");return Path.Combine(docs, $"Snapshot_{timestamp}.jpg");}private void Form2_FormClosing(object sender, FormClosingEventArgs e){_cts?.Cancel();_cts?.Dispose();// 释放所有资源_capture?.Dispose();lock (_bitmapLock){_latestFrameBitmap?.Dispose();}// 清理预览图if (picCamera.Image != null){var img = picCamera.Image;picCamera.Image = null;img.Dispose();}// 新增快照预览清理if (pictureBoxSnapshot.Image != null){var img = pictureBoxSnapshot.Image;pictureBoxSnapshot.Image = null;img.Dispose();}}}