1、winform 应用程序,两个picturebox空间,一个用于视频呈现,一个用于抓拍呈现。

2、引用包OpenCvSharp4、OpenCvSharp4.Extensions、OpenCvSharp4.runtime.win等。

  1 // 定义一个部分类 Form2,继承自 Form 类,用于创建一个 Windows 窗体应用程序的窗口
2 public partial class Form2 : Form
3 {
4 // 修改成员变量
5 // 声明一个 volatile 修饰的 Bitmap 类型变量,用于存储最新的视频帧图像
6 // volatile 关键字确保该变量在多线程环境下的可见性,避免线程缓存导致的数据不一致问题
7 private volatile Bitmap _latestFrameBitmap;
8 // 声明一个用于线程同步的对象,用于对 _latestFrameBitmap 的访问进行加锁操作
9 private readonly object _bitmapLock = new object();
10 // 声明一个 VideoCapture 类型的变量,用于捕获摄像头的视频流
11 private VideoCapture _capture;
12 // 声明一个 CancellationTokenSource 类型的变量,用于取消异步操作
13 private CancellationTokenSource _cts;
14
15 // 窗体的构造函数,在创建 Form2 实例时自动调用
16 public Form2()
17 {
18 // 调用 Windows 窗体设计器生成的初始化方法,初始化窗体的控件和布局
19 InitializeComponent();
20 }
21
22 // 窗体加载事件处理方法,当窗体加载完成后自动触发
23 private async void Form2_Load(object sender, EventArgs e)
24 {
25 // 创建一个 VideoCapture 实例,参数 0 表示使用默认的摄像头设备
26 _capture = new VideoCapture(0);
27 // 检查摄像头是否成功打开
28 if (!_capture.IsOpened())
29 {
30 // 如果摄像头无法打开,弹出消息框提示用户
31 MessageBox.Show("无法打开摄像头!");
32 // 直接返回,不再执行后续的摄像头捕获操作
33 return;
34 }
35
36 // 创建一个 CancellationTokenSource 实例,用于取消异步操作
37 _cts = new CancellationTokenSource();
38 try
39 {
40 // 调用异步方法 StartCapturingAsync 开始捕获摄像头的视频流,并传入取消令牌
41 await StartCapturingAsync(_cts.Token);
42 }
43 catch (OperationCanceledException)
44 {
45 // 捕获 OperationCanceledException 异常,表示操作被正常取消,不做额外处理
46 }
47 catch (Exception ex)
48 {
49 // 捕获其他异常,弹出消息框显示异常信息
50 MessageBox.Show($"捕获出错: {ex.Message}");
51 }
52 }
53
54 // 异步方法,用于开始捕获摄像头的视频流
55 private async Task StartCapturingAsync(CancellationToken token)
56 {
57 // 使用 using 语句创建一个 Mat 对象,用于存储从摄像头读取的视频帧
58 // Mat 是 OpenCvSharp 库中用于表示图像矩阵的类,using 语句确保在使用完后自动释放资源
59 using (var frame = new Mat())
60 {
61 // 进入一个循环,只要取消令牌未被触发,就持续捕获视频帧
62 while (!token.IsCancellationRequested)
63 {
64 // 从摄像头读取一帧视频数据,并存储到 frame 对象中
65 _capture.Read(frame);
66 // 检查读取的帧是否为空,如果为空则跳过本次循环,继续读取下一帧
67 if (frame.Empty()) continue;
68
69 // 将 OpenCvSharp 的 Mat 对象转换为 .NET 的 Bitmap 对象,以便在 Windows 窗体中显示
70 var newBitmap = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(frame);
71
72 // 更新最新帧
73 // 使用 lock 语句对 _bitmapLock 对象加锁,确保在多线程环境下对 _latestFrameBitmap 的访问是线程安全的
74 lock (_bitmapLock)
75 {
76 // 保存旧的 _latestFrameBitmap 对象
77 var old = _latestFrameBitmap;
78 // 将新的 Bitmap 对象赋值给 _latestFrameBitmap
79 _latestFrameBitmap = newBitmap;
80 // 如果旧的 _latestFrameBitmap 对象不为空,则释放其资源
81 old?.Dispose();
82 }
83
84 // 调用异步方法 UpdateCameraPreviewAsync 更新摄像头预览界面
85 await UpdateCameraPreviewAsync(newBitmap);
86 // 异步延迟 30 毫秒,控制视频帧的捕获频率
87 await Task.Delay(30, token);
88 }
89 }
90 }
91
92 // 异步方法,用于更新摄像头预览界面
93 private async Task UpdateCameraPreviewAsync(Bitmap bitmap)
94 {
95 // 检查 picCamera 控件是否已经被释放,如果已经释放则释放传入的 Bitmap 对象并返回
96 if (picCamera.IsDisposed)
97 {
98 bitmap.Dispose();
99 return;
100 }
101
102 try
103 {
104 // 检查当前线程是否需要通过 Invoke 方法在 UI 线程上执行操作
105 if (picCamera.InvokeRequired)
106 {
107 // 如果需要,则使用 BeginInvoke 方法在 UI 线程上调用 UpdateCamera 方法
108 picCamera.BeginInvoke(new Action(() => UpdateCamera(bitmap)));
109 }
110 else
111 {
112 // 如果不需要,则直接调用 UpdateCamera 方法
113 UpdateCamera(bitmap);
114 }
115 }
116 catch (ObjectDisposedException)
117 {
118 // 捕获 ObjectDisposedException 异常,释放传入的 Bitmap 对象
119 bitmap.Dispose();
120 }
121 }
122
123 // 方法,用于更新摄像头预览界面
124 private void UpdateCamera(Bitmap newBitmap)
125 {
126 // 检查 picCamera 控件是否已经被释放,如果已经释放则释放传入的 Bitmap 对象并返回
127 if (picCamera.IsDisposed)
128 {
129 newBitmap.Dispose();
130 return;
131 }
132
133 // 保存 picCamera 控件当前显示的图像
134 var old = picCamera.Image;
135 // 将新的 Bitmap 对象赋值给 picCamera 控件的 Image 属性,更新显示的图像
136 picCamera.Image = newBitmap;
137 // 如果旧的图像不为空,则释放其资源
138 old?.Dispose();
139 }
140
141 // 优化后的抓拍方法,当点击抓拍按钮时触发
142 private async void catchBtn_Click(object sender, EventArgs e)
143 {
144 try
145 {
146 // 声明一个 Bitmap 类型的变量,用于存储抓拍的图像
147 Bitmap snapshot = null;
148
149 // 安全获取当前帧
150 // 使用 lock 语句对 _bitmapLock 对象加锁,确保在多线程环境下对 _latestFrameBitmap 的访问是线程安全的
151 lock (_bitmapLock)
152 {
153 // 检查 _latestFrameBitmap 是否不为空
154 if (_latestFrameBitmap != null)
155 {
156 // 如果不为空,则克隆一份 _latestFrameBitmap 对象赋值给 snapshot 变量
157 snapshot = (Bitmap)_latestFrameBitmap.Clone();
158 }
159 }
160
161 // 检查 snapshot 是否为空,如果为空则弹出消息框提示用户当前没有可用的视频帧
162 if (snapshot == null)
163 {
164 MessageBox.Show("当前没有可用的视频帧");
165 return;
166 }
167
168 // 异步保存防止界面卡顿
169 // 调用 Task.Run 方法在后台线程中执行 SaveSnapshot 方法,保存抓拍的图像
170 await Task.Run(() => SaveSnapshot(snapshot));
171 }
172 catch (Exception ex)
173 {
174 // 捕获其他异常,弹出消息框显示异常信息
175 MessageBox.Show($"抓拍失败: {ex.Message}");
176 }
177 }
178
179 // 方法,用于保存抓拍的图像
180 private void SaveSnapshot(Bitmap bitmap)
181 {
182 try
183 {
184 // 调用 GenerateUniqueFileName 方法生成一个唯一的文件名
185 var fileName = GenerateUniqueFileName();
186 // 使用 using 语句确保在使用完 bitmap 对象后自动释放资源
187 using (bitmap)
188 {
189 // 将 bitmap 对象保存为 JPEG 格式的文件
190 bitmap.Save(fileName, ImageFormat.Jpeg);
191
192 // 显示预览(需要克隆新实例)
193 // 克隆一份 bitmap 对象用于在界面上显示预览
194 var previewBitmap = (Bitmap)bitmap.Clone();
195
196 // 使用 BeginInvoke 方法在 UI 线程上执行更新预览界面和显示保存成功消息框的操作
197 BeginInvoke(new Action(() =>
198 {
199 // 调用 UpdateSnapshotPreview 方法更新抓拍图像的预览界面
200 UpdateSnapshotPreview(previewBitmap);
201 // 弹出消息框显示图像保存的路径
202 MessageBox.Show($"图片已保存到:\n{fileName}");
203 }));
204 }
205 }
206 catch (Exception ex)
207 {
208 // 捕获其他异常,使用 BeginInvoke 方法在 UI 线程上弹出消息框显示异常信息
209 BeginInvoke(new Action(() =>
210 {
211 MessageBox.Show($"保存失败: {ex.Message}");
212 }));
213 }
214 }
215
216 // 新增预览更新方法,用于更新抓拍图像的预览界面
217 private void UpdateSnapshotPreview(Bitmap newBitmap)
218 {
219 // 检查 pictureBoxSnapshot 控件是否已经被释放,如果已经释放则释放传入的 Bitmap 对象并返回
220 if (pictureBoxSnapshot.IsDisposed)
221 {
222 newBitmap.Dispose();
223 return;
224 }
225
226 // 处理跨线程访问
227 // 检查当前线程是否需要通过 Invoke 方法在 UI 线程上执行操作
228 if (pictureBoxSnapshot.InvokeRequired)
229 {
230 // 如果需要,则使用 BeginInvoke 方法在 UI 线程上递归调用 UpdateSnapshotPreview 方法
231 pictureBoxSnapshot.BeginInvoke(new Action(() => UpdateSnapshotPreview(newBitmap)));
232 return;
233 }
234
235 // 更新控件并释放旧资源
236 // 保存 pictureBoxSnapshot 控件当前显示的图像
237 var old = pictureBoxSnapshot.Image;
238 // 将新的 Bitmap 对象赋值给 pictureBoxSnapshot 控件的 Image 属性,更新显示的图像
239 pictureBoxSnapshot.Image = newBitmap;
240 // 如果旧的图像不为空,则释放其资源
241 old?.Dispose();
242 }
243
244 // 方法,用于生成一个唯一的文件名
245 private string GenerateUniqueFileName()
246 {
247 // 获取用户的图片文件夹路径
248 var docs = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
249 // 获取当前时间并格式化为指定的字符串格式
250 var timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmssfff");
251 // 组合图片文件夹路径、文件名前缀和时间戳,生成一个唯一的文件名
252 return Path.Combine(docs, $"Snapshot_{timestamp}.jpg");
253 }
254
255 // 窗体关闭事件处理方法,当窗体关闭时自动触发
256 private void Form2_FormClosing(object sender, FormClosingEventArgs e)
257 {
258 // 取消异步操作
259 _cts?.Cancel();
260 // 释放 CancellationTokenSource 对象的资源
261 _cts?.Dispose();
262
263 // 释放所有资源
264 // 释放 VideoCapture 对象的资源
265 _capture?.Dispose();
266 // 使用 lock 语句对 _bitmapLock 对象加锁,确保在多线程环境下对 _latestFrameBitmap 的访问是线程安全的
267 lock (_bitmapLock)
268 {
269 // 释放 _latestFrameBitmap 对象的资源
270 _latestFrameBitmap?.Dispose();
271 }
272
273 // 清理预览图
274 // 检查 picCamera 控件的 Image 属性是否不为空
275 if (picCamera.Image != null)
276 {
277 // 保存 picCamera 控件当前显示的图像
278 var img = picCamera.Image;
279 // 将 picCamera 控件的 Image 属性设置为 null
280 picCamera.Image = null;
281 // 释放旧的图像资源
282 img.Dispose();
283 }
284
285 // 新增快照预览清理
286 // 检查 pictureBoxSnapshot 控件的 Image 属性是否不为空
287 if (pictureBoxSnapshot.Image != null)
288 {
289 // 保存 pictureBoxSnapshot 控件当前显示的图像
290 var img = pictureBoxSnapshot.Image;
291 // 将 pictureBoxSnapshot 控件的 Image 属性设置为 null
292 pictureBoxSnapshot.Image = null;
293 // 释放旧的图像资源
294 img.Dispose();
295 }
296 }
297 }

C# USB 摄像头 OpenCV 视频picBox呈现,抓拍图像保存呈现。的更多相关文章

  1. 利用opencv从USB摄像头获取图片

    由于opencv自带的VideoCapture函数直接从usb摄像头获取视频数据,所以用这个来作为实时的图像来源用于实体检测识别是很方便的. 1. 安装opencv 安装的步骤可以按照之前这个文章操作 ...

  2. python opencv 读取USB摄像头的像素问题

    问题描述 每次调用capture读取video的时候,还回的像素都是640x480,不管是笔记本的摄像头还是USB摄像头,明明我的摄像头是支持130万读取的功能的呀. 问题分析 一番查找,关于用ope ...

  3. 使用openCV打开USB摄像头(UVC 小米micro接口)

    之前在AndroidStudio上就用了别人用写的库成功地打开了USB摄像头. 于是我之后又在PC上尝试了一下,首先去淘宝买了个MICRO母转USB公的转接口,然后在Qt上配置了一下OPENCV后开始 ...

  4. C# 基于Directshow.Net lib库 USB摄像头使用DirectShow.NET获取摄像头视频流

    https://blog.csdn.net/u010118312/article/details/91766787 https://download.csdn.net/download/u010118 ...

  5. 使用摄像头或视频运行 ORB-SLAM2 SLAM14讲 第一次课后作业

    参考:视觉SLAM十四讲(第一章作业) 深蓝上高博的第一讲课后题: 题目:6 * 使用摄像头或视频运行 ORB-SLAM2(3 分,约 1 小时)请注意本题为附加题.了解⼀样东西最快的⽅式是⾃⼰上⼿使 ...

  6. USB摄像头无法正常读取问题

    opencv读取摄像头或者视频一种是早期版本的IplImage结构体,图片就存在结构体指针IplImage*中,另一种是Mat类,两者在操作上略有差异,且opencv2都兼容这两个版本,前面的博客也说 ...

  7. DIY远程移动图像监测(tiny6410+USB摄像头+motion+yeelink+curl)

    看到有博客上采用motion搭建移动图像监测系统,感觉很强大,但大多缺少远程监测能力,大多局限于局域网.OK,笔者手头刚好有一个30W像素的USB摄像头,那么借用yeelink服务,也来DIY一把,哈 ...

  8. 荣品四核4412开发板的USB摄像头问题

    RP4412开发板是荣品电子研发的一款三星四核Exynos4412评估板开发板,支持WIFI+LAN上网.蓝牙4.0.4G上网.500万自动对焦摄像头.GPS.网卡.音频,1080P HDMI音视频同 ...

  9. Linux USB摄像头驱动【转】

    本文转载自:http://www.itdadao.com/articles/c15a509940p0.html 在 cortex-a8 中,可接入摄像头的接口通常可以分为两种, CAMERA 接口和 ...

  10. javaCV开发详解之2:推流器实现,推本地摄像头视频到流媒体服务器以及摄像头录制视频功能实现(基于javaCV-FFMPEG、javaCV-openCV)

    javaCV系列文章: javacv开发详解之1:调用本机摄像头视频 javaCV开发详解之2:推流器实现,推本地摄像头视频到流媒体服务器以及摄像头录制视频功能实现(基于javaCV-FFMPEG.j ...

随机推荐

  1. 【UI框架】ANtDesignVue中的Content超过一页显示问题

    假设content部分是绿色 当内容超出一页之后,滑下去,后面的都没有底色. 如果footer有内容,content的内容甚至会和footer内容重叠在一起,同时,footer的内容随着下滑,位置就不 ...

  2. Specifications动态查询

    [前言说明] 针对CRUD种的查询,因为我们的查询总是具有各种各样的筛选条件 为了我们的程序能够更加适应筛选条件的变化,SpringDataJpa提供了Specifications这种解决方案 Spe ...

  3. 会话丢失-NGINX配置之underscores_in_headers

    1.描述问题NGINX代理某个web服务时,单机情况下也出现不停的要求认证的情况 初步分析去掉NGINX代理,直接访问服务,未出现上述情况: 进一步分析:查看经过NGINX的请求和直接访问服务请求区别 ...

  4. Sealos Devbox 基础教程:使用 Cursor 从零开发一个 One API 替代品

    随着技术的成熟和 AI 的崛起,很多原本需要团队协作才能完成的工作现在都可以通过自动化和智能化的方式完成.于是乎,单个开发者的能力得到了极大的提升 - 借助各种工具,一个人就可以完成开发.测试.运维等 ...

  5. Qt编写安防视频监控系统39-onvif图片参数

    一.前言 通过onvif来调整图片的Brightness(亮度).ColorSaturation(色彩饱和度).Contrast(饱和度)这三个参数,可以实时观测到监控画面对应的变化,比如讲亮度Bri ...

  6. 开源即时通讯IM框架MobileIMSDK的微信小程序端技术概览

    一.基本介绍 MobileIMSDK - 微信小程序端是一套基于微信原生 WebSocket 的即时通讯库: 1)超轻量级.无任何第 3 方库依赖(开箱即用): 2)纯 JS 编写.ES6 语法.高度 ...

  7. 内华达大地测量实验室GNSS数据tenv3格式下载

    GNSS时序形变位移数据下载 引言 下载方式 注意事项 引言 目的:教大家如何下载GNSS时序形变位移数据,本人主要是利用GNSS位移结果进行InSAR相关成果的精度验证工作.若大家需要在自己的研究领 ...

  8. 再制作个WCH-LINK下载器

    用CH549可以制作成支持两种模式的WCH-LINK下载器,两种模式指的是RISC-V和DAPLINK模式. 如果用于沁恒的CH32V203等芯片,我们可以将这个下载器设置成RISC-V下载模式. 如 ...

  9. uwp 下载文件显示进度并解压文件

    uwp 下载文件显示进度并解压文件. <Page x:Class="App4.MainPage" xmlns="http://schemas.microsoft.c ...

  10. C#轻松实现条形码二维码生成及识别

    一.前言 大家好!我是付工. 今天给大家分享一下,如何基于C#来生成并识别条形码或者二维码. 二.http://ZXing.Net 实现二维码生成的库有很多,我们这里采用的是http://ZXing. ...