Kinect開発備忘録(1)
- 2014年7月17日
- Kinect
今週15日、遂に新型Kinect、Kinect for Windows v2が発売されました! (厳密には発売されたのはパブリックベータ版で正式版は後ほど発売だとか)
先月、全世界に向けてv2の先行予約が開始されたのですが、何故か日本だけcoming soonと表記され予約が出来ない状態でした。 その状態が今月に入っても続いていたので、日本ではXboxも不調だったし、もしかして国内の発売自体が危ぶまれていたのですが……無事、発売しました。 そして、早速弊社も購入しました! センサーの到着が今から楽しみです。
さて、現在弊社ではKinectアプリの開発を進めております。 開発を進める中で特に気になったのが情報の少なさ。 日本語はもちろん英語圏でも有用な情報が見つからない事が多々あります。 Googleでクラスやプロパティを検索しても2ページしかヒットせず、しかもリファレンスとそのクラスを使用しているソースコードしか見つからず。具体的な仕様や動きがつかめない事もありました。
これに関してはWebアプリやスマートフォンアプリと異なり、開発を行っている開発者の絶対数が少ないという事も挙げられますが、KinectのSDKが頻繁に変更となっているのも大きな要因かと思います。かつての解決策も現在では使えないという事にも何度も遭遇しました。(ちなみに新型Kinect対応の最新のSDKでは、また仕様が変更となりそうです……)
そこで自分が開発を進めるにあたっての備忘録として、また他のKinectアプリを制作されている開発者への情報共有として、開発中に遭遇した問題と解決策を本ブログで公開していきたいと思います。
今回は取得した骨格の位置をWPFの画面上に反映する方法についてつまづいた点と解決方法について説明していきます。 利用シーンとしては、右手、左手の位置にカーソルを表示しアプリを操作したい、という時が考えられますね。
実装。しかし……
公式リファレンスを確認すると、Kinectセンサーから取得した骨格情報の中に、Joint.Positionというプロパティが存在しているのが分かりました。 Joint.Positionの中にX、Y、Zという座標を表しているプロパティも存在していたので、この値をそのまま使用すれば良いかと当初考えていました。 が、Joint.Positionから返ってくる値を確認したところ、-0.8や1.3などの謎の数値。 左上原点(0,0)なのにマイナスの値が返ってくるのがおかしいし、いくら腕を伸ばしても4を切る値しか返ってきませんでした。 どうやらJoint.Positionから返ってくる値は、私の期待する座標系(アプリケーション内での座標)では無かった事に気づきました。
開発者用コミュニティにすがる。しかし……
Joint.Positionのリファレンスをもう一度確認してみましょう。
http://msdn.microsoft.com/en-us/library/microsoft.kinect.joint.position.aspx
説明を確認すると「関節位置の取得、設定を行えます」。……有用な情報は記載されていないですね(笑)。
Stack Overflow等の開発者用コミュニティでこの件に関する解決策が無いかを調べてみました。 幾つか見つかって、その方法は「取得した値を画面のサイズを掛ける」というものでした。つまり、Joint.Position.Xに対しては画面の幅、Joint.Position.Yに対しては画面の高さを掛けていきます……。当然、返ってくる値がマイナスの時点でこの方法は使えません。はてして、古いSDKではこの方法で対応出来たのでしょうか。
検証してみました
ネットの情報も使えなかったので、焦る気持ちを抑えて検証してみました。
結果、Joint.Positionに入ってくる値は、Kinectセンサーから検知した頭を基準とした相対距離である事が分かりました。 例えば、Joint.Position.Xですと、頭から左方向に手が存在すると負の値、右方向に存在すると正の値が返ってきます。(値の単位は不明ですが……)
解決
少し長くなりましたので、駆け足で解決方法を書き込んでいきます。 実はCoordinateMapper.MapSkeletonPointToDepthPointという、アプリ用の座標に変換してくれるメソッドが存在していました。
以下に参考までに変換用メソッドを記載します。
// 引数はJoint.Positionをセットします
private Point SkeletonPointToScreen(SkeletonPoint skeletonPoint) {
if (skeletonPoint == null) { return; }
// ここでアプリ用の座標に変換します
DepthImagePoint depthPoint = this.kinectSensor.CoordinateMapper.MapSkeletonPointToDepthPoint(skeletonPoint, DepthImageFormat.Resolution640x480Fps30);
// 深度センサーの解像度が640×480としている為、画面の解像度も640×480でない限り表示する位置がずれてしまいます。その為、画面サイズに合わせて座標の修正を行います。
double adjustX = System.Windows.SystemParameters.PrimaryScreenWidth / 640; double adjustY = System.Windows.SystemParameters.PrimaryScreenHeight / 480;
return new Point(depthPoint.X * adjustX, depthPoint.Y * adjustY);
}
解決策は非常にシンプルですね。今ならコピペで一瞬で解決です。 ですが、この単純な解決策を見つけるまで結構時間を取られました。そういう経験もありましたので、今後Kinect開発に関する情報を本ブログに書き綴っていこうと思った次第です。
木曜日担当 Window開発チーム 古村
komu at 2014年07月17日 10:00:57