Kinect for Windows SDK 2.0 Unity Pro Add-in で シンプルなデプス画像を表示する
はじめに
Microsoftから配布されている Kinect for Windows SDK 2.0 Unity Pro Add-in には
- カラー画像
- 赤外画像
- デプスにカラー画像をマッチングさせた立体画像
- 人のボーン表示
を表示するサンプルシーンが含まれているのですが、デプスを単純にビットマップで表示するサンプルがありません。UnityでKinect v2 を使ってみる手慣らしとして作ってみました。結果が上の写真になります。
基本的なところ
Kinect for Windows v2
Kinect for Windows v2 良いですね。先代からの変化点などは、以下のサイト様が非常に詳しいです。いつもお世話になっております。
「Kinect for Windows SDK 2.0 Public Preview」が公開されています - Natural Software
上記事様のスライドが非常に良くまとまっています。
環境
- Windows 8.1 Pro 64bit
- Unity 4.5.2 Pro
- Kinect for Windows v2
SDK(およびドライバ)導入
Unityで使える様にするために、まずはWindowsからアクセス出来るようドライバ等をインストールし、機器を認識させます。
Kinect v2 をWindowsに認識させるところで手こずっている方が結構いらっしゃるようです。私はこちらのサイト様を参考に、USB3.0 カードを追加することで、Kinect v2 を認識させることに成功しました。
正しく認識できていたら、Kinect Studio などを動かして、デプス画像が表示されるか確認してみましょう。
Kinect for Windows SDK 2.0 Unity Pro Add-in を導入する
おなじみNatural Software様 の記事通りでOKです。まずは、サンプルのシーンが正しく表示されるか確認すると良いでしょう。
私の環境では、DLしたzipを Cube ICE を使って解凍したところ、エラーが出てファイルが正しく展開されませんでした。Windows標準の解凍機能を使ったら、すべてのファイルが展開されました。
シンプルなデプス画像
デプス情報の取得部
デプスの情報をセンサーから取得する部分については、 配布物に含まれて居る(上記サンプルシーンでも使われている) DepthSourceManager.cs
が行ってくれますので、それをそのまま流用します。ヒエラルキに空のオブジェクトをひとつ追加し、それに scripts/DepthSourceManager.cs
をアタッチします。(下図)
デプス情報の表示部
ここではデプス情報をUnityのシーン内に描画するために、以下の事を行っています。
- 表示にはQuadメッシュのゲームオブジェクトを利用し、テクスチャとしてデプス画像を貼り付けることにする。
- 毎フレーム以下の事をする
- ushort 形式でデプスデータを取得する
- 取得したデプスデータを byte 形式に変換し、更にテクスチャを生成する
- テクスチャを、ゲームオブジェクトのマテリアルに貼り付ける
ゲームオブジェクトの生成
できあがったゲームオブジェクトは下図のようになります。一個ずつ説明します。
- ヒエラルキにQuadのオブジェクトを作ります
- 画像を表示したいだけなので、ライティングの影響を受けない
Unlit/Texture
のマテリアルを作成し、アタッチします - 正しい表示になる様に、オブジェクトを回転させます。ここではZ軸中心に180度回しています。
- Scaleは (1, 1, 1) のままだと小さいのですが、スクリプトから変更します。縦横比は固定なので、インスペクタから直接編集してもOKです。
- 以下に示すスクリプトをアタッチします
using UnityEngine; using System.Collections; using Windows.Kinect; public class SimpleDepthView : MonoBehaviour { public GameObject depthSourceManager; private DepthSourceManager depthSourceManagerScript; Texture2D texture; byte[] depthBitmapBuffer; FrameDescription depthFrameDesc; public float scale = 1.0f; void Start () { // Get the description of the depth frames. depthFrameDesc = KinectSensor.GetDefault().DepthFrameSource.FrameDescription; // get reference to DepthSourceManager (which is included in the distributed 'Kinect for Windows v2 Unity Plugin zip') depthSourceManagerScript = depthSourceManager.GetComponent<DepthSourceManager> (); // allocate. depthBitmapBuffer = new byte[depthFrameDesc.LengthInPixels * 3]; texture = new Texture2D(depthFrameDesc.Width, depthFrameDesc.Height, TextureFormat.RGB24, false); // arrange size of gameObject to be drawn gameObject.transform.localScale = new Vector3 (scale * depthFrameDesc.Width / depthFrameDesc.Height, scale, 1.0f); } void Update () { updateTexture (); gameObject.renderer.material.mainTexture = texture; } // referred the code below. thx kaorun55-san // https://github.com/kaorun55/Kinect-for-Windows-SDK-v2.0-Samples/blob/master/C%23(WinRT)/02_Depth/KinectV2-Depth-01/KinectV2/MainPage.xaml.cs void updateTexture() { // get new depth data from DepthSourceManager. ushort[] rawdata = depthSourceManagerScript.GetData (); // convert to byte data ( for ( int i = 0; i < rawdata.Length; i++ ) { // 0-8000を0-256に変換する byte value = (byte)(rawdata[i] * 255 / 8000); int colorindex = i * 3; depthBitmapBuffer[colorindex + 0] = value; depthBitmapBuffer[colorindex + 1] = value; depthBitmapBuffer[colorindex + 2] = value; } // make texture from byte array texture.LoadRawTextureData (depthBitmapBuffer); texture.Apply (); } }
画像の階調表現のはなし
黒から白へ256段階のグラデーションで全域を表現する
ポイントは、uShort 形式のデプス情報を byte 形式の RGBA 配列に圧縮しているところで、ここは、Natural Software様のこちらの記事を参考にさせていただきました。
http://www.naturalsoftware.jp/blog/8846
この変換だと、 0mm - 8000mm を8000階調で返してくるKinect for Windows v2 のデプスを、黒(0) - 白(255) にマップしています。 諧調が落ちてしまいますが、遠近感が分かりやすい表示になります。
色の折り返しを許容して、グレースケースで8000階調を再現する
上記も良いのですが、せっかくKinect v2が8000階調でデータを渡してくれるので、それを再現します。256段階のグレーのグラデーションを繰り返していきます。
これで、8000階調の本領が発揮されます。写っているのが私で申し訳ないのですが、先ほどはわからなかった服のしわなどが再現されているのがわかるかと思います。
ソースコードの変更点は以下のような感じです。
void updateTexture() { // get new depth data from DepthSourceManager. ushort[] rawdata = depthSourceManagerScript.GetData (); // convert to byte data ( for ( int i = 0; i < rawdata.Length; i++ ) { // 0-8000を0-255に変換する // byte value = (byte)(rawdata[i] * 255 / 8000); // 0-8000 を色の折り返しを許容して256段階にマップする byte value = (byte)(rawdata[i] % 256); // ★変更点 int colorindex = i * 3; depthBitmapBuffer[colorindex + 0] = value; depthBitmapBuffer[colorindex + 1] = value; depthBitmapBuffer[colorindex + 2] = value; } // make texture from byte array texture.LoadRawTextureData (depthBitmapBuffer); texture.Apply (); }
8000階調をHSV表色系でHueを回して表現する
上記だと255->0となるときにパキッと色が折り返してしまうので、その違和感をなくすためにHSV表色系を利用してみます。色相hueを虹のようにぐるぐる回すことで連続的なデプス変化を表現できます。
色のパキッとした折り返しがなく、かつ階調も細かくデプスが表現されているのでは、と思います。
ソースコードの変更点は以下のような感じです。
using UnityEditor; // HSVToRGB()のためにusingを追加 // 中略 void updateTexture() { // get new depth data from DepthSourceManager. ushort[] rawdata = depthSourceManagerScript.GetData (); // convert to byte data ( for ( int i = 0; i < rawdata.Length; i++ ) { // HSVで色相をぐるぐる回す int value = (int)rawdata[i] % 360; Color color = EditorGUIUtility.HSVToRGB( (float)value/360.0f, 0.5f, 1.0f); int colorindex = i * 3; depthBitmapBuffer[colorindex + 0] = (byte)(color.r * 255.0f); depthBitmapBuffer[colorindex + 1] = (byte)(color.g * 255.0f); depthBitmapBuffer[colorindex + 2] = (byte)(color.b * 255.0f); } // make texture from byte array texture.LoadRawTextureData (depthBitmapBuffer); texture.Apply (); }
最後に
やってみましたが、色の変化は、グレースケースの変化に比べると、すこし変化がわかりにくいかなぁ、という印象を持ちました。2番目にやったものの方が、服のしわや指の一本一本内のデプス変化がわかりやすい感じがします。
こういった画像でのデプス表示はあくまでデバッグ用だったり、目的の途中段階だったりすると思うので、用途に応じて使い分ければよいと思います。