前回はコンソールアプリでUSBカメラ画像の表示や保存をしてみましたが、今回はWPFアプリとして作ってみました。
ただ、まだまだWPFのお約束には慣れていないので、まずはコードビハインドで実装してみます。
また、画像の取得についても、
- OpenCvSharpをつかう その7 (WindowsFormsで動画の再生) - schima.hatenablog.com
- WPF Threads: Build More Responsive Apps With The Dispatcher
などの記事を参考に、BackgroundWorkerを使って行うようにします。
環境
- Windows7
- .NET Framework 4.5
- OpenCvSharp 2.4.9
- USBカメラ - Microsoft LifeCam VX-2000
MainWindow.xaml
USBカメラ画像を表示するImageと、保存ボタンだけがあるWindowです。
<Grid> <Grid.RowDefinitions> <RowDefinition Height="270"/> <RowDefinition Height="35"/> </Grid.RowDefinitions> <StackPanel Grid.Row="0"> <Image Name="Monitor" VerticalAlignment="Top" Loaded="imaging_Loaded"></Image> </StackPanel> <Button Grid.Row="1" Margin="4" Width="100" Content="保存" Click="Button_Click" /> </Grid>
MainWindows.xaml.cs
コードビハインド部分です。
BackgroundWorkerの利用
コンストラクタとWindow_Loadedイベントで、BackgroundWorkerに関する処理を記載します。
public MainWindow() { InitializeComponent(); worker = new BackgroundWorker(); // ProgressChangedイベントを発生させるようにする worker.WorkerReportsProgress = true; // RunWorkerAsyncメソッドで呼ばれるDoWorkに、 // 別スレッドでUSBカメラの画像を取得し続けるイベントハンドラを追加 worker.DoWork += (sender, e) => { using (var capture = Cv.CreateCameraCapture(0)) { IplImage frame; while (true) { frame = Cv.QueryFrame(capture); // 新しい画像を取得したので、 // ReportProgressメソッドを使って、ProgressChangedイベントを発生させる worker.ReportProgress(0, frame); } } }; // ReportProgressメソッドで呼ばれるProgressChangedのイベントハンドラを追加 worker.ProgressChanged += new ProgressChangedEventHandler(worker_ProgressChanged); } private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) { // frameがe.UserStateプロパティにセットされて渡されてくる var image = (IplImage)e.UserState; // Sourceプロパティにセットするため、frameをWriteableBitmapへと変換(Bitmapだと型変換エラー) // WriteableBitmapConverterを使うには、 // usingディレクティブにOpenCvSharp.Extensionsを追加 // (OpenCvSharp.UserInterface.dll内) Monitor.Source = WriteableBitmapConverter.ToWriteableBitmap(image); } private void Window_Loaded(object sender, RoutedEventArgs e) { // DoWorkイベントハンドラの実行を開始 worker.RunWorkerAsync(); }
なお、イベントハンドラはラムダ式と<hoge>EventHandler
の2つの方法で実装していますが、書き方を比較したかっただけで特に深い意味はありません。ラムダ式のほうが簡潔になりますね。
Image.Sourceへの設定
上のコードにある通り、ProgressChangedイベントで設定します。
ただ、USBカメラの画像はIplImage
型で渡されてくるため、そのままではSystem.Windows.Media.ImageSource
型であるImage.Sourceへと設定することができません。
そのため、OpenCvSharpにあるWriteableBitmapConverter
を利用して、 WriteableBitmap
型へと変換します *1。
なお、WriteableBitmap型へ変換するWriteableBitmapConverter.ToWriteableBitmap()
メソッドは、OpenCvSharp.Extensions
名前空間 (OpenCvSharp.UserInterface.dll内)にあるため、usingディレクティブに追加しておくとよいかもしれません。
この部分のコードを再掲します。
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e) { var image = (IplImage)e.UserState; Monitor.Source = WriteableBitmapConverter.ToWriteableBitmap(image); }
ボタンを押した時の保存処理
Image.Sourceの値を、実行ファイルと同じディレクトリに上書き出力してみます。
また、画像の形式はBitmap以外にも指定できますが、今回はBitmapのまま出力します。
まずは、Image.Sourceの値をWriteableBitmap型に変換します。
var image = (WriteableBitmap)Monitor.Source;
あとはFileStreamを使って出力します。
using (var fs = new System.IO.FileStream("hoge.bmp", System.IO.FileMode.Create)) { // BmpBitmapEncoderの他に、PngBitmapEncoderとかもある var enc = new BmpBitmapEncoder(); enc.Frames.Add(BitmapFrame.Create(image)); enc.Save(fs); MessageBox.Show("保存しました"); }
ちなみに、BmpBitmapEncoder以外にPngBitmapEncoderなどもあるため、必要に応じて適切なEncoderを使います。
bitmapsource - How to save a WPF image to a file - Stack Overflow
画面イメージ
前回とは別の、極早生リンゴの「ちなつ」を表示しています。
ソースコード
GitHubに上げました。OpenCvSharpAppプロジェクトに追加しています。
CSharp-Sample/OpenCvSharpApp at master - thinkAmi/CSharp-Sample - GitHub