.NET 摄像头采集

news/2024/9/20 3:50:34/文章来源:https://www.cnblogs.com/kybs0/p/18385599

本文主要介绍摄像头(相机)如何采集数据,用于类似摄像头本地显示软件,以及流媒体数据传输场景如传屏、视讯会议等。

摄像头采集有多种方案,如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方案

值得一提的是,公司大屏HDMI采集卡信号Hdmi Record即6911龙讯固件,采用MediaCapture采集画面会稳定很多,减少了黑屏、粉屏的概率。从这点也说明Windows系统相机的原生实现方案,兼容性更好


参考列表:

使用 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博客

SharpCaptureDemo: C#,vb, .net采集摄像头,桌面屏幕,麦克风话筒声音,摄像头画面,并且支持混音,也支持同时采集录制。效率高,底层采用了directshow技术,稳定性强,兼容性好。 (gitee.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/789322.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python深度学习股价预测、量化交易策略:LSTM、GRU深度门控循环神经网络|附代码数据

全文链接:https://tecdat.cn/?p=37539 原文出处:拓端数据部落公众号 分析师:Shuo Zhang 本文以上证综指近 22 年的日交易数据为样本,构建深度门控循环神经网络模型,从股价预测和制定交易策略两方面入手,量化循环神经网络在股票预测以及交易策略中的效果,结合一个Pytho…

Windos NTP 服务设定

1、修改注册表2, 双击 打开 设为自动 应用 保存

【专题】中国游戏产业AIGC发展前景报告合集PDF分享(附原数据表)

原文链接:https://tecdat.cn/?p=37535 原文出处:拓端数据部落公众号 近八成头部游戏企业在人工智能、数字孪生、引擎开发、云技术和XR等技术领域布局;有近六成头部游戏企业已构建AI生产管线、赋能虚拟内容生产或智能营销;此外,国内TOP50游戏厂商投资AI企业已超百次。阅读原…

自动化测试:Monkey工具实践应用~

在移动应用的自动化测试中,意外的用户操作和各种不可预见的场景往往是导致应用崩溃的主要原因。如何有效地模拟这些复杂场景,成为了测试工程师的一大挑战。而在这一过程中,Monkey工具凭借其随机化测试的独特优势,成为了许多团队的利器。 那么,Monkey工具究竟是如何帮助测试…

TCP/IP、UDP/IP协议

参考链接 1、OSI七层模型 (1)OSI含义“OSI模型,即开放式通信系统互联参考模型(Open System Interconnection Reference Model),是国际标准化组织(ISO)提出的一个试图使各种计算机在世界范围内互连为网络的标准框架,简称OSI。”(2)OSI定义了网络互连的七层模型(物理…

HTTP与UDP/TCP区别

1、HTTPHTTP是应用层协议,TCP是传输层协议! 虽然HTTP本身是一个协议,但其最终还是基于TCP的。不过,目前,有人正在研究基于TCP+UDP混合的HTTP协议 HTTP协议是建立在请求/响应模型上的2、OSI七层模型3、TCP/IP五层模型

RSA加密

题目来源[BUUCTF]REVERSE——rsa 打开文件夹有两个文件打开 pub.key 文件复制到解密网站对应 RSA 密钥指数 E=65537,这一串模数可以转化为十进制后可以分离出 p、qp=285960468890451637935629440372639283459,q=304008741604601924494328155975272418463 所以已知 E、p、q,也就…

JMeter手机app录制

在移动应用的性能测试中,如何准确、全面地捕捉用户操作并生成可复用的测试脚本,始终是测试工程师面临的一大挑战。而JMeter,作为一款功能强大的开源性能测试工具,不仅在Web测试中表现优异,在手机App的录制方面同样拥有独到的优势。 那么,如何利用JMeter来进行手机App的录…

form-data与x-www-form-urlcoded区别

引言 很多同学在使用 postman 工具时,经常搞不清楚 form-data 与 x-www-form-urlcoded 的区别,如下:multipart/form-data 和 application/x-www-form-urlencoded 是两种常用的 HTTP 请求内容类型(Content-Type),它们用于在 HTTP请求中发送数据,但是它们之间存在一些区别…

创建结算清单时提示冲突解决方案

一、报错界面 二、原因分析【我的冲突列表】下没有这个冲突 原来组织间结算单独表单显示。 依次点击【供应链】→【组织间结算】→【基础资料】→【组织间结算冲突操作列表】,如下图所示 三、解决方式

最优化与计数

动态规划:可以认为由状态,转移两个过程构成 树上优化技巧 P1272 重建道路 设,dp[i][j]为包含i的大小为j的连通块的最小操作次数,枚举i的每个子树一个个合并上去。 考虑两个点i,j只会在lca处有计算时间贡献,所以是\(O(n^2)\)的 LOJ160. 树形背包 先跑dfs序,设dp[i][w]为从第…

轻松搞定工厂数据分析:一款报表工具就够了

在当今快速发展的工业环境中,工厂管理者越来越依赖数据分析来优化生产流程、提升效率、降低成本。然而,传统的数据分析工具通常较为复杂,操作困难且价格高昂,这让许多工厂望而却步。不过,最近我发现了一款非常实用的报表工具,能够帮助工厂管理者轻松完成综合数据分析,并…