書籍転載:KINECT for Windows SDKプログラミング Kinect for Windows v2センサー対応版(3)
Kinectの座標とUnityの座標を合わせる
― Chapter 6 Kinect for Windows SDK v2をUnityで使う 6.3.5 ―
UnityでKinectを使う際にポイントとなる、Kinectの座標系とUnityの座標系を合わせる方法を、実際のサンプルコードを通して説明する。
UnityでKinectを活用する方法を紹介します。前回はUnityによるKinect活用の基礎を解説しました。今回はその続きです。
書籍転載について
本コーナーは、秀和システム発行の書籍『KINECT for Windows SDKプログラミング Kinect for Windows v2センサー対応版』の中から、特にBuild Insiderの読者に有用だと考えられる項目を編集部が選び、同社の許可を得て転載したものです。
『KINECT for Windows SDKプログラミング Kinect for Windows v2センサー対応版』の詳細や購入は秀和システムのサイトや目次ページをご覧ください。プログラムのダウンロードも、秀和システムのサイトから行えます。
ご注意
本記事は、書籍の内容を改変することなく、そのまま転載したものです。このため用字用語の統一ルールなどはBuild Insiderのそれとは一致しません。あらかじめご了承ください。
■
6.3.5 Kinectの座標とUnityの座標を合わせる
ここで一つコードを触ったサンプルを紹介します。Kinectを使うUnityコンテンツの場合、Kinectのカラー画像とBodyの座標を合わせなければならないことが多いでしょう。KinectにはKinectの座標系が、UnityにはUnityの座標系があるので、どちらか一方に合わせなくてはなりません。ここではKinectの座標系をUnityの座標系に変換して表示します。
まずKinectとUnityの座標系を確認します。KinectにはBodyのカメラ座標系、Depth(赤外線、BodyIndex)のDepth座標系、カラー画像のカラー座標系の3種類があり、それぞれ相互に変換することができます。
続いてUnityです。Unityにも3つの座標系があります。Unity内の実際の座標系であるワールド座標系、画面から見たスクリーン座標系、画面の解像度に拠らない2次元のビューポート座標系があり、やはり、それぞれ変換できます。
KinectとUnityが扱える座標系のうち、いずれか1つずつを互いに変換できれば良いことになります。結論からいうと、Kinectのカラー座標系をUnityのスクリーン座標系に変換します。これによって、Kinectのカメラ座標系(Body)をUnityのワールド座標系(実空間)に移動できるようになります。
この変換にはKinectのColor/Depthの解像度とスクリーン座標のカメラの座標を合わせる必要がありますが「変換のためのカメラを別に用意する」ことで解決しています。
より具体的な手順は次の通りです。
- KinectのBody(カメラ座標)を取得する
- Kinect SDKのCoordinateMapper.MapCameraPointToColorSpace()でカメラ座標をカラー座標に変換する
- Kinectのカラー座標(1920×1080)をUnityのスクリーン座標(Screen.width,Screen.height)に比率変換する
- Untiyのスクリーン座標をCamera.ScreenToWorldPoint()でワールド座標に変換する
では、このシーンを作ってみましょう。必要なGameObjectは次の3つです。
- カラー画像表示用のCube(など表示可能なもの)
- 変換用のカメラ
- Body表示用の空のGameObject
まず、カラー画像表示用のCubeを配置します。これは先ほどカラー画像の表示で行った手順と同じです。「Hierarchy」ビューにCubeを配置します。Posision、Rorationはすべて0に、ScaleをX=19.2、Y=10.8に設定し、ColorSourceManagerおよびColorSourceViewスクリプトを追加します。
次に変換用のカメラを配置します。「Hierarchy」ビューに「Camera」を追加します。「Posotion」はX=0、Y=0、Z=−5とします。Z座標の値はCubeが見える位置であれば任意で構いません。「Projection」を「Orthographic」(平行投影)に変更します。「Perspective」ですと座標の変換ができません。最後に「Size」を変更します。「Camera」の画角が「Cube」にぴったり合うように設定します。ここでは5.4に設定しています。1つのシーンに2つのAudio Listenerがあると警告がでるため、Audio Listenerを削除または無効にします(Main Cameraのみ有効になっている状態)。
最後にBodyのための空のGameObjectを作成します。基本的には先ほどの「Bodyを表示する」と同じ手順です。
一点だけ異なるのが、先ほどは「BodySourceView」を追加しましたが、今回は「BodySourceView」をベースにコードを変更したものを使用します。
Unity上ではなく、Windowsのエクスプローラー上でBodySourceView.csファイルをコピーし、ColorBodySourceView.csとします。スクリプトのクラス名も同様にColorBodySourceViewとします。配置先のフォルダーは任意で構いませんが、ここでは「Project」の直下に「Scripts」フォルダーを作成し、そこに配置しています。
ColorBodySourceViewスクリプトを開きコードを編集します。変数として、変換用のカメラ(ConvertCamera)、Kinectの座標変換オブジェクト(CoordinateMapper)そしてKinectのカラー画像の幅と高さの値を追加します。
public class ColorBodySourceView : MonoBehaviour
{
public Material BoneMaterial;
public GameObject BodySourceManager;
private Dictionary<ulong, GameObject> _Bodies = new Dictionary<ulong, GameObject>();
private BodySourceManager _BodyManager;
public Camera ConvertCamera;
private Kinect.CoordinateMapper _CoordinateMapper;
private int _KinectWidth = 1920;
private int _KinectHeight = 1080;
...
}
|
続いてUpdate()でCoordinateMapperの設定を行います。なお、BodyManagerのSensor(KinectSensorクラス)がpublicになっていないため、BodySourceManagerにプロパティを追加します。
void Update ()
{
...
if ( _CoordinateMapper ==null ) {
_CoordinateMapper = _BodyManager.Sensor.CoordinateMapper;
}
|
BodySourceManagerを開き、次のプロパティを追加します。
public KinectSensor Sensor
{
get
{
return _Sensor;
}
}
|
最後に座標変換用のコードを追加します。ColorBodySourceViewクラスに戻り、GetVector3FromJoint()メソッドを変更します。staticメソッドになっているので、staticを削除します。KinectのBodyから取得できるJoint(関節)座標はKinectのカメラ座標系となっているので、MapCameraPointToColorSpace()メソッドでカラー座標系に変換します。
続いてScreen.widthおよびScreen.heightを使ってKinectのカラー座標系をUnityのスクリーン座標系に変換します。
最後にUnityのカメラ(Camera)のScreenToWorldPoint()メソッドでワールド座標系に変換します。このときY座標が上下逆になるので−1を掛けて反転、Z座標は単一でよいので—1(Cubeの少しカメラ側)に設定します。
private Vector3 GetVector3FromJoint(Kinect.Joint joint)
{
var valid = joint.TrackingState != Kinect.TrackingState.NotTracked;
if ( ConvertCamera != null || valid ) {
// KinectのCamera座標系(3次元)をColor座標系(2次元)に変換する
var point =_CoordinateMapper.MapCameraPointToColorSpace( joint.Position );
var point2 = new Vector3( point.X, point.Y, 0 );
if ( (0 <= point2.x) && (point2.x < _KinectWidth) &&
(0 <= point2.y) && (point2.y < _KinectHeight) ) {
// スクリーンサイズで調整(Kinect->Unity)
point2.x = point2.x * Screen.width / _KinectWidth;
point2.y = point2.y * Screen.height / _KinectHeight;
// Unityのワールド座標系(3次元)に変換
var colorPoint3 = ConvertCamera.ScreenToWorldPoint( point2 );
// 座標の調整
// Y座標は逆、Z座標は-1にする(Xもミラー状態によって逆にする必要あり)
colorPoint3.y *= -1;
colorPoint3.z = -1;
return colorPoint3;
}
}
return new Vector3( joint.Position.X * 10,
joint.Position.Y * 10,
-1 );
}
|
以上でスクリプトの変更は完了です。
空のGameObjectにBodySourceManagerとColorBodySourceViewを配置します。ColorBodySourceViewのBone MaterialにBoneMaterialを、Body Source ManagerにGameObject自身を設定します。Convert Cameraには先ほど追加した変換用のCmaeraを設定します。
これを実行すると、Kinectのカラー画像の上に関節のオブジェクトが表示されるようになります。関節のオブジェクトはUnity上のものですので、当たり判定を設定すれば、Unity内のGameObjectに対して干渉するようになります。
■
次回は、実際の活用例として、Kinectでユニティちゃんを動かす方法を説明します。
※以下では、本稿の前後を合わせて5回分(第1回~第5回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
1. Kinect開発者のための、Unityの基礎知識とインストール
UnityでKinectを活用しよう。Unityのインストール方法と、Kinectを使うためのパッケージの導入方法、Visual Studio Tools for Unityの概要を解説する。
2. UnityでKinectを使う(カラー画像/赤外線画像/Depthデータ/Body)
Unity+Kinectの開発環境が整ったら、実際にUnityからKinectの機能を使ってみよう。UnityによるKinect活用の基礎を解説。
3. 【現在、表示中】≫ Kinectの座標とUnityの座標を合わせる
UnityでKinectを使う際にポイントとなる、Kinectの座標系とUnityの座標系を合わせる方法を、実際のサンプルコードを通して説明する。
5. Kinect WPF ControlsでWPFアプリでもジェスチャーを活用しよう
WPFアプリにKinectを操作するための機能を実装する場合にはKinect WPF Controlsが便利だ。その機能概要を紹介する。