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();
}
}
}