Azure Kinect DKで入力処理を作成した時の話
導入
今回、制作中のゲームのInput周りの開発をしました。
ゲーム自体の内容には触れませんが、イメージでは4人のプレイヤーが机を囲むように座り、デジタルボードゲームを現実でプレイするゲームです。
この時プレイヤーは入力機器としてそれぞれ違う道具を持っておりInput情報として、その道具が存在するデジタルボードゲーム上の座標とその種類が必要になってきます。
また、今回のゲームを作るときに決まっていることは以下の3点でした。
- プリジェクターを使用してプロジェクションマッピングのような形で作ること
- Azure Kinect DK を使用すること
- Unityでゲームを作ること
この時点で座標を取る方法として考えたものは以前、HoloDiveというゲームで使用していたvuforiaというサービスでした。 しかし、HoloDiveでは会場のライトが強くカメラの映像が白飛びしてうまく物体検出ができない問題がありました。 今回プロジェクションマッピングで投影している机の上に存在するオブジェクトを認識するのはとても不可能に近いと考えました。
Azure Kinect DKには通常のRGBカメラ以外にToFで測定する深度カメラと近赤外線のNIRが実装されています。 learn.microsoft.com
そこで今回、会場のライトやプロジェクターの光量に影響しない方法として深度カメラと近赤外線カメラを使用して物体検出することにしました。
計画
①NIRの最大距離より外側にプレイヤーが持つ道具が来るようにAzure Kinect DKを配置する
②プレイヤーが持つ道具に識別できる固有のマークを反射材で付ける
③近赤外線の光を②で付けた反射材で反射させ、IR画像にプレイヤーがマークのみが映るようにする このとき画像を白黒にする
④Depth画像から机よりカメラ側の物体を白黒で映す
⑤③と④の画像でBitwiseAnd
をかけ余計なものが映ることを極力少なくする
以下の画像を参考に作成する
マスクの処理
DepthとIRの白黒画像を使用してOpenCVのBitwiseAndをかけてResultの画像を生成します。 実際にはこの段階で物体検出モデルにかけます。
使用する机がちょうど正方形の机なので上下左右の方向から一定の値だった場合物体検出で返された値は使用しないというようなMaskの計算をします。 ただし、実際に調整する際に直感的ではないのでResultにはMaskの値が大体どのくらいかを表示するために乗算した画像を表示しています。
このMaskのみOpenCVではなくGraphicsのFillRectangleを使用していますが勉強のために使用してます。
以下の記事はUnityでこのMaskの処理をしようとしていたときの記事ですがやっていることは一緒です。 qiita.com
CustomVisionでONNXモデルを作成する
今回ONNXモデルを作成するのに使用したのはCustomVisionです。 www.customvision.ai 使用理由の前に使用する条件として - 物体検出モデルが作成できる - WindowsのPC上で実行できる というものがありました。 CustomVisionでは物体検出モデルが作成でき、そのモデルをONNXなど複数の形式で書き出すことができました。 また、今回モデルの作成を個人でしていたらとても時間がかかるので比較的時間のあったプランナーに手伝ってもらうためにも何かしらのサービスを使用することは必要でした。 結果条件にあったCustomVisionを使用することになりました。 ただし、書き出されるモデルがどのYOLOモデルなのかが分からずとても時間を消費しました。
書き出したONNXモデルをNetronで表示したときに入力が3x416x416だったので試しに以下のリポジトリのコードを使用して実行したときに多少修正は必要ですが実行できたのでこのリポジトリを使用して作成していくことにしました。
UIとかこだわり
実際にゲームをプレイしてもらう会場での準備時間はとても少ないので、その場でコードの修正することはできる限り避けてすべてUIにある数値を微調整することで調整を終わらせたいというのが理想でした。
この結果を実現するためにしたことが以下の3つです。 - 実際にKinectで描画している画面の表示 - 物体検出の出力結果の表示 - 値の保存と読み込み
これを反映させたものがこの画像になります。 左側のウィンドウがMaskの値を入力や通信の開始をするもので、右のコンソールに物体検出の結果が表示されます。 また、ソフトの起動と終了時に値の保存と読み込みをするようにしました。
使用方法としては最初にSendType
でKinectRun
を押し、MaskとPositionOffsetの値で実際の座標と合うよう調整します。
ConnectIP
に送信先のIPアドレスを追加し、UDPConnectStart
を押すことで物体検出で返された座標とラベルを送信します。
DebugSender
では通信のテスト時に適当な値が欲しい時が多々あったので円を描くように座標を送信するようにしました。
以下に実際のコードがあります。 実行にはAzureKinectDKが必要です。 github.com