WPF实现获取摄像头帧图的代码示例
一.前言
项目需求:支持uvc的摄像头,取出其画面帧图,进行相关叠加,并重新展示在image控件上
环境:用的是.net framework 4.8.1 。 当然.net6 也支持
使用的第三方插件:AForge.Video.DirectShow
备注:winform和uwp都可以进行参考
二.项目demo代码
MainWindow.xaml部分
<Window x:Class="WpfApp2.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfApp2" xmlns:vlc="clr-namespace:LibVLCSharp.WPF;assembly=LibVLCSharp.WPF" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <Image x:Name="video" Margin="0,40,0,0"/> <Button x:Name="StartButton" Content="start" HorizontalAlignment="Left" Margin="39,11,0,0" VerticalAlignment="Top" Click="StartButton_event"/> <Button x:Name="StopButton" Content="stop" HorizontalAlignment="Left" Margin="88,10,0,0" VerticalAlignment="Top" Click="StopButton_event"/> </Grid> </Window>
MainWindow.cs部分
using AForge.Video; using AForge.Video.DirectShow; using LibVLCSharp.Shared; using LoggerServiceFK; using SkiaSharp; using System; using System.Collections.Concurrent; using System.Diagnostics; using System.Drawing; using System.Windows; using System.Windows.Controls; using System.Windows.Interop; using System.Windows.Media.Imaging; namespace WpfApp2 { /// /// MainWindow.xaml 的交互逻辑 /// public partial class MainWindow : Window { private VideoCaptureDevice videoSource; private bool isCapturing = false; public MainWindow() { InitializeComponent(); } private void VideoSource_NewFrame(object sender, NewFrameEventArgs eventArgs) { if (isCapturing) { // Convert AForge.NET Framework's Bitmap to WPF's BitmapSource Bitmap aforgeBitmap = (Bitmap)eventArgs.Frame.Clone(); // Display the video frame in an Image control Dispatcher.Invoke(() => { //这里就可以对图片进行相关叠加操作,再赋值给source video.Source = Convert(aforgeBitmap); }); } } public static BitmapSource Convert(Bitmap bitmap) { if (bitmap == null) { throw new ArgumentNullException(nameof(bitmap)); } // 获取 Bitmap 的 HBitmap 句柄 IntPtr hBitmap = bitmap.GetHbitmap(); // 使用 CreateBitmapSourceFromHBitmap 方法创建 BitmapSource BitmapSource bitmapSource = Imaging.CreateBitmapSourceFromHBitmap( hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions()); // 释放 Bitmap 的 HBitmap 句柄 NativeMethods.DeleteObject(hBitmap); return bitmapSource; } private static class NativeMethods { [System.Runtime.InteropServices.DllImport("gdi32.dll")] [return: System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.Bool)] public static extern bool DeleteObject(IntPtr hObject); } private void StartButton_event(object sender, RoutedEventArgs e) { //这里获取usb摄像头的地方 FilterInfoCollection videoDevices = new FilterInfoCollection(FilterCategory.VideoInputDevice); if (videoDevices.Count > 0) { //这里默认选择设备列表[0],如果是笔记本外接usb摄像头要取数组[1] videoSource = new VideoCaptureDevice(videoDevices[0].MonikerString); videoSource.NewFrame += VideoSource_NewFrame; videoSource.Start(); isCapturing = true; } } private void StopButton_event(object sender, RoutedEventArgs e) { if (videoSource != null && videoSource.IsRunning) { //SignalToStop()停止信号就行,使用stop()方法会导致更新线程卡死 videoSource.SignalToStop(); isCapturing = false; } } } }
上面这部分代码就是取图片帧的方法,基本延迟在200ms左右,比vlc插件调取更快。
三.需要定制化下,如何优化此代码
demo所展示的只是基本取图,如要在取图的基础上 叠加定制化内容并不影响取图效率,该如何考虑?
本人分享一个解决办法:
首先不使用Bitmap 类型,改为SKBitmap类型———SKBitmap 使用的是 SkiaSharp的库
把Bitmap转成SKBitmap类型
var CurrentBitmap = new SKBitmap(640,480); CurrentBitmap=aforgeBitmap.ToSKBitmap();
需要定制化的内容 使用SKBitmap类型的指针进行处理,速度更快——此处根据项目本身进行处理
定制后的SKBitmap类型内容再转换成WriteableBitmap类型,使用WritePixels方法进行构成,video.source可以进行读取
//参考例子 var writeableBitmap = new WriteableBitmap(640, 480, 96, 96, PixelFormats.Bgr32, null); writeableBitmap.WritePixels(new Int32Rect(0, 0, skbitmap.Width, skbitmap.Height), skbitmap.GetPixels(), skbitmap.RowBytes * skbitmap.Height, skbitmap.RowBytes);
这个定制转换的效果与demo展示的效率相当
四. demo展示
以上就是WPF实现获取摄像头帧图的代码示例的详细内容,更多关于WPF取摄像头帧图的资料请关注脚本之家其它相关文章!
最新评论