.NET 摄像头采集
本文主要介绍摄像头(相机)如何采集数据,用于类似摄像头本地显示软件,以及流媒体数据传输场景如传屏、视讯会议等。
摄像头采集有多种方案,如AForge.NET、WPFMediaKit、OpenCvSharp、EmguCv、DirectShow.NET、MediaCaptre(UWP),网上一些文章以及github已经有很多介绍,这里总结下
1. AForge.NET
AForge视频库是基于DirectShow技术开发的,提供了捕捉、处理和显示视频流接口,以及图像丰富的图像处理功能,如滤镜、特征提取和物体检测。详见官网开源仓库 andrewkirillov/AForge.NET(github.com)
我们下面看下AForge录制代码,安装Nuget包依赖:
1 <PackageReference Include="AForge.Video" Version="2.2.5" />
2 <PackageReference Include="AForge.Video.DirectShow" Version="2.2.5" />
3 <PackageReference Include="System.Drawing.Common" Version="8.0.8" />
摄像头显示:
1 private void StartButton_OnClick(object sender, RoutedEventArgs e)
2 {
3 // 获取所有视频输入设备
4 var videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice);
5 if (videoDevices.Count > 0)
6 {
7 // 选择第一个视频输入设备
8 var videoSource = new VideoCaptureDevice(videoDevices[0].MonikerString);
9 // 注册NewFrame事件处理程序
10 videoSource.NewFrame += new NewFrameEventHandler(videoSource_NewFrame);
11 // 开始摄像头视频源
12 videoSource.Start();
13 }
14 }
15
16 private async void videoSource_NewFrame(object sender, NewFrameEventArgs eventArgs)
17 {
18 // 获取当前的视频帧,显示
19 var image = ToBitmapImage(eventArgs.Frame);
20 await Dispatcher.InvokeAsync(() => { CaptureImage.Source = image; });
21 }
摄像头录制视频流:
1 private async void videoSource_NewFrame1(object sender, NewFrameEventArgs eventArgs)
2 {
3 // 获取当前的视频帧
4 // 将Bitmap转换为byte[],用于流媒体传输
5 byte[] byteArray = BitmapToByteArray(eventArgs.Frame, out int stride);
6 // 将byte[]转换为BitmapImage,用于临时展示
7 BitmapImage image = ByteArrayToBitmapImage(byteArray, eventArgs.Frame.Width, eventArgs.Frame.Height, stride, eventArgs.Frame.PixelFormat);
8 await Dispatcher.InvokeAsync(() => { CaptureImage.Source = image; });
9 }
其中的数据转换,这里要把分辨率stride同byte[]数据一同储存,不然后续数据是无法处理的:
1 // 将Bitmap转换为byte[]
2 public byte[] BitmapToByteArray(Bitmap bitmap, out int stride)
3 {
4 Rectangle rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
5 BitmapData bitmapData = bitmap.LockBits(rect, ImageLockMode.ReadOnly, bitmap.PixelFormat);
6 //stride是分辨率水平值,如3840
7 stride = bitmapData.Stride;
8 int bytes = Math.Abs(bitmapData.Stride) * bitmap.Height;
9 byte[] rgbValues = new byte[bytes];
10
11 // 复制位图数据到字节数组
12 Marshal.Copy(bitmapData.Scan0, rgbValues, 0, bytes);
13
14 bitmap.UnlockBits(bitmapData);
15 return rgbValues;
16 }
17
18 // 将byte[]转换为BitmapImage
19 public BitmapImage ByteArrayToBitmapImage(byte[] byteArray, int width, int height, int stride, PixelFormat pixelFormat)
20 {
21 var bitmapImage = new BitmapImage();
22 using (var memoryStream = new MemoryStream())
23 {
24 var bmp = new Bitmap(width, height, stride, pixelFormat, Marshal.UnsafeAddrOfPinnedArrayElement(byteArray, 0));
25 // 保存到MemoryStream中
26 bmp.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Bmp);
27 memoryStream.Seek(0, SeekOrigin.Begin);
28 bitmapImage.BeginInit();
29 bitmapImage.StreamSource = memoryStream;
30 bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
31 bitmapImage.EndInit();
32 bitmapImage.Freeze();
33 }
34 return bitmapImage;
35 }
详见Demo代码 kybs00/AForgeNETDemo (github.com)
经验证,延迟较大,对比Windows系统相机不够清晰
2. WPFMediaKit
WPFMediaKit也是基于DirectShow的,它提供了一些封装便于在WPF应用中使用媒体功能。
使用 WPFMediaKit 要录制摄像头视频,需要结合 WPFMediaKit 提供的视频捕获功能和其他库(例如 AForge 或 FFmpeg)来实现录制功能。
这里引用WPFMediaKit、AForge.Video.FFMPEG俩个Nuget包,然后通过定时器捕获当前视频帧:
1 var bitmap = new Bitmap(videoCaptureElement.Width, videoCaptureElement.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
2 var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),System.Drawing.Imaging.ImageLockMode.WriteOnly,bitmap.PixelFormat);
3 try
4 {
5 videoCaptureElement.VideoCaptureDevice.GetCurrentVideoFrame(out IntPtr frame);
6 System.Runtime.InteropServices.Marshal.Copy(frame, 0, bitmapData.Scan0, videoCaptureElement.Width * videoCaptureElement.Height * 3);
7 }
8 finally
9 {
10 bitmap.UnlockBits(bitmapData);
11 }
这个定时器的实现比较low。延时较低、较流畅,但与Win系统相机对比也是不够清晰
3.MediaCapture(UWP)
MediaCapture是Windows 8及以上版本的WinRT API,专为捕获音频、视频和照片设计。
MediaCaptuer是UWP应用的API 使用 MediaCapture 捕获基本的照片、视频和音频 - UWP applications | Microsoft Learn,要在WPF内使用需要引入俩个Nuget包:
1 <PackageReference Include="Microsoft.Toolkit.Wpf.UI.XamlHost" Version="6.1.2" />
2 <PackageReference Include="Microsoft.Windows.SDK.Contracts" Version="10.0.26100.1" />
初始化MediaCapture:
1 var mediaCapture =new MediaCapture();
2 var videos = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
3 var settings = new MediaCaptureInitializationSettings()
4 {
5 VideoDeviceId = videos[0].Id,
6 StreamingCaptureMode = StreamingCaptureMode.Video,
7 };
8 await mediaCapture.InitializeAsync(settings);
分几个场景,分别输出Demo。录制本地文件,注释已经很详细了并不重复解释,直接看代码:
1 private MediaCapture _mediaCapture;
2 private InMemoryRandomAccessStream _randomAccessStream;
3 private async void StartButton_OnClick(object sender, RoutedEventArgs e)
4 {
5 // 1. 初始化 MediaCapture 对象
6 var mediaCapture = _mediaCapture = new MediaCapture();
7 var videos = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
8 var settings = new MediaCaptureInitializationSettings()
9 {
10 VideoDeviceId = videos[0].Id,
11 StreamingCaptureMode = StreamingCaptureMode.Video,
12 };
13 await mediaCapture.InitializeAsync(settings);
14
15 // 2. 设置要录制的数据流
16 var randomAccessStream = _randomAccessStream = new InMemoryRandomAccessStream();
17 // 3. 配置录制的视频设置
18 var mediaEncodingProfile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Auto);
19 // 4. 开始录制
20 await mediaCapture.StartRecordToStreamAsync(mediaEncodingProfile, randomAccessStream);
21 }
22
23 private async void StopButton_OnClick(object sender, RoutedEventArgs e)
24 {
25 // 停止录制
26 await _mediaCapture.StopRecordAsync();
27 // 处理录制后的数据,保存至"C:\Users\XXX\Videos\RecordedVideo.mp4"
28 var storageFolder = Windows.Storage.KnownFolders.VideosLibrary;
29 var file = await storageFolder.CreateFileAsync("RecordedVideo.mp4", Windows.Storage.CreationCollisionOption.GenerateUniqueName);
30 using var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
31 await RandomAccessStream.CopyAndCloseAsync(_randomAccessStream.GetInputStreamAt(0), fileStream.GetOutputStreamAt(0));
32 _randomAccessStream.Dispose();
33 }
摄像头显示,通过UWP-WindowsXamlHost承载画面(置顶):
1 private async void StartButton_OnClick(object sender, RoutedEventArgs e)
2 {
3 _mediaCapture = new MediaCapture();
4 var videos = await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture);
5 var settings = new MediaCaptureInitializationSettings()
6 {
7 VideoDeviceId = videos[0].Id,
8 StreamingCaptureMode = StreamingCaptureMode.Video,
9 };
10 await _mediaCapture.InitializeAsync(settings);
11 //显示WindowsXamlHost
12 VideoViewHost.Visibility = Visibility.Visible;
13 //绑定画面源
14 _captureElement.Source = _mediaCapture;
15 await _mediaCapture.StartPreviewAsync();
16 }
MediaCapture是UWP平台的实现方案,直接给CaptureElement赋值绑定画面源。直接用CaptureElement渲染速度很快,这个实现逻辑同windows系统相机是一样的
另外,使用MediaCapture也可以捕获画面帧事件,用于流媒体数据捕获收集:
1 // 配置视频帧读取器
2 var frameSource = mediaCapture.FrameSources.Values.FirstOrDefault(source => source.Info.MediaStreamType == MediaStreamType.VideoRecord);
3 _frameReader = await mediaCapture.CreateFrameReaderAsync(frameSource, MediaEncodingSubtypes.Argb32);
4 _frameReader.FrameArrived += FrameReader_FrameArrived;
5 await _frameReader.StartAsync();
如下方所示,监听FrameArrived,使用Windows.UI.Xaml.Media.Imaging.BitmapImage渲染展示(仅用于展示,延迟很高):
1 private async void FrameReader_FrameArrived(MediaFrameReader sender, MediaFrameArrivedEventArgs args)
2 {
3 var frame = sender.TryAcquireLatestFrame();
4 if (frame != null)
5 {
6 var bitmap = frame.VideoMediaFrame?.SoftwareBitmap;
7 if (bitmap != null)
8 {
9 // 在这里对每一帧进行处理
10 await Dispatcher.InvokeAsync(async () =>
11 {
12 var bitmapImage = await ConvertSoftwareBitmapToBitmapImageAsync(bitmap);
13 _captureImage.Source = bitmapImage;
14 });
15 }
16 }
17 }
如需要将SoftwareBitmap转为buffer字节数据,可以按如下处理:
1 public async Task<byte[]> SoftwareBitmapToByteArrayAsync(SoftwareBitmap softwareBitmap)
2 {
3 // 使用InMemoryRandomAccessStream来存储图像数据
4 using var stream = new InMemoryRandomAccessStream();
5 // 创建位图编码器
6 var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, stream);
7 // 转换为BGRA8格式,如果当前格式不同
8 var bitmap = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
9 encoder.SetSoftwareBitmap(bitmap);
10 await encoder.FlushAsync();
11 bitmap.Dispose();
12
13 // 读取字节数据
14 using var reader = new DataReader(stream.GetInputStreamAt(0));
15 byte[] byteArray = new byte[stream.Size];
16 await reader.LoadAsync((uint)stream.Size);
17 reader.ReadBytes(byteArray);
18
19 return byteArray;
20 }
以上,MediaCapture能实现摄像头显示及录制相关功能。MediaCapture代码Demo详见Github:kybs00/CameraCaptureDemo: 摄像头预览、捕获DEMO (github.com)
3.其它
OpenCvSharp是OpenCV在C#环境中的包装,提供跨平台的计算机视觉和图像处理功能。2K视频较为流畅,4K视频延迟较低但显示效果较差
其它的如EmguCV是另一个基于OpenCV的C#包装组件库,具有与OpenCVSharp相同的强大功能。
DirectShow.NET,提供对DirectShow API的托管包装,使得在.NET框架中可以直接使用DirectShow的强大功能来进行视频捕获和处理。DirectShow本身性能较好,但DirectShow.NET作为托管包装,性能会受一定影响。延迟效果待验证
此处就不一一例写Demo了
我们看看性能数据,4K屏设备+4K摄像头,通过本地摄像头预览显示。借用组内小伙伴建凯大佬对各方案的延时统计数据:

验证了下MediaCaptre的延时与系统相机差不多。通过任务管理器我们可以看到,系统相机CPU占用3%,但GPU是15%。使用了硬件加速,性能方面很不错,所以摄像头采集推荐MediaCaptre方案
参考列表:
使用 MediaCapture 捕获基本的照片、视频和音频 - UWP applications | Microsoft Learn
[C#] 使用Accord.Net,实现相机画面采集,视频保存及裁剪视频区域,利用WriteableBitmap高效渲染 - 孤独成派 - 博客园 (cnblogs.com)
.net中捕获摄像头视频的方式及对比(How to Capture Camera Video via .Net) - Wuya - 博客园 (cnblogs.com)
C# 利用AForge进行摄像头信息采集 - 老码识途呀 - 博客园 (cnblogs.com)
WPF摄像头使用(WPFMediaKit) - 深秋无痕 - 博客园 (cnblogs.com)
C#使用OpenCvSharp4库读取电脑摄像头数据并实时显示_c#显示摄像头画面-CSDN博客
.NET 摄像头采集的更多相关文章
- 基于opencv和mfc的摄像头采集代码(GOMFCTemplate2)
编写带界面的图像处理程序,选择opencv+mfc是一种很好的选择:在读取摄像头数据方面,网上的方法很多,其中shiqiyu的camerads的方法是较好的. 基于现有资料 ...
- dshow,Sample Grabber 从摄像头采集
char* CCameraDS::QueryFrame() { long evCode, size = 0; #if CALLBACKMODE static double lastSampleTime ...
- 基于opencv和mfc的摄像头采集代码(GOMFCTemplate2)持续更新
编写带界面的图像处理程序,选择opencv+mfc是一种很好的选择:在读取摄像头数据方面,网上的方法很多,其中shiqiyu的camerads的方法是较好的. 基于现有资料 ...
- Linux 下V4l2摄像头采集图片,实现yuyv转RGB,RGB转BMP,RGB伸缩,jpeglib 库实现压缩RGB到内存中,JPEG经UDP发送功(转)
./configure CC=arm-linux-gnueabihf-gcc LD=arm-linux-gnueabihf-ld --host=arm-linux --prefix=/usr/loca ...
- 利用ffmpeg一步一步编程实现摄像头采集编码推流直播系统
了解过ffmpeg的人都知道,利用ffmpeg命令即可实现将电脑中摄像头的画面发布出去,例如发布为UDP,RTP,RTMP等,甚至可以发布为HLS,将m3u8文件和视频ts片段保存至Web服务器,普通 ...
- [转]GStreamer资料(摄像头采集,视频保存,远程监控)DVR
http://blog.csdn.net/wzwxiaozheng/archive/2010/12/26/6099397.aspx GStreamer资料整理(包括摄像头采集,视频保存,远程监控,流媒 ...
- QT学习之usb摄像头采集(Opencv+QT)[cvCapture,IplImage,QImage]
参考自:http://blog.chinaunix.net/uid-23381466-id-3826748.html 将Opencv中的cvCaptureFromCAM不断获得摄像头采集到的图像数据, ...
- 利用AXI VDMA实现OV5640摄像头采集
利用AXI VDMA实现OV5640摄像头采集 导读:摄像头采样图像数据后经过VDMA进入DDR,通过PS部分控制,经过三级缓存,将DDR中保持的图形数据通过VDMA发送出去.在FPGA的接收端口产生 ...
- FFmpeg 摄像头采集
FFmpeg 摄像头采集 extern "C" { #include "libavcodec/avcodec.h" #include "libavfo ...
- ffmpeg从USB摄像头采集一张原始图片(转)
本文讲解使用ffmpeg从USB摄像头中采集一帧数据并写入文件保存,测试平台使用全志A20平台,其他平台修改交叉工具链即可移植.开发环境使用eclipse+CDT.交叉工具链使用arm-Linux-g ...
随机推荐
- 【论文阅读】VDBFusion: Flexible and Efficient TSDF Integration of Range Sensor Data
Type: Sensors Year: 2022 tag: Mapping 组织: Bonn 参考与前言 论文链接:https://www.ncbi.nlm.nih.gov/pmc/articles/ ...
- 快速调用 GLM-4-9B-Chat 语言模型
一.确认本机显卡配置 二.下载大模型 国内可以从魔搭社区下载, 下载地址:https://modelscope.cn/models/ZhipuAI/glm-4-9b-chat/files 三.运行官方 ...
- Power BI实用技巧:轻松打造专业级甘特图
Power BI实用技巧:轻松打造专业级甘特图 大家好,今天我们要一起探索Power BI中一个既实用又强大的功能--制作甘特图.甘特图以其直观展示项目时间线和任务进度的特点,在项目管理中扮演着重要角 ...
- LaTeX 编辑协作平台 Overleaf 安装和使用教程
在学术界和科技行业,LaTeX 已成为撰写高质量文档的标准工具.然而,传统的 LaTeX 使用体验常常伴随着以下挑战: 学习曲线陡峭 环境配置复杂 多人协作困难 实时预览不便 当然,市面上不乏很多在线 ...
- frp内网穿透:基于centos8 云服务器和debian12客户端服务器
前言 入了一台本地工控机盒子,刷成了debian12系统,性能比云服务器要好一点,现在想要远程访问这台盒子,但是盒子又没有公网地址,所以想通过内网穿透的方式,通过云服务器转发请求实现内网穿透.原来体验 ...
- [oeasy]python0145_版本控制_git_备份还原
git版本控制 回忆上次内容 上次我们了解了 try 的完全体 try 尝试运行 except 发现异常时运行的代码块 else 没有发现异常时运行的代码块 finally 无论是否发现异 ...
- [oeasy]python0093_电子游戏起源_视频游戏_达特茅斯_Basic_家酿俱乐部
编码进化 回忆上次内容 Ed Robert 的 创业之路 从 售卖 diy 组装配件 到进军 计算器市场 最后 发布 牛郎星8800 intel 8080 的出现 让 人人都有 自己的 个人电脑 Bi ...
- 修改 /etc/resolv.conf
修改 /etc/resolv.conf /etc/resolv.conf 是 Linux 系统中用于配置 DNS 解析器的文件.确认 systemd-resolved 或 NetworkManager ...
- 如何平稳地从nacos迁移到r-nacos?
1. 引言 很多同学了解r-nacos特性后最开始只将r-nacos用于开发测试环境. 经过一段时间的使用后,部分同学有打算生成环境也从nacos迁移到r-nacos. 一些之前使用nacos服务的同 ...
- CF1929B Sasha and the Drawing 题解
CF1929B 题意 给定一个 \(n\times n\) 的正方形,已知正方形最多有 \(4\times n-2\) 条对角线,要求要有至少 \(k\) 条对角线经过至少一块黑色方格,求至少要将几条 ...