はじめに
前回はKinect™ for Windows® SDKのための開発環境構築を紹介しました。今回はこの開発環境を使い、Kinectのカメラを使った簡単なアプリケーションを作成します。
WPFとC#について
この連載では、WPFと呼ばれるフレームワークを使ったC#のアプリケーションをターゲットにしています。アプリケーションの作成に入る前に「WPF」と「C#」について簡単に説明します。
WPFとは
WPF(Windows Presentation Foundation)とはMicrosoft®社が開発したユーザーインターフェースフレームワークです。特徴として、プレゼンテーションとロジックの記述を明確に分離しており、プレゼンテーションをXAML(Extensible Application Markup Language)と呼ばれる言語で宣言的に記述し、ロジックをC#などのプログラミング言語で記述します。 (WPFの特徴というよりは、ASP.NETやSilverlight®など、近年のMicrosoft社が採用するプログラミングモデルの傾向と言えるかもしれません。また、競合する技術であるJSFやAdobe® AIR®も同様のプログラミングモデルを採用しています)。 なお、XAMLに対応してロジックを記述したプログラムのことを「コードビハインド」と言います。ヘルプなどを読むときに使われることがあるので覚えておきましょう。
WPFアプリケーションの構造
典型的なWPFのアプリケーションでは、App.xamlとApp.xaml.csでアプリケーション全般のプレゼンテーションとロジックを記述し、MainWindow.xamlとMainWindow.xaml.csでアプリケーションのメインウィンドウのプレゼンテーションとロジックを記述します。 (.xamlがXAMLで書かれ、.csがC#で書かれます)
C#プログラムの構造
C#のプログラムは次に示す構造になっています。
using System; (省略) using System.Windows.Shapes; namespace SampleApplication { public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } }
Java™やC++などのオブジェクト指向の言語を何か知っていれば、なんとなくイメージがつかめるのではないでしょうか。これらは以下の構造を持ちます。
- 先頭にusingディレクティブが並ぶ。
このファイル内で使用する型の名前空間を宣言します。例えば"System.Math"クラスを使う場合、"using System;"と宣言しておけば、そのファイル中では"Math"と書くことで、”System.Math”クラスを参照できます。 Javaにおけるimportと同様の宣言です。 - namespaceで名前空間が宣言される。
Javaにおけるpackageや、C++におけるnamespaceに相当する、名前空間の宣言です。ファイル中に複数書いたり、ネストさせることもできます。 - 名前空間の内側で、クラス・構造体などを宣言します。
- クラスの内側で、メソッド・プロパティなどクラスのメンバーを宣言します。
作成するアプリケーションについて
第1回で紹介したように、Kinectセンサーは複数のセンサーから構成されています。今回は、そのうちのカメラを利用し、入力映像を画面に表示するアプリケーションを作成します。
Kinectセンサーのデータストリームを使った処理の流れ
Kinect for Windows SDKでは、カメラなど各センサーの入力情報をフレーム毎にアプリケーションから利用できます(これらを「データストリーム」と呼びます)。データストリームを使ったアプリケーションの処理の流れを以下に示します。(ここではイベント・モデルを紹介しますが、その他にポーリング・モデルもあります)
- 使用するストリーム(今回はColorStream)を有効化する
- ストリームを受け取るイベントハンドラを登録する
- Kinectランタイムにストリーム取得の開始を通知する
- Kinectランタイムはフレームデータが準備できるとイベントハンドラを呼ぶ
- イベントハンドラでフレームを処理する(4に戻る)
アプリケーションの構造
今回作成するアプリケーションの構造を図1に示します。
図1: 作成するアプリケーションの構造
- MainWindow: メインウィンドウの構成やイベント処理方法をXAMLで記述します。
- MainWindow(ロジック): メインウィンドウのロジックをC#で記述します。
- WindowLoaded: メインウィンドウの”Loaded”イベント(初期化完了)を処理し①、Kinectの初期化(カラーストリームの有効化・イベントハンドラ登録)②、アプリケーションの初期化を行います。
- ColorImageReady: Kinectランタイムからカラーフレーム毎に呼び出され③、RGBカメラの画像情報をメインウィンドウのImageコントロールに書き込みます④。
VisualStudio®による開発
それでは、実際にVisualStudioを操作しながら、アプリケーションを作成しましょう。今回は以下の手順で作成します。
- プロジェクトの作成
- XAMLによるメインウィンドウの記述
- 画像情報を表示するImageコントロールの追加
- "Loaded"イベントのハンドラメソッドを設定
- C#によるロジックの記述
- usingディレクティブの追加
- フィールドの追加
- "Loaded"イベントハンドラの記述
- "ColorFrameReady"イベントハンドラの記述
プロジェクトの作成
アプリケーションを作るには、ソースコード、コンパイルオプションなどの各種設定ファイル、リソースとして埋め込む画像など、様々なファイルが必要になります。 Visual Studioでは、それらを「プロジェクト」という単位で管理しています(複数のプロジェクトを持つ「ソリューション」という単位もありますが、今は気にしなくて構いません)。それでは、プロジェクトを作成し、Kinect for Windows SDKを使うための準備をしましょう。
- Windowsのスタートメニューから、[すべてのプログラム]-[Microsoft Visual Studio 2010 Express]-[Microsoft Visual C# 2010 Express]を選び、Visual C#®を起動します。
- メニューから[ファイル]-[新しいプロジェクト(P)...]を選択します。
- [新しいプロジェクト]ダイアログで、以下を行います。
- [WPFアプリケーション]を選択
- [名前(N):] に "KinectCameraSample" と入力
- [OK]をクリック
- [ソリューションエクスプローラー]で、作成したプロジェクト(ここでは"KinectCameraSample")を右クリックして[参照の追加(R)...]を選択します。
- [参照の追加]ダイアログで[.NET]タブを選択、[コンポーネント名]から"Microsoft.Kinect"をクリックし、[OK]をクリックします。
- この時点ではまだプロジェクトが保存されていないので、メニューから[ファイル]-[すべてを保存(L)]を選択し、[プロジェクトの保存]ダイアログで[場所(L):]にプロジェクトを保存するフォルダを指定し、[上書き保存(S)]をクリックします。
コントロールの追加
WPFなどのUIフレームワークでは、ユーザからの入力を受け取ったり、出力を表示する部品を「コントロール」と呼びます。Visual C# を使い、WPFのコントロールを追加してみましょう。ここでは、"Image"コントロールを作成し、"Height"などのプロパティを設定する手順を紹介しますが、その他のコントロールでも同様の手順で追加できます。
- 追加対象のXAMLファイルを開きます (ここでは "MainWindow.xaml" )。
[ソリューションエクスプローラー]から"KinectCameraSample"の下にある"MainWindow.xaml"をダブルクリックします。(すでに開いている場合は不要) - ウィンドウ左の[ツールボックス]をクリックし、表示される[ツールボックス]ビューの中から、"Image"をドラッグし、"MainWindow.xaml"の上にドロップします。
- 追加されたコントロールを右クリックし、コンテキストメニューから[プロパティ]を選択します。
- [プロパティ]から、必要なプロパティを設定します。
- ここでは Image を親の Grid のサイズに合わせたいので、以下のプロパティすべてでプロパティ名を右クリックして[値のリセット]を選択します。
Height, Width, HorizontalAlignment, VerticalAlighment, Margin - また、イメージの拡大・縮小で縦横比を変えないように Stretch プロパティも、値をリセットします (指定しない場合 "Uniform" になり、縦横比を維持して拡大・縮小されます)。
- わかりやすくするため、Name プロパティを変更します (ここでは "rgbImage" とします) 。
- ここでは Image を親の Grid のサイズに合わせたいので、以下のプロパティすべてでプロパティ名を右クリックして[値のリセット]を選択します。
イベントハンドラの設定
「ユーザがマウスをクリックした」とか、「コントロールが初期化された」といった各種イベントに対する処理は「イベントハンドラ」に記述します。 WPFのコントロールに対するイベントハンドラを記述するには、以下の2つの手順が必要です。
- WPFで該当するコントロールのイベントに対して、ハンドラメソッド名を記述する。
- コードビハインドに、イベントに対する処理を記述したハンドラメソッドを記述する。
今回作成するアプリケーションは、メインウィンドウの初期化時にKinectなどの初期化を行います。このためにメインウィンドウのWindowオブジェクトの"Loaded"イベントを使います。ここでは、"Loaded"イベントのハンドラメソッドとして"WindowLoaded"を登録する手順を示します。
- 追加対象のXAMLファイルを開きます (ここでは "MainWindow.xaml" )
[ソリューションエクスプローラー]から"KinectCameraSample"の下にある"MainWindow.xaml"をダブルクリックします。(すでに開いている場合は不要) - 追加対象のコントロールを選択します。ここでは、Windowを選択します。
なお、デザインビュー上ではコントロールの選択が難しいことがあります。この場合、以下の方法も試してみてください。- コントロールを選択した状態で[ESC]キーを押すと親のコントロールが選択されます。これを利用して、たとえば上記で追加したImageコントロールを選択し、[ESC]を2回押すことで親の親(つまりWindow)を選択できます。
- XAMLビュー上で該当するタグ(この場合は<Window>タグ)をクリックすることでもコントロールを選択できます。
- [プロパティ]ビューの[イベント]タブを選択し、[Loaded]に"WindowLoaded"を入力し、エンターを押します。
- XAMLにハンドラメソッド名が指定され、コードビハインドにハンドラメソッドのひな形が作られます。(ハンドラメソッドへの処理の追加は後程行います)
コードビハインドへの処理の追加
コードビハインドへの処理の追加は、Visual Studio のソースエディタで行います。追加対象のC#ファイルを開き、以下のように記述しましょう。(ここでは "MainWindow.xaml.cs"に追加しています )
- usingディレクティブの追加
Kinect for Windows SDKを使った開発では、Microsoft.Kinect.KinectSensorクラスなどを多用するので、usingディレクティブを追加して、"KinectSensor"で参照できるようにしておきましょう。 - フィールドの追加
MainWindowクラスの宣言の直下に以下の2つのフィールドを追加します。これらの役割については後述します。 - "Loaded"イベントハンドラの記述
WindowLoadedメソッドでは、メインウィンドウの初期化完了イベントを処理し、アプリケーションのデータの初期化、Kinectの初期化・起動を行います(図1)。処理の流れは以下の通りです。- “KinectSensor kinect = …”で、Kinectセンサーのオブジェクトを取得します。これはKinectセンサーが1台正しく接続されていることを前提にしています。実際のアプリケーションではそのような前提はおけませんので、エラー処理などが必要になります。今回は省略していますので、もしうまく動かない場合はKinectの接続など確認しましょう。(※ 実は今回のアプリケーションは終了時の処理も省略しています。「まずは全体像を」というコンセプトのため、いいかげんなコードですがご容赦ください)
- ColorStreamのEnableメソッドを呼び出し、カラーストリームの出力を有効化します。引数で解像度とフレームレートが指定できます。
- バッファなどのアプリケーションデータを初期化します。pixelBufferは、Kinectのカラーストリームの画像情報を一時的に読み込むバッファです。フレーム毎に再利用しますので、領域だけ確保しておきます。
bmpBufferは、メインウィンドウのImageコントロール(rgbImage)にセットするビットマップオブジェクトです。今回のアプリケーションでは、フレーム毎に書き直すのでWriteableBitmapを使っています。 - カラーフレームのイベントハンドラとしてColorImageReadyメソッドを登録します。この書き方はC#言語特有の書き方であり、大きな特長なのですが、今回は説明を省略します。ここでは「イベント += メソッド」と書くと、イベントにハンドラメソッドが登録できると理解しておけば十分です。
- 最後にkinectのStartメソッドで、ストリーム取得を開始します。以降、Kinectランタイムからフレーム毎に登録したColorFrameReadyメソッドが呼び出されるようになります。
- "ColorFrameReady"イベントハンドラの記述
ColorImageReadyメソッドでは、Kinectランタイムから画像情報をフレーム毎に受け取り、bmpBufferに描画することで、メインウィンドウの画像を更新します(図1)。処理の流れは以下の通りです。- “ColorImageFrame imageFrame = e.OpenColorImageFrame()“ で、イベント引数から、OpenColorImageFrameメソッドでイメージフレームのデータを受け取り、imageFrame変数に保持します。
ここで使用しているusing文もC#言語特有の書き方です(usingディレクティブと似ていますが別の構文です)。MSDNライブラリによると、using文とは「IDisposableオブジェクトの正しい使用を保証する簡易構文」とあります(http://msdn.microsoft.com/ja-jp/library/yh598w02.aspx)。IDisposableオブジェクトとは.NET frameworkにおいて使用後に後始末が必要なオブジェクト(これらはIDisposableインタフェースを実装することになっています)ですが、「using(オブジェクト生成) { 処理 }」と書いておくと、オブジェクトの処理後に後始末(Disposeメソッドの呼び出し)を保証してくれます。 - imageFrameから、フレームの幅と高さをそれぞれWidth, Heightプロパティから取得します。
- 画像データを、CopyPixelDataToメソッドで作業用の配列にコピーします。
- WritePixelsメソッドで、bmpBufferに取得した画像データを描画します。
- “ColorImageFrame imageFrame = e.OpenColorImageFrame()“ で、イベント引数から、OpenColorImageFrameメソッドでイメージフレームのデータを受け取り、imageFrame変数に保持します。
実行
以上で実装は完了です。第2回のサンプルアプリケーションの実行と同様に作成したアプリケーションを実行してみましょう。メニューから[デバッグ(D)]-[デバッグ実行(S)]を選択するだけです。
無事カメラの画像が表示されたでしょうか?
まとめ
今回は駆け足になりましたが、Kinectのカメラを使った簡単なアプリケーションを作成しました。次回はこのアプリケーションを修正して、よりKinectらしいアプリケーションを作成します。
商標について
Kinect、Microsoft、Silverlight、Visual C#、Visual Studio、Windowsは、米国Microsoft Corporationの米国およびその他の国における登録商標または商標です。Adobe、AIRは、米国Adobe Systems Incorporatedの米国およびその他の国における登録商標または商標です。Javaは、米国Oracle Corporation の米国およびその他の国における登録商標または商標です。
インデックスが範囲を超えています。負でない値で、コレクションのサイズよりも小さくなければなりません。
パラメーター名: index
というエラーがでました。このエラーについて教えてください。
よろしくお願いします。
回答ありがとうございました。
今度書かせていただくときはもっとわかりやすく書きたいとおもいます。
ありがとうございました。 2012-07-12 14:04
> このエラーについて教えてください。
ArgumentOutOfRangeExceptionについては以下が参考になります。
・ http://msdn.microsoft.com/ja-jp/library/system.argumentoutofrangeexception.aspx
・ http://msdn.microsoft.com/ja-jp/library/wkd6khbd.aspx
なお、おそらく聞きたいことは他にもあるのではないかと予想しますが、残念ながらもとの質問からは上記しか答えようがありません。
質問の仕方について書かれたページ、例えば http://tts.utopiat.net/bbshowto.html などを参考にしてみてください。(特に最後の「質問の仕方チェックシート」が参考になると思います)
2012-07-11 20:32