Kinectを.netで



Kinect買いました。んで、OpenNI.netのサンプルソースコード(C#)を参考にして分かったことなんかをまとめときます。
C#でハックしている人ってあまりいないんで少しでも役に立てたら幸いです。
60日で800万台売れただけあってなかなかおもしろいです。
春(4月?)に公式SDKが発表なのでWindows使いの人は報われるかも?
正直C++とか読めませんorz

なので慣れ親しんでいるC#で弄ってみました。
ググれば環境設定は山ほど出てくるけど一応紹介。
ここを参考に設定しました。

以下、事前準備

  1. Kinect(読み方:キネクト)を買います
  2. KinectをUSB接続してドライバ設定を行います
  3. OpenNIを入れてサンプル表示されればOK

OpenNIインストール時のキーはOpenNI自体のサイトに記載されています。
今回はUnstableを使ってみました。
UserTracker.net.exeが色々できてサンプルとしては参考になります。
ソースコード見れば分かるけど、x,b,s,l,iキーでいろんなものの表示/非表示が切り替わります。
ESCでサンプル終了です。

.netで映像出力

さて、ようやくハックしていきます。
今回は、映像を表示する処理を.netで紹介(.netサンプルに無かったので)

ここを参考にしました。

まずは開発環境を設定です。
  • 参照設定
  • 設定ファイルの読み込み


が必要です。OpenNIが正常に入ったなら「C:\Program Files\OpenNI\Bin」にOpenNI.net.dllがあるはずなので参照設定に追加。設定ファイルは「C:\Program Files\OpenNI\Data\SamplesConfig.xml」を指定します。xmlファイルの中身はライセンス部分を書き換えます(

using xn;
string File = @"C:\Program Files\OpenNI\Data\SamplesConfig.xml";

設定ファイルはxnに含まれるContextクラスをインスタンスかするときに使用する設定ファイルです。Configurationを弄ると正常に動作しなくなる場合がありますので気をつけてください。

クラスの紹介

行いたい処理によって扱うクラスの紹介します

UserGeneratorクラス:人検出クラス。人物を検出・ロストしたときのイベントなんかを設定できる
PoseDetectionCapabilityクラス:ポーズ検出してくれる。Ψポーズでキャリブレーション
DepthGeneratorクラス:深度検出クラス。検出された結果をゴニョゴニョしましょう
ImageGeneratorクラス:映像検出クラス。普通の映像を表示したい場合はこちら

処理内容

まあ大まかな流れとしてはDepthGenerator(ImageGenerator)で検出したデータを、Bitmapで表示してるだけなんですが。。。

ポインタ?なにそれおいしいの?

って感じで最初はイミフでしたorz
(ホントは今も良く分かっていn)

やってる処理としては逐次取得データをポインタ指定でBitmapに反映してるだけなんです本当にw
スレッド処理メソッドにunsafeつけて、そのなかでBitmapをロックしている最中にピクセル設定します。
そして毎回Metadataを取得するところがミソです。

        private unsafe void DrawImage(ImageGenerator generator,Bitmap imgBitmap, ImageMetaData metaData) {
            Rectangle rect = new Rectangle(0, 0, imgBitmap.Width, imgBitmap.Height);
            BitmapData data = imgBitmap.LockBits(rect, ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            byte* pSrc = ( byte* )generator.GetImageMapPtr().ToPointer();
            for( int y = 0 ; y < metaData.YRes ; ++y ) {
                byte* pDest = ( byte* )data.Scan0.ToPointer() + y * data.Stride;
                for( int x = 0 ; x < metaData.XRes ; ++x,pSrc+=3,pDest+=3 ) {
                    pDest[0] = pSrc[2];
                    pDest[1] = pSrc[1];
                    pDest[2] = pSrc[0];
                }
            }
            imgBitmap.UnlockBits(data);
        }
        private unsafe void Run() {
            while( shouldRun ) {
                try {
                    context.WaitAndUpdateAll();
                } catch { }
                ImageMetaData imageMD = new ImageMetaData();

                this.image.GetMetaData(imageMD);
                if( image.IsDataNew() ) {
                    lock( imageBitmap ) {
                        DrawImage(this.image, this.imageBitmap, imageMD);
                    }
                    this.Invalidate();
                }
            }
        }


あとはImegeMetaDataが設定されたBitmapをFormに表示すればいいわけです。

        protected override void OnPaintBackground(PaintEventArgs e) {
            lock( imageBitmap ) {
                e.Graphics.DrawImage(imageBitmap, 0, 0);
            }
        }


文字を表示させたければ以下のようなコードで表示できますね。

        private void DrawBitMapString(Bitmap bitMap, string message, Font font, Brush brush, int x, int y) {
            Graphics g = Graphics.FromImage(bitMap);
            g.DrawString(message, font, brush, x, y);
            g.Dispose();
        }


サンプルを眺めればDepthGeneratorを使って特定の位置の深度も取得できます(以下はMetadataの中心の深度を取得している)

depthMD[( int )depthMapMode.nXRes / 2, ( int )depthMapMode.nYRes / 2]

これを使えば特定深度領域に来た情報だけを取得したり、手が特定の領域に来た時に特定の処理を作れますね。


とまあ夢は広がる一方ですね。
libfreenectは使ったことが無いですが、そちらも結構遊べるみたいなので弄ってみた人は連絡くれるとうれしいです。

これからが楽しみなおもちゃです。