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)を各関節の四元数で回転させた方向を算出しています。
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); }
これで JointOrientation
を CameraSpacePoint
座標以外でも利用することができそうです。
やりましたね :)
*1:例えば、「腕が体の前方向にあるか、後ろ方向にあるか」は両肩のJointOrientationをそのまま利用したほうが簡単でしょう