他人のフンドシで相撲をとってしまうけど・・・。
ことの始まりは、AR(Augmented Reality:拡張現実)って簡単にできないかなーと思ったですよ。たとえばUSBカメラを使って、リアルタイムに画像処理してっていう感じ。
画像処理やパターン認識、モーション解析などは、OpenCVを使えば簡単にできるらしい。
まずは、vaio type pに付いているUSBカメラを使うのは、どうやるんだろう思ったので調べてみました。
塚田浩二さんのWebサイトに「USBカメラをC#で使おう」というエントリがあり、こちらではDirectShowを使ってます。プログラムをダウンロードしてvaio type p(MOTION EYE追加バージョン)で使ってみると、USBカメラで静止画・動画を撮れることを確認できました。
んで、OpenCVに食わせて画像処理をしてみたいと思い調べてみると、C#でOpenCVを使うにはラッパーのOpenCVSharpを使えば簡単だとわかりました。
本当はUSBカメラの画像をリアルタイムに処理できないかなと思ったのだけど、ソース見ても「なるほど、わからん」。撮影したときイメージを更新するので、そのイメージデータに対して画像処理することはできそうだと思い、試行錯誤して実現できました。
CaptureForm.csにPictureBoxを貼り付けて、DSCapture.csのOnCaptureDone()の以下のようにコードを追加することによって、撮影ボタン押下でPictureBox1に画像エッジ検出した絵が出てきます。
もちろん、OpenCVをインストールするなりして使えるようにして、C#からOpenCVSharpのDLL(OpenCvSharp.dll)を参照設定を行ってね。
//キャプチャしたデータの配列をメモリ空間内に固定し,ビットマップに変換する.
void OnCaptureDone()
{
Bitmap bitmap2 = null;
try
{
int result;
if(sampleGrabber == null) return;
//コールバック関数の利用を停止する
result = sampleGrabber.SetCallback(null, 0);
//フレームデータのサイズを取得
int width = videoInfoHeader.BmiHeader.Width;
int height = videoInfoHeader.BmiHeader.Height;
//widthが4の倍数でない場合&widthとheightの値が適正でない場合は終了.
if( ((width & 0x03) != 0) || (width < 32) || (width > 4096) || (height < 32) || (height> 4096) )
return;
//stride(1ライン分のデータサイズ(byte)=width* 3(RGB))を設定.
int stride = width * 3;
//配列frameArrayのアドレスを,メモリ空間内で固定する.
GCHandle gcHandle = GCHandle.Alloc(frameArray, GCHandleType.Pinned);
int addr = (int) gcHandle.AddrOfPinnedObject();
addr += (height - 1) * stride;
//frameArrayを格納したメモリアドレスから,ビットマップデータを作成.
Bitmap bitmap = new Bitmap(width,height,-stride,PixelFormat.Format24bppRgb, (IntPtr) addr);
capturedBitmap = new Bitmap(width,height,-stride,PixelFormat.Format24bppRgb, (IntPtr) addr);
gcHandle.Free();
frameArray = null;
Image pre = null;
//画面上にキャプチャした画像を表示する.
pre = parentForm.PictureBoxImage;
parentForm.PictureBoxImage = bitmap;
using (IplImage src = BitmapConverter.ToIplImage(new Bitmap(bitmap)))
using(IplImage dst = new IplImage(src.Size, BitDepth.U8, 1))
{
Cv.CvtColor(src, dst, ColorConversion.BgrToGray);
dst.Canny(dst, 50, 200);
bitmap2 = dst.ToBitmap();
if (pict_ != null)
{
pict_.Image = bitmap2;
}
if (pre != null) pre.Dispose();
}
//更新完了.
updatedFlag = true;
return;
}
catch(Exception e)
{
MessageBox.Show("フレームのキャプチャ(Grab)に失敗しました." + e.ToString());
}
}
こんな感じになりますた。
次、OpenCVを調べていくと橋本詳解さんに「SharperCVでカメラ表示画面を出す」がありました。これはリアルタイムで動画として表示されます。そして、Schimaの日記さんでは、OpenCVSharpについて色々使われています。こちらも参考にして、画像エッジを抽出してみました。(ただ組み合わせただけと話なのだが)
Formアプリケーションを作って、ボタンを一つ貼り付ける。んで、以下のようにコードを入れるよ。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using OpenCvSharp;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void buttonStart_Click(object sender, EventArgs e)
{
captureRun();
}
public void captureRun()
{
const int Width = 640;
const int Height = 480;
const int ColorDepth = 24;
IplImage frameImage;
CvCapture capture = new CvCapture(0);
using (IplImage canny = new IplImage(Width, Height, BitDepth.U8, 1))
using (CvWindow windowCapture = new CvWindow("capture"))
using (CvWindow windowCanny = new CvWindow("canny"))
{
byte[] buffer = new byte[Width * Height * ColorDepth / 8];
while (true)
{
frameImage = capture.QueryFrame();
Cv.CvtColor(frameImage, canny, ColorConversion.BgrToGray);
Cv.Threshold(canny, canny, 128, 255, ThresholdType.Binary);
Cv.Canny(canny, canny, 50, 180);
windowCapture.ShowImage(frameImage);
windowCanny.ShowImage(canny);
if (CvWindow.WaitKey(33) == 'q') break;
}
}
}
}
}
んで、こんな感じになりますた。
これらのプログラムは、まったく理解せずにてきとーに組み合わせたので動作保障しませんし、質問されても困っちゃいます。
なにか参考本を購入しようかしら。
最近のコメント