画像解析まわりを F# で組もうとする場合、OpenCV の Mat と相互変換しなくちゃいけません。OpenCV 自体は C++ ベースなのでロジックを C++ で組むのがベータなのですが、ちょっとトライ&エラーがやりづらいんですよね。OpenCV の機能自体を使うのであれば、そのまま OpenCvSharp を使えばいいのですが、内部ロジックを弄る必要がある(特にテンプレートマッチの関数とか特徴量の関数とか)予定なので、できるだけデータ変換はおこしたくないな、と思っていました。
■OpenCvSharp は導入が簡単である
shimat/opencvsharp
http://github.com/shimat/opencvsharp
OpenCvSharpをつかう その17(NuGetで導入) – schima.hatenablog.com
http://schima.hatenablog.com/entry/2013/12/15/110513
OpenCvSharp 自体を知ったのは検討したのは3年前ぐらいで、その頃ちょうど .NETラボで亀川さんが講演していて、OpenCvSharp あたりを使った画像解析の話だったんですよね。データの変換自体が、C++ のネイティブなポインタと、.NET Framework のラッピングされたポインタでは、点単位ではひどく遅くなるだろうという会話をしたのを覚えています。その頃は、導入が結構面倒(OpenCvのDLL自体が面倒)だったので、C++ で組んでも C# で組んでもあまり変わりがなかったのですが、いつの間にか(失礼)、NuGet で設定ができるようになっていました。
Nuget で「OpenCvSharp」で検索してインストールすると、必要な DLL とプロジェクトの設定が自動的に行われます。
現状の OpenCV は 2.49(3のほうは、まだアルファ版です)なのですが、これらが一気に dll フォルダにダウンロードされます。
C++ プロジェクトで組むときに、最初に面倒なのがこの DLL を導入する(いちいちバージョンを変えないといけない)ことなので、プログラミング時の敷居がぐっと下がっています。
■カメラの画像をキャプチャする
サンプル自体は、先の github の中
https://github.com/shimat/opencvsharp/tree/master/sample
にあるので、目的のものを動かしてみるとよいでしょう。もともと OpenCV に含まれているサンプルが C#/VB 版に書き直されている感じです。
サンプルコードはこちら
http://1drv.ms/1lREkUV
■カメラへの接続
カメラへの接続は VideoCapture で設定すれば ok です。capture.Read あたりも C++ 版と同じなので、OpenCv を使っている場合はだいたい想像がつくでしょう。このウィンドウは OpenCv が勝手に作るウィンドウなので、Cv2.WaitKey でキー待ちをしてエスケープキーで閉じるようにしておきます。また、このウィンドウはスレッドで動いているらしく、async/await とか createwindows しなくても、モーダレスで動きます。これはこれで便利。
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 | void Camera() { var capture = new VideoCapture(); // 最初のカメラを取得 capture.Open(0); if (!capture.IsOpened()) throw new Exception( "capture initialization failed" ); var fs = FrameSource.CreateCameraSource(0); using ( var normalWindow = new Window( "normal" )) { var normalFrame = new Mat(); var srFrame = new Mat(); while ( true ) { capture.Read(normalFrame); if (normalFrame.Empty()) break ; _frame = normalFrame; normalWindow.ShowImage(normalFrame); int key = Cv2.WaitKey(100); if (key == 27) break ; // ESC キーで閉じる } } } |
■カメラから画像をキャプチャ
カメラから Bitmap へ落とすのも非常に簡単です。BitmapConverter.ToBitmap でフレームを渡してやれば Bitmap で落とせます。これをそのままピクチャボックスに張り付ければ画像が出ます。ここの BitmapConverter は以前とは違って OpenCvSharp.Extensions の中に入っているそうです。
1 2 3 4 5 6 | private void button2_Click( object sender, EventArgs e) { // カメラからキャプチャ Bitmap bitmap = BitmapConverter.ToBitmap(_frame); this .pictureBox1.Image = bitmap; } |
■ファイルに保存する
以前作ったツールから流用して、Bitmap を PNG 形式にして保存します。日付と連番でファイル名を付けるので
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 | /// <summary> /// マイピクチャに保存 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button3_Click( object sender, EventArgs e) { var bmp = (Bitmap)pictureBox1.Image; var fmt = ImageFormat.Png; var path = MakeFileName( fmt ); bmp.Save(path); label1.Text = Path.GetFileName(path); } string FOLDER = @"d:workblogimage" ; string MakeFileName(ImageFormat fmt) { string path = FOLDER; DateTime dt = DateTime.Now; string dname = string .Format( "{0:0000}{1:00}{2:00}" , dt.Year, dt.Month, dt.Day); var files = System.IO.Directory.GetFiles(FOLDER); var file = files.Where(f => f.IndexOf(dname + "_" ) > 0).Max(); var ext = fmt.ToString(); if (ext == ImageFormat.Jpeg.ToString()) // 拡張子を変更 { ext = "jpg" ; } if (file == null ) { path += dname + "_01." + ext; } else { var mat = new Regex( @"_(\d+)" ).Match(file); int num = int .Parse(mat.Groups[1].Value); num++; path += dname + "_" + num.ToString( "00" ) + "." + ext; } return path; } |
と、こんな感じにしておけば、手軽に Web カメラから画像へ落とせるようになります。
あとで、テンプレートマッチング用の関数のスピードも確認しておきましょう。これができると、ほぼ F# で組めるので。
Hi:
I have a question , how does do show camera video to C# winfrom’s picturebox Control ?
Yes, we can display camera’s image on Windows Forms, too.
In loop, when it capture on capture.Read(normalFrame), every time the camera data convert to bitomap by BitmapConverter.ToBitmap function.
and set this.pictureBox1.Image = bitmap .