登录
/
注册
首页
论坛
其它
首页
科技
业界
安全
程序
广播
Follow
关于
导读
排行榜
资讯
发帖说明
登录
/
注册
账号
自动登录
找回密码
密码
登录
立即注册
搜索
搜索
关闭
CSDN热搜
程序园
精品问答
技术交流
资源下载
本版
帖子
用户
软件
问答
教程
代码
写记录
写博客
小组
VIP申请
VIP网盘
网盘
联系我们
发帖说明
道具
勋章
任务
淘帖
动态
分享
留言板
导读
设置
我的收藏
退出
腾讯QQ
微信登录
1
2
/ 2 页
下一页
返回列表
首页
›
业界区
›
安全
›
C# USB 摄像头 OpenCV 视频picBox呈现,抓拍图像保存呈 ...
C# USB 摄像头 OpenCV 视频picBox呈现,抓拍图像保存呈现。
[ 复制链接 ]
皆炳
2025-6-1 21:24:55
程序园永久vip申请,无限下载程序园所有程序/软件/数据/等
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
视频
相关帖子
有手就行!我自制了一个高速USB转4路隔离RS-485的模块。
扣子Coze实战:混剪视频工作流,日产50条爆款,单月变现6位数(喂饭教程)
再讨论一次视频平台接入摄像机要注意的问题
USB基础知识学习笔记
Linux USB应用开发学习笔记
春节期间浅试了 ai 视频生成
2026年河北抖音短视频代运营5强推荐榜单
EXE加密视频不能看?教你手动解除一机一码限制。
OpenCV中的VideoCapture后端参数详解
ESP32S3 USB MSC 调试全过程记录
回复
使用道具
举报
提升卡
置顶卡
沉默卡
喧嚣卡
变色卡
千斤顶
照妖镜
相关推荐
业界
有手就行!我自制了一个高速USB转4路隔离RS-485的模块。
10
68
遗憩
2026-02-02
业界
扣子Coze实战:混剪视频工作流,日产50条爆款,单月变现6位数(喂饭教程)
13
628
公西颖初
2026-02-05
安全
再讨论一次视频平台接入摄像机要注意的问题
7
47
闻成
2026-02-11
安全
USB基础知识学习笔记
5
57
步雪卉
2026-02-14
安全
Linux USB应用开发学习笔记
6
796
更成痒
2026-02-14
业界
春节期间浅试了 ai 视频生成
3
922
俞秋荣
2026-02-25
安全
2026年河北抖音短视频代运营5强推荐榜单
0
92
毡轩
2026-03-08
业界
EXE加密视频不能看?教你手动解除一机一码限制。
0
28
谯梨夏
2026-03-14
业界
OpenCV中的VideoCapture后端参数详解
2
739
孙淼淼
2026-03-17
业界
ESP32S3 USB MSC 调试全过程记录
2
715
少琼
2026-03-26
回复
(21)
坪钗
2025-12-1 01:36:54
回复
使用道具
举报
照妖镜
程序园永久vip申请,无限下载程序园所有程序/软件/数据/等
感谢分享,下载保存了,貌似很强大
管水芸
2026-1-21 02:32:34
回复
使用道具
举报
照妖镜
程序园永久vip申请,无限下载程序园所有程序/软件/数据/等
yyds。多谢分享
殷罗绮
2026-1-22 11:08:28
回复
使用道具
举报
照妖镜
猛犸象科技工作室:
网站开发,备案域名,渗透,服务器出租,DDOS/CC攻击,TG加粉引流
热心回复!
纪晴丽
2026-1-23 13:47:09
回复
使用道具
举报
照妖镜
程序园永久vip申请,无限下载程序园所有程序/软件/数据/等
分享、互助 让互联网精神温暖你我
喳谍
2026-1-25 11:12:13
回复
使用道具
举报
照妖镜
程序园永久vip申请,无限下载程序园所有程序/软件/数据/等
喜欢鼓捣这些软件,现在用得少,谢谢分享!
米嘉怡
2026-1-27 02:39:53
回复
使用道具
举报
照妖镜
猛犸象科技工作室:
网站开发,备案域名,渗透,服务器出租,DDOS/CC攻击,TG加粉引流
收藏一下 不知道什么时候能用到
卒挪
2026-1-31 17:30:59
回复
使用道具
举报
照妖镜
程序园永久vip申请,无限下载程序园所有程序/软件/数据/等
用心讨论,共获提升!
龙正平
2026-2-2 02:47:18
回复
使用道具
举报
照妖镜
猛犸象科技工作室:
网站开发,备案域名,渗透,服务器出租,DDOS/CC攻击,TG加粉引流
分享、互助 让互联网精神温暖你我
挫莉虻
2026-2-7 23:56:58
回复
使用道具
举报
照妖镜
猛犸象科技工作室:
网站开发,备案域名,渗透,服务器出租,DDOS/CC攻击,TG加粉引流
谢谢楼主提供!
僚娥
2026-2-9 12:36:46
回复
使用道具
举报
照妖镜
猛犸象科技工作室:
网站开发,备案域名,渗透,服务器出租,DDOS/CC攻击,TG加粉引流
谢谢分享,辛苦了
峰邑
2026-2-9 14:30:15
回复
使用道具
举报
照妖镜
猛犸象科技工作室:
网站开发,备案域名,渗透,服务器出租,DDOS/CC攻击,TG加粉引流
感谢,下载保存了
釉她
2026-2-11 09:22:50
回复
使用道具
举报
照妖镜
猛犸象科技工作室:
网站开发,备案域名,渗透,服务器出租,DDOS/CC攻击,TG加粉引流
喜欢鼓捣这些软件,现在用得少,谢谢分享!
晦险忿
2026-2-11 12:39:20
回复
使用道具
举报
照妖镜
猛犸象科技工作室:
网站开发,备案域名,渗透,服务器出租,DDOS/CC攻击,TG加粉引流
谢谢分享,试用一下
搜娲瘠
2026-2-12 05:23:01
回复
使用道具
举报
照妖镜
猛犸象科技工作室:
网站开发,备案域名,渗透,服务器出租,DDOS/CC攻击,TG加粉引流
喜欢鼓捣这些软件,现在用得少,谢谢分享!
羊夏菡
2026-2-13 09:30:15
回复
使用道具
举报
照妖镜
程序园永久vip申请,无限下载程序园所有程序/软件/数据/等
不错,里面软件多更新就更好了
晦险忿
2026-2-17 21:01:40
回复
使用道具
举报
照妖镜
猛犸象科技工作室:
网站开发,备案域名,渗透,服务器出租,DDOS/CC攻击,TG加粉引流
感谢分享,下载保存了,貌似很强大
柏球侠
2026-2-25 05:03:47
回复
使用道具
举报
照妖镜
程序园永久vip申请,无限下载程序园所有程序/软件/数据/等
yyds。多谢分享
呼延含玉
2026-2-26 15:28:00
回复
使用道具
举报
照妖镜
程序园永久vip申请,无限下载程序园所有程序/软件/数据/等
东西不错很实用谢谢分享
蟠鲤
2026-3-2 04:24:23
回复
使用道具
举报
照妖镜
程序园永久vip申请,无限下载程序园所有程序/软件/数据/等
用心讨论,共获提升!
下一页 »
1
2
/ 2 页
下一页
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
|
立即注册
回复
本版积分规则
回帖并转播
回帖后跳转到最后一页
签约作者
程序园优秀签约作者
发帖
皆炳
2026-3-2 04:24:23
关注
0
粉丝关注
21
主题发布
板块介绍填写区域,请于后台编辑
财富榜{圆}
3934307807
991125
anyue1937
9994892
kk14977
6845359
4
xiangqian
638210
5
神泱
9529
6
韶又彤
9911
7
荪俗
9023
8
宋子
9875
9
蓬森莉
9871
10
俞瑛瑶
10000
查看更多
今日好文热榜
411
【EF Core】直接更新数据
519
Agent Skill 专业的事情交给专业的 Skill
387
【EF Core】直接更新数据
177
【OpenClaw】通过 Nanobot 源码学习架构---
234
NCCL EP 论文解读
5
200 行 Python 代码,从零手搓极简 Agent,
3
200 行 Python 代码,从零手搓极简 Agent,
5
手把手教你用Python做本地AI聊天机器人最终
4
200 行 Python 代码,从零手搓极简 Agent,
370
200 行 Python 代码,从零手搓极简 Agent,
6
200 行 Python 代码,从零手搓极简 Agent,
5
手把手教你用Python做本地AI聊天机器人最终
3
200 行 Python 代码,从零手搓极简 Agent,
5
手把手教你用Python做本地AI聊天机器人最终
5
200 行 Python 代码,从零手搓极简 Agent,
6
手把手教你用Python做本地AI聊天机器人最终
780
手把手教你用Python做本地AI聊天机器人最终
5
200 行 Python 代码,从零手搓极简 Agent,
5
200 行 Python 代码,从零手搓极简 Agent,
5
手把手教你用Python做本地AI聊天机器人最终