KinectV2 で カラー画像を映す- WPF * C#

KinectV2 のカラー(カメラ)画像を WPF と C#を使って表示する手順について解説します。 基本的には公式の SDK や Toolkit に付属してくるサンプルをきちんと読めば良いのですが、 ここでは最もシンプルな状態のサンプルを用意してもう少し丁寧に解説します。

サンプルプロジェクト
KinectV2Samples/01_Wpf_KinectV2_SimpleColorImage
開発環境
Kinect for Windows v2 Sensor
MS Kinect SDK 2.0.1410.19000
VisualStudio 2012

サンプルの配布方法に GitHub を採用してみることにしました。 試験的なものなので変更したり停止する場合があります。

Kinectへの接続とカラー画像の読み込み準備

//コンストラクタ。起動時に一度だけ実行される。
public MainWindow()
{
    InitializeComponent();

    //Kinect 本体への参照を確保する。
    this.kinect = KinectSensor.GetDefault();

    //読み込む画像のフォーマットとリーダを設定する。
    this.colorImageFormat = ColorImageFormat.Bgra;
    this.colorFrameDescription
        = this.kinect.ColorFrameSource.CreateFrameDescription(this.colorImageFormat);
    this.colorFrameReader = this.kinect.ColorFrameSource.OpenReader();
    this.colorFrameReader.FrameArrived += colorFrameReader_FrameArrived;

    //Kinect の動作を開始する。
    this.kinect.Open();
}
初期化と実行

まずはコンストラクタなどで KinectSensor.GetDefault() によって Kinect への参照を確保します。 続いてデータを読み込む準備を進めていきましょう。

予め Kinect から読み込むカラー画像のフォーマットを決めておきます。 ColorFrameSource.CreateFrameDescription() を利用して、FrameDescription のインスタンスを生成します。 いくつかフォーマットを選択することができますが、一般的には BgraRgba を利用することになるでしょう。 ここでは Bgra です。

データの読み込みを行う ColorFrameReader を用意するには、KinectSensor.ColorFrameSource.OpenReader() を利用します。 ColorFrameReader を利用することで、Kinect からカラー画像の情報を取得することができます。

カラー画像を取得する方法は大きく2つありますが、 ここでは Kinect がカラー画像を撮影するたび(1秒間に30回程度)に取得するするようにしましょう。 ColorFrameReader.FrameArrived イベントは、Kinect がカラー画像を撮影すると通知されるイベントです。 これにイベントハンドラを追加して、新しい画像が送られてくる度に処理を実行します。処理の内容は後述します。

最後に KinectSensor.Open() によって Kinect の動作を開始させます。

画像を表示する領域の用意

<Window x:Class="Wpf_KinectV2_SimpleColorImage.MainWindow"
        xmlns="http:\/\/schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http:\/\/schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Canvas x:Name="canvas"/>
    </Grid>
</Window>
表示する領域の用意

次の処理に移る前にカラー画像を表示する領域を用意しておきましょう。WPF なら Canvas か何かで良いと思います。 ここでは適当に名前を canvas にして用意しました。

Kinect からデータを受け取って表示する

FrameArrive イベントが通知されるとき、実行されるメソッド(イベントハンドラ)を実装しましょう。 ここで Kinect からデータを受け取り、画面に表示することになります。

まずはフレームを取得します。ここで言うフレームとは Kinect から送られてくるデータのセットの最小単位です。 例えば 2 フレームなら、Kinect が取得したデータ 2 回分のデータが含まれていることになります。

void colorFrameReader_FrameArrived(object sender, ColorFrameArrivedEventArgs e)
{
    ColorFrame colorFrame = e.FrameReference.AcquireFrame();

    if (colorFrame == null)
    {
        return;
    }


…中略(カラーデータの取得と可視化などの処理)

    colorFrame.Dispose();
}
フレームの取得と破棄

Kinect から送られてきた(通知された)フレームを取得するには、e.FrameReference.AcquireFrame() を利用します。 今回はカラー画像を取得するためのイベントですから、ColorFrame のインスタンスが得られます。

ただし Kinect がイベントを通知してきても上手く受け取れないことがあるので、 null かどうかを確認しましょう。(確認しなくても動くこともありますが、セキュアな設計のために。) 何故そういうことが起こるかを解説すると長くなるのでここでは割愛します。

また取得したフレームは使い終わったらColorFrame.Dispose()によって必ず破棄します。忘れないようにしましょう。

カラー画像情報を取得する

byte[] colors = new byte[this.colorFrameDescription.Width
                         * this.colorFrameDescription.Height
                         * this.colorFrameDescription.BytesPerPixel];

colorFrame.CopyConvertedFrameDataToArray(colors, this.colorImageFormat);
フレームからカラー画像情報を取得する

フレームが得られたらそこからカラー画像情報を抜き出します。 カラー画像情報を抜き出すためには、カラー画像情報を保存する領域(バッファ=colors)を用意する必要があります。 バッファの大きさ = カラー画像情報のデータ量は次のようにして算出します。

今回指定したカラー画像のフォーマットは Bgra です。 これは 1 画素あたりを [青・緑・赤・α] の 4 つの成分で表すフォーマットです。 各成分はそれぞれ 1byte = 8bit で表されます。 したがって、1byte * 4ch = 4byte が 1 画素当たりの情報量です。 カラー画像とは、この 1 画素当たりのデータが連なった配列によって表されています。 KinectV2 は基本的に、1920 * 1080 画素のカラー画像を取得しますから、データ量は 1920 * 1080 * 4 になります。 この概念は非常に重要なので Kinect を扱うのであれば覚えていた方が良いでしょう。

画像データの構造が分からなかったら「画像データの基本的な構造」を読んでみてください。 これで分からなければお手上げです。

対象の Kinect が扱う画像の高さや幅は 1920 や 1080 と直接指定しても良いですが、 ColorFrameDescription.Width(Height) によって取得することができます。実用的にはこちらの書き方良いです。 また画素あたりの情報量(4byte)も実は ColorFrameDescription.BytesPerPixel によって取得することができます。 くどいようですが最低限の画像データの構成などについて知った上で利用するのが良いです。

保存する領域が用意できたら CopyConvertedFrameDataToArray(領域, フォーマット) によってデータを複製します。

画像にして表示する

BitmapSource bitmapSource
    = BitmapSource.Create(this.colorFrameDescription.Width,
                          this.colorFrameDescription.Height,
                          96,
                          96,
                          PixelFormats.Bgra32,
                          null,
                          colors,
                          this.colorFrameDescription.Width * (int)this.colorFrameDescription.BytesPerPixel);

//キャンバスに表示する。
this.canvas.Background = new ImageBrush(bitmapSource);

colorFrame.Dispose();
WPF の Canvas に描画する

カラー画像情報が取得できたら WPF で表示するための形式に変換して表示しましょう。 ここでは BitmapSourceImageBrush を利用することにします(他にいくらでも方法はありますが)。 BitmapSource のインスタンスの生成方法については細かい説明を省きますが、 重要なのは PixelFormatsBgra32 を指定することです。 これは Kinect から取得するデータを Bgra に設定しているためです。 他のどんな方法で画像を表示する場合にも、フォーマットは必ず統一する必要があります。

BitmapSource.Create の最後の引数、stride(ストライド) についても理解が難しいかもしれないので解説しておきます。 先に解説した通り、画像データとは各画素のデータが配列になって構成されています。しかしその配列からは、画像の幅を推定することができません。 したがって配列のどこからどこまでが画像の横一列分を構成するデータにあたるのかが判断できないのです。ソレを定めるのが stride(ストライド) です。 ストライドは、画像の横一列分のデータの長さを表す単位です。ここでは画像の幅は 1920 で、1 画素あたりは 4byte(=32bit) ですから、 1920pix * 4byte = 7680byte がストライドとなります。

最後に確保した colorFrame を破棄するのを忘れないようにしましょう。 ここまできたらビルドして実行します。Canvas にカラー画像が表示されると思います。