C#で利用できるOpenCVのラッパーライブラリの「OpenCvSharp」を使ってWebカメラで撮影した画像をWPFに表示したいと思います。
OpenCVを使用すれば数行で実現できるのでとっても簡単に実装できます。
今回使用するOpenCvSharpはバージョンが、3.4.1.20180319で2018年5月30日現在の最新版です。
準備編
外部ライブラリが必要なので、OpenCvSharp3をNuGetから取得します。
まずソリューションエクスプローラー上のプロジェクトから
参照 > NuGet パッケージ管理
を選択します。次に、参照タブにて、OpenCvShapr3 と入力しパッケージを検索します。
そうしたら画像のパッケージを選択してインストールを実行します。
これで準備は完了です。
WPFで画面を作成
一応上部に撮影終了ボタン、下に撮影した画像の表示をする画面を用意します。
画像を表示するのはImageで、Window の Loaded イベントでWebカメラ処理を開始します。
<Window x:Class="CameraCapture.MainWindow" // ...省略 Title="WebCamera Capture" Loaded="Window_Loaded"> <StackPanel Margin="10"> <StackPanel Orientation="Horizontal"> <Button Width="120" Height="24" Content="End Capture" Click="Button_Click"/> </StackPanel> <Image x:Name="_Image" Margin="0 10 0 0" Width="480" Height="270" HorizontalAlignment="Left"/> </StackPanel> </Window>
C#側の実装
関係ない処理の方が多いですが、Window_Loaded イベントハンドラでスレッドプールにCaptureメソッドの実行を投げています。
WebCamera制御は VideoCaptureクラス、読んだ画像を格納するために Matクラスを使用します。
WPFとOpenCvSharp3は連携がシームレスにできるように、Matクラスに ToWriteableBitmap があり、戻り値が WriteableBitmap のためImage.Source にそのまま設定することが可能です。
using OpenCvSharp; using OpenCvSharp.Extensions; // これ追加しておく using System.Threading; using System.Windows; namespace CameraCapture { /// <summary> /// MainWindow.xaml の相互作用ロジック /// </summary> public partial class MainWindow : System.Windows.Window { public bool IsExitCapture { get; set; } public MainWindow() { this.InitializeComponent(); } /// <summary> /// カメラ画像を取得して次々に表示を切り替える /// </summary> public virtual void Capture(object state) { var camera = new VideoCapture(0/*0番目のデバイスを指定*/) { // キャプチャする画像のサイズフレームレートの指定 FrameWidth = 480, FrameHeight = 270, // Fps = 60 }; using (var img = new Mat()) // 撮影した画像を受ける変数 using (camera) { while (true) { if (this.IsExitCapture) { this.Dispatcher.Invoke(() => this._Image.Source = null); break; } camera.Read(img); // Webカメラの読み取り(バッファに入までブロックされる if (img.Empty()) { break; } this.Dispatcher.Invoke(() => { this._Image.Source = img.ToWriteableBitmap(); // WPFに画像を表示 }); } } } // ---- EventHandlers ---- /// <summary> /// Windowがロードされた時 /// </summary> private void Window_Loaded(object sender, RoutedEventArgs e) { ThreadPool.QueueUserWorkItem(this.Capture); } /// <summary> /// Exit Captureボタンが押され時 /// </summary> protected virtual void Button_Click(object sender, RoutedEventArgs e) { this.IsExitCapture = true; } } }
【注意】パフォーマンスがとっても悪い
VideoCaptureのコンストラクタに画像のサイズを指定できるのですが、今回、1920x1080のFHD画質で30FPSが出るWebカメラだったため、初期化に1920x1080を指定したところRead()メソッドの呼び出しが120ミリ秒もかかって画面表示が10FPS以下(!?)という結果になりました。(Imageに画像を設定するところは数ミリ秒)
CPU使用率もかなり高く、これ以上コードもシンプル化できそうにないのでパフォーマンスは想像しているよりかなり悪いようです。