前回は顔認識における前処理の説明を行いました。

Introduction

前回、さりげなく、というかさらっとカメラを使っていたので、カメラの使い方を説明します。
OpenCVSharp はカメラも簡単に使えます。
複数のカメラを接続して切り替えることも可能です。

Explanation

ソースコード

説明するよりソースコードを見た方が早いです。
サンプルソースは末尾にあります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
public class MainViewModel : ViewModelBase
{

#region フィールド

private IDisposable _CaptureHandler;

private CvCapture _CvCapture;

#endregion

#region コンストラクタ

public MainViewModel()
{
this.CameraIndex = 0;
}

#endregion

#region プロパティ

private BitmapSource _CameraImage;

public BitmapSource CameraImage
{
get
{
return this._CameraImage;
}
set
{
this._CameraImage = value;
this.RaisePropertyChanged();
}
}

private int _CameraIndex;

public int CameraIndex
{
get
{
return this._CameraIndex;
}
set
{
this._CameraIndex = value;
this.RaisePropertyChanged();

this.ExecuteStart();
}
}

#endregion

#region メソッド

#region ヘルパーメソッド

private CvCapture CreateCvCapture()
{
CvCapture cvCapture = null;

try
{
cvCapture = new CvCapture(this.CameraIndex);
}
catch (Exception)
{
// ignore
}

return cvCapture;
}

private void ExecuteStart()
{
this.ExecuteStop();

this._CvCapture = this.CreateCvCapture();
if (this._CvCapture == null)
{
return;
}

var ms = 60;
this._CaptureHandler = Observable.Interval(TimeSpan.FromMilliseconds(ms), DispatcherScheduler.Current)
.Select(_ =>
{
if (this._CvCapture == null)
{
return null;
}

return Cv.QueryFrame(this._CvCapture);
})
.Where(frame => frame != null)
.Subscribe(frame =>
{
var writeableBitmap = frame.ToWriteableBitmap();
frame.Dispose();
this.CameraImage = writeableBitmap;
});
}

private void ExecuteStop()
{
if (this._CvCapture != null)
{
this._CvCapture.Dispose();
this._CvCapture = null;
}

if (this._CaptureHandler != null)
{
this._CaptureHandler.Dispose();
this._CaptureHandler = null;
}
}

#endregion

#endregion

}

CameraImage プロパティ

XAMLのViewとバインディングされており、ここが変化するとカメラ映像が行進されます。

CameraIndex プロパティ

XAMLのViewとバインディングされており、ここが変化するとカメラが切り替わります。
基本は0以上の整数値です。
カメラに対応していない数値が設定されると、 CvCapture のコンストラクタで例外を投げて、その後の処理でカメラの処理が自動で停止します。

ExecuteStart メソッド

CameraIndex プロパティの変更で呼ばれます。
まずは既存のカメラを停止させます。
次に、Reactive ExtensionsSystem.Reactive.Linq.Observable.Interval メソッド を使い指定した間隔でカメラ映像の取得を試みます。
OpenCVSharp.Cv.QueryFrame メソッドに作成した CvCapture のインスタンスを渡すことで、OpenCVSharp.IplImage オブジェクトを返します。
あとは、次々の発生する IplImage を Where でフィルタして、CameraImage プロパティ設定します。

Reactive Extensions 利用のアイデアはkattoshi様のRx と OpenCVSharp を使用して 簡単にカメラキャプチャアプリを作成する を参考にさせていただきました。
ありがとうございます。

重要なのは2つ。

まず、Interval メソッドの最後の引数は現在のスレッドの System.Windows.Threading.Dispatcher を渡します。
これにより、Viewに対する更新が自動的にバックグランドで実行されます。

次に、Subscribe メソッドの戻りをクラスメンバーに保持しておくことです。
この戻りは System.IDisposable インターフェイスです。
これを破棄することで、Interval メソッドによるイベントの発生が停止します。

ExecuteStop メソッド

CvCapture オブジェクトとSubscribe メソッドの戻りを破棄し、カメラを停止させます。

動作イメージ

サンプルには、MVVM Light ToolkitMahApps.Metro を使っています。
味気ないデフォルトウィンドウ以外を使うだけで、なんかかっこよく見えます。
これ大事。

Conclusion

OpenCVSharpを使えば、カメラも簡単に使えます。やったね。本当に。

Source Code

https://github.com/takuya-takeuchi/Demo/tree/master/OpenCV/02_OpenCV3