使用AForge.Net在WPF应用程序上实现WebCam

Joã*_*oso 9 wpf webcam aforge

我正在写一个WPF应用程序,我需要显示一个Webcam feed.我能够使用AForge框架轻松完成这项工作.但是当我从计算机更改为另一台计算机时,相同的代码不能以相同的方式工作.

在第一个网络摄像头提供完美的工作,但在另一个,这不会发生,饲料有很多延迟,应用程序无法正常工作.

这是代码:

    private void video_NewFrame(object sender, NewFrameEventArgs eventArgs)
    {
        Bitmap img = (Bitmap)eventArgs.Frame.Clone();

        this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, (SendOrPostCallback)delegate
            {
                IntPtr hBitmap = img.GetHbitmap();
                System.Windows.Media.Imaging.BitmapSource bitmapSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                    hBitmap,
                    IntPtr.Zero,
                    Int32Rect.Empty,
                    System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());

                DeleteObject(hBitmap);

                img.Dispose();
                GC.Collect();
                image1.Source = bitmapSource;

            }, null);

    }
Run Code Online (Sandbox Code Playgroud)

这段代码非常简单,它以一种形式从网络摄像头获取一个new_frame Bitmap,我需要做的是将它转换为一个BitmapSource,所以我可以在WPF的图像框架中显示.我认为这种转换是造成这种混乱的原因,但我不明白为什么它在计算机中起作用而在另一种情况下却不起作用.

计算机规格几乎相同,处理器是相同的,以及系统内存.

我的问题是关于性能,这个代码在一台计算机上运行顺畅,网络摄像头提供应该呈现,当我将它移植到另一台PC时,这不会发生.

Dev*_*per 19

这是基于工作代码文章.

(1)下载并安装最后一个AForge框架.(我使用的是2.2.4版本)

(2)创建WPF应用程序项目.

(3)添加对那些AForge DLL的引用.(你可以在C:\ Program Files(x86)\ AForge.NET\Framework\Release文件夹下找到它们ie)

在此输入图像描述

(4)建立你的项目.(我用过VS 2012)

(5)添加WPF Image控件并将其命名为"frameHolder".

所以你有类似的东西

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Image HorizontalAlignment="Stretch" Name="frameHolder"  VerticalAlignment="Stretch"  Stretch="Fill"/>
    </Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)

(6)添加C#代码:

using AForge.Video;
    using AForge.Video.DirectShow;
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
Run Code Online (Sandbox Code Playgroud)

/////

namespace WpfApplication1
    {
        public partial class MainWindow : Window
        {
            VideoCaptureDevice LocalWebCam;
            public FilterInfoCollection LoaclWebCamsCollection; 

        void Cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
        {
            try
            {
                System.Drawing.Image img = (Bitmap)eventArgs.Frame.Clone();

                MemoryStream ms = new MemoryStream();
                img.Save(ms, ImageFormat.Bmp);
                ms.Seek(0, SeekOrigin.Begin);
                BitmapImage bi = new BitmapImage();
                bi.BeginInit();
                bi.StreamSource = ms;
                bi.EndInit();

                bi.Freeze();
                Dispatcher.BeginInvoke(new ThreadStart(delegate
                {
                    frameHolder.Source = bi;
                }));
            }
            catch (Exception ex)
            {
            }
        } 

        public MainWindow()
        {
            InitializeComponent();
            Loaded += MainWindow_Loaded;
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            LoaclWebCamsCollection = new FilterInfoCollection(FilterCategory.VideoInputDevice);
            LocalWebCam = new VideoCaptureDevice(LoaclWebCamsCollection[0].MonikerString);
            LocalWebCam.NewFrame += new NewFrameEventHandler(Cam_NewFrame);

            LocalWebCam.Start();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

(7)重建项目,它的工作原理!

注意:我们默认使用第一个检测到的WebCam.确保你已经安装了WebCam驱动程序并且WebCam正常工作...... :)

  • 如果您打算在WPF应用程序中多次将其用作UserControl,请在Unloaded事件中添加`LocalWebCam.Stop();`.在不先停止相机的情况下再次创建控制时可能会出现问题.无论如何,这个解决方案给了我20分钟的麻烦,直到我注意到我忘了在Loaded事件结束时放置`LocalWebCam.Start();`是的,它发生了. (4认同)

Pat*_*cus 5

我知道原来的帖子已经超过 3 年了,但我一直在想如何使用这个代码。我发现 Dimi 给出的答案几乎是一个功能齐全的代码。但是,我发现我遇到了内存泄漏问题,并且在某些计算机上无法可靠地呈现帧。该代码在我更强大的开发计算机(i7、16GB RAM、Quadro Pro 图形卡)上完美运行,但是当我将应用程序部署在资源更有限的计算机(i5、4GB RAM、集成 Intel 显卡)上时,框架消失了一次一段时间后,程序也会在系统内存耗尽后崩溃。在互联网上搜索了一段时间后,我想我终于根据人们的所有反馈修补了一个工作代码。我知道另一台计算机能够从网络摄像头运行帧捕获,因为我有一个使用 AForge.NET 编写的 WinForm C# 应用程序,并且它在可靠地呈现帧并且没有内存泄漏的情况下没有问题。不幸的是,WPF 处理图形的方式与 WinForm 不同,我们必须做这个 hack 才能让 AForge.NET 使用它。

基本上,除了 Cam_NewFrame 方法之外,代码与 Dimi 相同。

void Cam_NewFrame(object sender, NewFrameEventArgs eventArgs)
    {
        try
        {
            BitmapImage bi;
            using(var bitmap = (Bitmap)eventArgs.Frame.Clone())
            {
                bi = new BitmapImage();
                bi.BeginInit();
                MemoryStream ms = new MemoryStream();
                bitmap.Save(ms, ImageFormat.Bmp);
                bi.StreamSource = ms;
                bi.CacheOption = BitmapCacheOption.OnLoad;
                bi.EndInit();
            }
            bi.Freeze();
            Dispatcher.BeginInvoke(new ThreadStart(delegate { frameHolder.Source = bi; }));


        }
        catch (Exception ex)
        {
            //catch your error here
        }

    } 
Run Code Online (Sandbox Code Playgroud)

所做的更改如下:

  1. 使用 Using 范围封闭位图处理,以便在范围结束后立即清除任何未使用的内存。
  2. 在处理内存流之前移动 bi.BeginInit() 以便位图立即准备好内存转储。
  3. 将 CacheOption 更改为 OnLoad,以便在加载时立即转储所有图像内存。否则,它使用 BitmapCacheOption.Default ,即使在发出 bi.Freeze() 时,它也可以允许图像保留在内存中。这导致即使调用 Dispatcher.BeginInvoke 来渲染图像也不会渲染帧。

到目前为止,它运行良好,但如果其他人发现其他问题,请发表评论,以便我们知道如何解决它。

  • 代码中有一个小错误。如果您启动应用程序,则不会显示图像。它可以通过在 `bitmap.Save(ms, ImageFormat.Bmp);` 之后添加 `ms.Seek(0, SeekOrigin.Begin);` 来解决,就像 Dmitry 所做的那样。 (2认同)

Jer*_*ill 2

在我的WPF MediaKit中,我有一个名为 VideoCaptureElement 的控件,它将向 WPF 渲染网络摄像头。您还可以通过挂钩新图像事件并在元素上设置 EnableSampleGrabbing 来访问示例。

  • WPF MediaKit 现在已经是一个死项目了。 (4认同)