C#によるUSBカメラ操作(WPF編)

前回は、WindowsFormsを使ったUSBカメラの撮像方法を紹介しました。

今回はWPF、といってもWPFはSilverlight4のようにUSBカメラの直接サポートはありませんので前回同様AForge.NETを使います。WPF?と思われるかと思いますが、個人の勉強のために使っただけです。ご了承ください。

まず、Nugetから

PM> install-package AForge.Video
PM> install-package AForge.Video.DirectShow

としてパッケージをインストールしておきます。

プロジェクト構成はこんな感じ。

0226project構成

WindowsFormsのときと使い方は同じなので、ヘルパー的なクラスが2つあります。

これを先に簡単に説明します。

DeviceFilters.cs

using System.Collections.Generic;
using System.Linq;
using AForge.Video.DirectShow;

namespace TestWpfApp
{
    class DeviceFilters
    {
        public string Name { set; get; }
        public string MonikerString { set; get; }

        public IEnumerable Get(){
            return from FilterInfo info in new FilterInfoCollection(FilterCategory.VideoInputDevice)
                   select new DeviceFilters { Name = info.Name, MonikerString = info.MonikerString };
        }
    }
}

BitmapToBitmapFrame.cs

using System.Drawing;
using System.IO;
using System.Drawing.Imaging;
using System.Windows.Media.Imaging;

namespace TestWpfApp
{
    internal class BitmapToBitmapFrame
    {
        internal static BitmapFrame Convert(Bitmap src)
        {
            using (var stream = new MemoryStream())
            {
                src.Save(stream, ImageFormat.Bmp);
                return BitmapFrame.Create(stream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
            }
        }

    }
}

前者DeviceFiltersは接続対象のUSBカメラデバイスです。オブジェクトとしてComboBoxにバインドするために作りました(ViewModel)。
後者BitmapToBitmapFrameはAForge.NETから取得できるのはBitmapですが、それがWPFのImageコントロールにそのまま貼り付けられないため
貼り付けられる形式のBitmapFrameに変換するためのクラスです。

下ごしらえがこれだけ。メインのCamDeviceOperationControlです。

見た目はこんな感じ。

0226camdeviceopectrl

WindowsFormsのときと似た感じです。

CamDeviceOperationControl.xaml

<UserControl x:Class="TestWpfApp.CamDeviceOperationControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:afd="clr-namespace:AForge.Video.DirectShow;assembly=AForge.Video.DirectShow"
             xmlns:dev="clr-namespace:TestWpfApp"
             Height="80" Width="375">
    <StackPanel>
        <StackPanel.Resources>
            <ObjectDataProvider x:Key="devices" ObjectType="dev:DeviceFilters" MethodName="Get"/>
        </StackPanel.Resources>
        <Label Content="Device"/>
        <ComboBox Name="deviceListCombo"
                  ItemsSource="{Binding Source ={StaticResource devices}}"
                  DisplayMemberPath="Name"
                  SelectedValuePath="MonikerString"/>
        <StackPanel Height="28" Orientation="Horizontal" HorizontalAlignment="Right">
            <Label Name="statusLabel">status</Label>
            <Button Width ="80" Content="Connect" Click="Connect_Click"/>
            <Button Width ="80" Content="Disconnect" Click="Disconnect_Click"/>
        </StackPanel>
    </StackPanel>
</UserControl>

CamDeviceOperationControl.xaml.cs

using System.Windows;
using System.Windows.Controls;
using AForge.Video;
using AForge.Video.DirectShow;

namespace TestWpfApp
{
    public partial class CamDeviceOperationControl : UserControl
    {
        public event NewFrameEventHandler NewFrameGot = delegate { };
        private VideoCaptureDevice device;

        public CamDeviceOperationControl()
        {
            InitializeComponent();
        }

        private void Connect_Click(object sender, RoutedEventArgs e)
        {
            device = new VideoCaptureDevice((string)deviceListCombo.SelectedValue);
            device.NewFrame += NewFrameGot;
            device.Start();
        }

        private void Disconnect_Click(object sender, RoutedEventArgs e)
        {
            device.NewFrame -= NewFrameGot;
            device.SignalToStop();
            device = null;
        }
    }
}

XAMLにデバイス検索&表示部を任せられているので、C#のコードが単純になっています。

最初に出てきたDebviceFilterをComboBoxにバインドさせ、Nameを表示させ取得する値はMonikerStringでこれを使ってVideoCaptureDeviceを作成しています。

これを使うのがMainWindowです、Imageと出てきたCamDeiceOperationControlの2つだけが貼り付けられています。

0226mainwindow

Imageコントロールが見えないので選択状態にしています。
MainWindow.xaml

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:TestWpfApp" x:Class="TestWpfApp.MainWindow"
        Title="MainWindow" Height="381.188" Width="417.574">
    <StackPanel Orientation="Vertical">
        <local:CamDeviceOperationControl x:Name="camDeviceCtrl" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="388"/>
        <Image x:Name="picture" HorizontalAlignment="Left" Height="246" Margin="10,0,0,0" VerticalAlignment="Top" Width="388"/>
    </StackPanel>
</Window>

MainWindow.xaml.cs

using System;
using System.Windows;
using System.Drawing;

namespace TestWpfApp
{
    public partial class MainWindow : Window
    {
        public MainWindow(){
            InitializeComponent();
            camDeviceCtrl.NewFrameGot += CamDeviceCtrlNewFrameGot;
        }

        private void CamDeviceCtrlNewFrameGot(object sender, AForge.Video.NewFrameEventArgs eventArgs){
            picture.Dispatcher.Invoke(new Action(bmp => picture.Source = BitmapToBitmapFrame.Convert(bmp)),eventArgs.Frame);
        }
    }
}

こちらもXAMLに任せている部分のおかげでC#のコードは簡単ですね。CamDeviceOperationControlが公開するイベント引数のBitmapを変換しているだけです。

動くとこんな感じ。

0226動作状況

WindowsFormsのときよりエラー処理などが不足しています;

WPFもっとこう使ったほうがイイヨ!というご指摘あれば是非お願いします。

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中