How to use JointOrientation ?

元ネタ

JointOrientation のワールド座標ってどうなってるの?

KINECTで取得できるデータの1つに JointOrientations (関節のねじれ具合)があります。

var orientations = body.JointOrientations;
// -> 左手首のねじれ具合(X, Y, Z, W)を取得する
var wristLeftOrientation = orientations[JointType.WristLeft]; 

JointOrientation はワールド座標(W)を持つ3次元のベクトルとして表現されます。このワールド座標はすべての関節でそれぞれ異なっていますので、実際には CoordinateMapper などを使って CameraSpacePoint 座標に変換して利用します。

しかし、CameraSpacePointに変換せずに利用する方が簡単なケース *1 もありますので、それぞれのワールド座標がどうなっているのかを知っておいて損はありません。

じゃあ実際どんなルールなのか

回答候補にあるように、それぞれのワールド座標は以下の様なルールに則って設定されています。

  • Bone direction(Y green) - always matches the skeleton.
  • Normal(Z blue) - joint roll, perpendicular to the bone
  • Binormal(X orange) - perpendicular to the bone and normal

適当翻訳をするとだいたい以下の様な意味でしょうか。

  • 各々のワールド座標系はSpineBaseを出発点として親から(SpineBaseに近い側の)子に向かう方向が子のY座標系(親側が0、子側が正)になります
  • Z座標はY座標系に対して鉛直かつ関節の順回転方向になります
  • X座標系はY座標系とZ座標系で構成される平面の従法線になります

日本語でも調べている人が

このブログでは、実際に「気をつけ」の姿勢をして(1,0,0),(0,1,0),(0,0,1)を各関節の四元数で回転させた方向を算出しています。

四元数とKinect その3

JointOrientationの使用例

例えばKINECT SDKに付属しているFace APIのサンプルでは顔の向きを認識するために首のOrientaionを利用しています。

/// <summary>
/// Converts rotation quaternion to Euler angles 
/// And then maps them to a specified range of values to control the refresh rate
/// </summary>
/// <param name="rotQuaternion">face rotation quaternion</param>
/// <param name="pitch">rotation about the X-axis</param>
/// <param name="yaw">rotation about the Y-axis</param>
/// <param name="roll">rotation about the Z-axis</param>
private static void ExtractFaceRotationInDegrees(Vector4 rotQuaternion, out int pitch, out int yaw, out int roll)
{
     double x = rotQuaternion.X;
     double y = rotQuaternion.Y;
     double z = rotQuaternion.Z;
     double w = rotQuaternion.W;

     // convert face rotation quaternion to Euler angles in degrees
     double yawD, pitchD, rollD;
     pitchD = Math.Atan2(2 * ((y * z) + (w * x)), (w * w) - (x * x) - (y * y) + (z * z)) / Math.PI * 180.0;
     yawD = Math.Asin(2 * ((w * y) - (x * z))) / Math.PI * 180.0;
     rollD = Math.Atan2(2 * ((x * y) + (w * z)), (w * w) + (x * x) - (y * y) - (z * z)) / Math.PI * 180.0;

     // clamp the values to a multiple of the specified increment to control the refresh rate
     double increment = FaceRotationIncrementInDegrees;
     pitch = (int)(Math.Floor((pitchD + ((increment / 2.0) * (pitchD > 0 ? 1.0 : -1.0))) / increment) * increment);
     yaw = (int)(Math.Floor((yawD + ((increment / 2.0) * (yawD > 0 ? 1.0 : -1.0))) / increment) * increment);
     roll = (int)(Math.Floor((rollD + ((increment / 2.0) * (rollD > 0 ? 1.0 : -1.0))) / increment) * increment);
}

これで JointOrientationCameraSpacePoint 座標以外でも利用することができそうです。 やりましたね :)

*1:例えば、「腕が体の前方向にあるか、後ろ方向にあるか」は両肩のJointOrientationをそのまま利用したほうが簡単でしょう

GetFacePointsInColorSpace returning all 0s multiple times a second

元ネタ

Face APIの各メソッドで取れる値が異なるケースがある

KINECT v2ではFace関連のAPIが多くあります。SDKにもサンプルがいくつか付いていますが、サンプルを動かしてみるとFace APIGetFracePointsInColorSpace() が時々結果が入っていないフレーム(全データが0sのフレーム)を返してくることがあります。

これ自体はそんなに珍しいことではないのですが、同じフレームに対して get_FaceBoundingBoxInColorSpace() を実行するとちゃんとした結果が返ってきてしまう。どっちを信じたらいいの?という質問です。ちなみに質問者のケースでは GetFaceProperties()DetectionResult_No を返してくるようです。

結論では GetFacePointsInColorSpace() で結果が帰ってこないフレームは無視してスキップしたほうが良いという回答が付いていました。 Face APIにかぎらず、KINECTから送られてくるフレームでは情報が落ちていることが時々あります。 *1

基本的にこのようなフレームはスキップするなりして、平均的な値をアプリケーションで扱うようにする方が良いようです。例えば今回の例では以下の様なコードを用いてすべてのフレームを検査することで余計なデータを回避することができます。

// check if this face frame has valid face frame results
if (this.ValidateFaceBoxAndPoints(faceFrame.FaceFrameResult))
{
    // store this face frame result to draw later
    this.faceFrameResults[index] = faceFrame.FaceFrameResult;
}
else
{
    // indicates that the latest face frame result from this reader is invalid
    this.faceFrameResults[index] = null;
}

*1:Bodyに関して言えば外れ値が出るケースが多いので外れ値を無視するコードの利用が推奨されています

Find angle between two kinect sensors

元ネタ

これすごいやりたい奴

2つのKINECTが同じ人間を検知している場合に、KINECT - 人 - KINECT のなす角ってどうやって求めたらいいの?という質問。 仕事でKINECTを使おうとすると1台ではなかなか実現できないことが多いので、複数台同時に使いたいニーズはあります。 その場合に複数KINECTがお互いにどのような位置関係にあるのか自動的に認識できると非常に助かりますので、これはなかなか興味のある内容です。

アルゴリズム的には Iterative Closest Point というものがあり、それを簡単に扱うライブラリもあるということで紹介がされています。

Interactive Closest Point ?

3次元のメッシュを回転させたりしてメッシュから無作為に抽出した対応点の距離が最小になればOKとかそういう感じっぽいです。 一種のパターンマッチングのような感じでしょうか。昔からある概念らしく論文もたくさん出ていますので、いくつか拾い読みすると良いのかもしれません。

現実的には「人間を基準点に」というのはやはり不確定要素が大きくなりますので、QRコードを貼ってそこを基準点にするとか、そういうのが必要になりそうですね。

False Positive, Average RMS in Analysis Results of VGB

元ネタ

VGBのAnalysis結果はどう見たらいいのか?

Visual Gesture BuilderはKinect Studioで撮影したClipに対してジェスチャ範囲を指定することでジェスチャを機械学習させるユーティリティです。 機械学習の強化方法には、ユーザがジェスチャ範囲を指定する方法の他に、ClipをVisual Gesture Builderに読み込ませて分析を実行した結果と人間が目で見て判断した結果を突き合わせる方法があります。

後者の強化方法は Analyze と呼ばれていますが、その出力結果が良いのか悪いのかよくわからん!という内容の質問でした。

基本の方針は2つ

ジェスチャの精度を上げるための基本的な方針は以下の2つです

  • False PositiveとFalse Negativeをなるべく少なくする
  • RMS(二乗平均平方根)を小さな値に近づけていく

FPとFNをなるべく少なくする

False PositiveとFalse Negativeはどちらも誤検知のことです。

False Positiveは「検出するべきでは ない 箇所で検出してしまった」ことを指し、False Negativeは「検出するべきで ある 箇所で検出しなかった」ことを指します。質問者が気にしているPrecision(適合率)は「False Positiveがどのくらい小さいか?」を基準とした検出精度です。*1

Visual Gesture BuilderのWhite Paperには「どのくらいのフレーム数で学習させるとFP, FNはどのくらいになるか?」を指標として出した図があります。(White PaperのFigure 3を参照)

おおまかな傾向としてFPはサンプリングするフレーム数が増えれば増えるほど減少しますが、FNについては10000フレーム程度が一番低く、20000フレーム以上読み込ませてもほとんど変化しないようです。

RMSを小さな値に近づけていく

RMSは検出のバラつき具合を表しています。RMSの値が小さいほど、ジェスチャを検出したフレームにズレがないことになります。この値を最小化するにはトレーニング用のClipに対するタグ付け(特に開始と終了のタイミング)を正確に行うことが重要になってきます(辛い!)。

その他豆知識

機械学習全般に言えることですが、機械学習で用いるデータは67%をトレーニング用、33%をテスト用に利用することが推奨されています。Kinect StudioでClipを撮影する場合にも、この割合を意識して複数のデータを取得したほうが良さそうです。

まとめ

Visual Gesture BuilderのAnalyze結果の見方と機械学習の強化方法に関する方針について書きました。 これでジェスチャの認識精度をあげられそうです。やりましたね :)

*1:Precision以外にもRecall(再現率)が検出精度の指標として存在しますが、これは「False Negativeがどのくらい小さいか?」を基準としています

Visual Gesture Builderのソリューションを分割するべきか?

Visual Gesture BuilderではVisual Studioと同じように Solution - Projects の関係でコンポーネントを管理します。 Gesture Builder Database File (.gdbファイル)はソリューション単位もしくはプロジェクト単位で作成することができますが、どのような単位でソリューションを分割することが適切なのでしょうか?

gdbファイルのビルドは(恐ろしく)重い

理想的なことを言えば論理的に把握しやすい単位(例えば対象とするお客様単位)でソリューションを分割することが望ましいと思いますが、現実としてgdbファイルのビルドには恐ろしく時間がかかります。

私が取り組んでいる仕事では、5プロジェクトが登録されているソリューションをビルドするのに30分近くかかります。各プロジェクトに組み込まれているxefファイルの総時間を合計すると50~60分程度ですから妥当といえばそれまでなのですが、これはちょっと厳しい…というのが正直な感想です。

そもそも問題はなにか

現状で問題の原因だと考えていることは以下の2つです。

  • gdbファイルをビルドしないとアプリケーションでテストできない
  • xefファイルを分割する方法がない

gdbファイルをビルドしないとアプリケーションでテストできない

gdbファイルをビルドするのに時間がかかるのに、gdbファイルをビルドしないとアプリケーションからテストできないというのは致命的です。アプリケーションのビルドはさまざまなノウハウがあって時間を短縮しやすくなってきています(サーバでビルドするとか、並列ビルドとか)が、gdbは直列にしかビルドできないのでアプリケーションの変更スピードに全く追いつけないという問題が発生しています。

一方で、単純にgdbが期待した通りの検出をするかどうかはVisual Gesture Builderのpreview機能を利用すれば検証できますので、用途によってはさほど問題にならない場合もあります。

xefファイルを分割する方法がない

ビルド時間はxefファイルの長さに大きく依存するようです。必要な時間のみのxefファイルを作成することができれば、それに越したことはありません。しかし、業務のオペレーションを記録したい場合では、いちいち作業者に止まってもらうわけにも行きません。

結果として長回しになりがちなのですが、xefファイルを分割する方法が現状存在しないので、長いビルド時間を引き起こす原因になります。

現状どうしているのか

現状では長いビルド時間を我慢して1つのソリューションにまとめています。論理単位でリソースを管理したいということの他に、KINECTのアプリケーションから利用する場合にgdb毎にインスタンスを作らなければならなかったりと色々面倒になりそうなことが理由として挙げられます。

もっとビルド時間が長くなってくると贅沢も言っていられなくなるので、どうやっていくのがベストなのか模索する必要があります。 :(

Kinect Studioの "Connect to Service" をするかしないかの違いはなに?

Kinect StudioではKINECTから撮影した様々な情報を保存・再生することができます。

Kinect Studioで保存されたクリップを再生する場合、KINECT Serviceに接続するか否かを指定することができます。一見すると再生時にKinect Serviceに接続しようがしまいが関係なさそうですが、実はちょっとした違いがあります。

Kinect Serviceに接続する場合はクリップの情報を再評価する

Kinect Serviceに接続してクリップを再生すると BodyBodyIndex の出方が接続しない場合と違うことに気がつくかもしれません。これは以下の様な違いに起因しています。

  • Kinect Serviceに接続 する 場合は
    • Kinect Serviceに対してクリップ内のColorやDepthのストリームが流し込まれ、Bodyの情報を再評価します
  • Kinect Serviceに接続 しない 場合は
    • クリップ内に保存されているBodyの情報をそのまま再生します

どんな時に影響するか

Kinect Serviceに接続してクリップを利用するつもりであれば、クリップの保存を開始するタイミングに注意をする必要があります。 KINECTは一度認識したスケルトンはしつこく追跡できますが、最初から横を向いている人などはなかなか人間として認識することができません。クリップの保存を開始するタイミングで横を向いている人がいると、

  • 保存するときには横を向く前からトラッキングしていたのでスケルトンが認識できている
  • 保存したものを再生すると、最初から横を向いているからスケルトンが認識できない

といったようなことが起こります。

一方でKinect Serviceに接続しないでクリップを利用するにはBodyなどの情報も保存が必要なため、クリップのサイズが大きくなりがちです。

まとめ

Kinect Serviceに接続するか否かでKinect Studioの挙動がどのように変わるのかまとめました。これで用途にあったクリップを保存することが出来ます。やりましたね :)

Setup for best accuracy

元ネタ

一番いいシチュエーションを頼む

KINECTを動作させるのに一番いいシチュエーションってどんな状態?というポストです。

どのような情報を取るかによって多少違いがあるかと思いますが、例えばボディトラッキングをするには以下の様なシチュエーションが最も適しているよ、というポストです。

センサーの場所

だいたい胸から頭の高さで、やや見下ろすような角度が望ましい。床が見えていれば最高!

光量

infrared frame の占める重要性がそんなに高くないのであれば、直射日光や白熱灯を避ければだいたい大丈夫。
そうでないなら他のセンサーの赤外光とかに考慮したほうがよい。

服装

ダボッとした服装は関節の位置特定を難しくする。黒っぽい服装はノイズを増やすし、より良い深度計速のためには十分な光量が必要になる。

...

私が自分で作ったアプリケーションを動作させた時の感覚も似たようなものなので、だいたい共通で使えそうです。イベント等にKINECTアプリケーションを出す場合にはチェックリストにしておくと便利かもしれません。