Last-modified: Sat, 02 Jun 2012 22:13:05 HADT
Counter:3948 Today:1 Yesterday:1 Online:1
このエントリーをはてなブックマークに追加

1.WindowsFormsでKinectのカメラ画像を表示する

Now Editing
0.png

 このコンテンツはKinectを利用した最も簡単な実装について、関連ページを追うようにして解説していきます。WPFのサンプルは公式SDKに含まれるサンプルの他、多くの方が解説されているので、先ずはWindowsFormを利用したものにしました。このページではプロジェクトの新規作成と設定から始まって、Kinectのカメラ画像を表示するサンプルについて解説します。

※このコンテンツでは画像処理に関する基本的なアルゴリズムなどについて解説を行っていません。また画像処理の基本的なアルゴリズムについて記事も書いていません(12/02/14現在)。

  • &ref(): File not found: "kinect_windowsformsapplication1.zip" at page "Kinect/KinectForWindowsSDK/Basics/KinectWithWindowsForms/ShowCameraImage";
    • Kinect for Windows SDK 1.0」で動作を確認しています。
    • ここではサンプル内の「Kinect_WindowsFormsApplication1」プロジェクトについて解説します。
    • このサンプルは関連する全てのページに共通です。
      • [[:kinect:sdkforwin:winform:usecamera]]
      • [[:kinect:sdkforwin:winform:usedepth]]
      • [[:kinect:sdkforwin:winform:usecameraanddepth]]
      • [[:kinect:sdkforwin:winform:useskeleton]]
      • [[:kinect:sdkforwin:winform:useall]]

プロジェクトの準備

 ここではVisualStudio2010Expressを利用した開発について紹介しています。次の手順でプロジェクトを新規作成して設定します。

新規プロジェクトの作成

  1. 「メニューバー>ファイル(F)」などから「新しいプロジェクト」を実行する。
  2. 「インストールされたテンプレート」から「VisualC#」を選択し「Windows フォーム アプリケーション」を選択する。
  3. 「名前(N):」の項目に適当なプロジェクト名を設定する。
  4. 「OK」ボタンを押して新規プロジェクトの作成を実行する。

参照の追加

 プロジェクトの初期状態ではKinectSDKを利用することが出来ないため、SDKへの参照を追加して利用できるようにします。「Kinect for Windows SDK」がインストールされていない場合、「コンポーネント名」一覧に「Microsoft.Kinect」は表示されません。

  1. 「ソリューションエクスプローラ」から「参照設定」を右クリックする。
  2. 表示されたポップアップメニューから「参照の追加(R)」を選択して実行する。
  3. 表示されたウィンドウのタブ「.NET」を選択する。
  4. 表示された「コンポーネント名」から「Microsoft.Kinect」を選択する。
  5. 「OK」ボタンを押して参照の追加を実行する。

Unsafeコードの許可

 WindowsFormを利用した実装を行う時、ソースコードの一部に「ポインタ」を利用するため、Unsafeコードをプロジェクトで許可する必要があります。

  1. 「ソリューションエクスプローラ」から「//プロジェクト名//」を右クリックする。
  2. 表示されたポップアップメニューから「プロパティ(R)」を選択する。
  3. 表示された画面左のタブ「ビルド」を選択する。
  4. 一番上の項目「全般」から「アンセーフコードの許可(F)」にチェックを入れる。
  5. 表示された画面をタブから閉じて終了する。

サンプルプログラムの解説

初期化

 Kinectは利用する機能に応じて初期化を行う必要があります。まずはKinectからデータを受け取るために「KinectSensor」のインスタンスを生成します。次のサンプルコードのように、配列で接続されたKinectの番号を指定して、インスタンスを生成します。

#box(tips){ 複数のKinectを接続する場合は配列が[0]では無くなりますが、ここでは最小のサンプルという名目上、[0]によって繋がれた1台のKinectのみを考慮します。(※接続されていない場合の扱いについてもここでは触れません。) }

   KinectSensor kinect;
   //Kinectの初期化
   kinect = KinectSensor.KinectSensors[0];

データ取得の準備と開始

 Kinectからデータを受け取る方法は2通りあって、片方は「ポーリング」もう一方は「イベントの通知」によってデータを受け取る方法があります。ここでは恐らく一般的な「イベントの通知」によるデータの受取方法を採用しています。次のサンプルコードのように「ColorFrameReadyイベント」にイベントハンドラを登録し、その処理を別のメソッド(ここではkinect_ColorFrameReadyメソッド)で実装します。イベントハンドラの実装内容についてはこのページで後述します。

 サンプルコードではカメラのストリーム「ColorStream」を有効化しています。有効化のための「Enableメソッド」を実行して初めてKinectのカメラからデータを取得する準備が完了する点に注意してください。「KinectSensor.ColorStream.Enable()」の引数には「ColorImageFormat」型の値を与えます。この値によってKinectから取得するデータの形式が異なります。例えば320x240の解像度であったり、1280x960の解像度があります。

 最後にKinectからのデータ受け取りを開始するために「KinectSensor.Start()」を呼び出して実行します。以降はKinectがカメラ画像の取得に成功する度に「ColorFrameReadyイベント」が発生して、定義したメソッドが実行されます。カメラ画像の取得タイミングは設定値によってマチマチで「ColorImageFormat」の名前の末尾につく「Fps」によって分かります。

 FpsとはFramePerSecondの略語で、1秒間に何フレーム取得(再生)するかを示す値です。つまり30Fpsでは1秒間に30枚のカメラ画像がKinectから送られることになります。一般に30Fpsはリアルタイムに動いて見える値です。

            //イベントハンドラの登録
            kinect.ColorFrameReady += 
               new EventHandler<ColorImageFrameReadyEventArgs>(kinect_ColorFrameReady);
            //カメラの有効化
            kinect.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);

データの取得

 実際にKinectからカメラのデータを送られると、イベントが通知されて、登録されたイベントハンドラが呼び出されることになります。ここでは次のサンプルコードのように実装し、カメラの映像をBitmapにしてWindowsFormのPictureBoxに表示します。このイベントは30Fpsで呼び出されて、その度にBitmapが更新されるため、実際には動画に見える、といったカラクリです。WindowsFormやPictureBoxなどについてはここでは触れないので他資料を参照してください。

 まず「ColorImageFrameReadyEventArgs」から「OpenColorImageFrameメソッド」を呼び出して、Kinectからのデータを受け取ります。受け取った「ColorImageFrame」にはカメラの画像がbyte配列で与えられているため「CopyPixelDataToメソッド」を呼び出してその値を複製します。最後に、得られたbyte配列からBitmapを生成して、PictureBoxに表示します。

ここで注意する点がいくつかあります。

  • 「OpenColorImageFrame」の値が「null」で返る可能性がある
    • nullであることを考慮して実装する必要がある
  • 「ColorImageFrame.CopyPixelDataToメソッド」の引数は、必要なサイズで初期化されている必要がある。
    • 予め既定のサイズ(以上)で初期化しておいたbyte配列が必要になる。ここでは32bitColor(各色RGBが8bit)で表示するため、そのbyte配列のサイズは「画像の縦幅 * 横幅 * 4(1byte=8bit)」になる。つまり解像度は640*480であるため、そこにピクセル毎のデータ長、4byteを乗算した値になる。
  • 「ColorImageFrame」を「Dispose」する
    • 下のサンプルコードのように「ColorImageFrame」の値は利用後に「Dispose」して破棄する。Disposeしなかった場合、次の様な警告メッセージが出力されることを確認できる。「Warning: An ImageFrame instance was not Disposed.」
        //640*480のカメラ画像を32bitColorで表示する
        byte[] colorData = new byte[640 * 480 * 4];

        void kinect_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
        {

            //ColorImageFrameデータを貰う
            ColorImageFrame temp = e.OpenColorImageFrame();

            if (temp != null)
            {
                //カメラ映像Rgbのbyte配列
                temp.CopyPixelDataTo(colorData);

                unsafe
                {
                    fixed (byte* ptr = &colorData[0])
                    {
                        this.pictureBox1.Image =
                            new Bitmap(
                                temp.Width,
                                temp.Height,
                                temp.BytesPerPixel * temp.Width,
                                System.Drawing.Imaging.PixelFormat.Format32bppRgb,
                                (IntPtr)ptr);
                    }
                }
                //ColorImageFrameを破棄する
                temp.Dispose();
            }

Kinectアプリケーションの終了

 Kinectを使ったプログラムを終了するときは、まずKinectの動作を停止させます。ここでは次のサンプルコードのようにウィンドウを閉じる際に発生するイベント「OnClosedイベント」をオーバーライドして、その中で「KinectSensor.Stopメソッド」を呼び出しています。ここで注意したいのは、その直後に必ず「KinectSensor.Disposeメソッド」を呼び出すことです。この処理を行わない場合、Kinectは停止しているものの、CameraFreamReady(後に登場するDepthFrameReady・SkeletonFrameReady含む)イベントの処理中に例外が発生する可能性があります。

protected override void OnClosed(EventArgs e)
{
    kinectSensor.Stop();
    kinectSensor.Dispose(); 
}

#box(info){  例外の発生については次のページを参照されたい。