登录
/
注册
首页
论坛
其它
首页
科技
业界
安全
程序
广播
Follow
关于
每日签到
每天签到奖励2圆-6圆
发帖说明
VIP申请
登录
/
注册
账号
自动登录
找回密码
密码
登录
立即注册
搜索
搜索
关闭
CSDN热搜
程序园
精品问答
技术交流
资源下载
本版
帖子
用户
软件
问答
教程
代码
写记录
写博客
VIP申请
VIP网盘
网盘
联系我们
每日签到
道具
勋章
任务
设置
我的收藏
退出
腾讯QQ
微信登录
返回列表
首页
›
业界区
›
安全
›
C# USB 摄像头 OpenCV 视频picBox呈现,抓拍图像保存呈 ...
C# USB 摄像头 OpenCV 视频picBox呈现,抓拍图像保存呈现。
[ 复制链接 ]
皆炳
2025-6-1 21:24:55
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 }
复制代码
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
呈现
USB
摄像头
OpenCV
视频
相关帖子
MoneyPrinterTurbo – 免费开源的AI短视频生成工具
智能光电检测:YOLO+OpenCV联合算法工程实践
读用数据说服:如何设计、呈现和捍卫你的数据08数据呈现和反对意见
OpenCV学习(1):Qt5.12配置OpenCV4.6教程
『OpenCV-Python』加载网络图片
逆天Qt/在嵌入式板子上同时播放4路8K视频/硬解码GPU绘制/RK3588性能太凶残
VideoPipe中集成多模态大模型做视频(图片)分析
开箱即用!一个自建视频平台的开源利器!
大/小模型在视频分析领域中的联合应用
音视频编解码——视频数据格式
vip免费申请,1年只需15美金$
回复
使用道具
举报
提升卡
置顶卡
沉默卡
喧嚣卡
变色卡
千斤顶
照妖镜
相关推荐
业界
MoneyPrinterTurbo – 免费开源的AI短视频生成工具
0
96
遇玷
2025-07-18
安全
智能光电检测:YOLO+OpenCV联合算法工程实践
0
105
后仲舒
2025-07-19
安全
读用数据说服:如何设计、呈现和捍卫你的数据08数据呈现和反对意见
0
71
寇油
2025-07-21
安全
OpenCV学习(1):Qt5.12配置OpenCV4.6教程
0
272
袋岖荤
2025-07-21
业界
『OpenCV-Python』加载网络图片
0
369
羊舌正清
2025-07-21
安全
逆天Qt/在嵌入式板子上同时播放4路8K视频/硬解码GPU绘制/RK3588性能太凶残
0
1064
胆饬
2025-07-24
业界
VideoPipe中集成多模态大模型做视频(图片)分析
0
527
苗嘉惠
2025-08-15
安全
开箱即用!一个自建视频平台的开源利器!
0
392
左优扬
2025-08-18
业界
大/小模型在视频分析领域中的联合应用
0
196
焦尔蕾
2025-08-22
业界
音视频编解码——视频数据格式
0
888
彼瞄
2025-08-29
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
|
立即注册
回复
本版积分规则
回帖并转播
回帖后跳转到最后一页
浏览过的版块
业界
科技
签约作者
程序园优秀签约作者
发帖
皆炳
2025-6-1 21:24:55
关注
0
粉丝关注
16
主题发布
板块介绍填写区域,请于后台编辑
财富榜{圆}
敖可
9984
杭环
9988
凶契帽
9988
4
氛疵
9988
5
黎瑞芝
9988
6
猷咎
9986
7
里豳朝
9986
8
肿圬后
9986
9
蝓俟佐
9984
10
虽裘侪
9984
查看更多