#はじめに
少し前に「複数台のAzureKinectを用いてプロジェクター投影面と位置合わせを行いたい」という話がありまして、TouchDesignerでKinectの位置合わせを試したので簡単に共有したいと思います。
#やったこと
「現実空間に投影されているChArUcoマーカーのコーナーの位置」と「3D空間に配置されているChArUcoマーカーのコーナーの位置」を用意して、これら2つの点群セットに対してレジストレーションによる変換行列を推定しました。推定した変換行列をKinectのポイントクラウドに掛け合わせることで以下のように位置を合わせます。
2台のグレーのカメラは位置合わせを行ったAzure Kinectで、緑色のカメラはプロジェクターの投影面のみを切り取っています。このままでは実際にモノを認識することはできませんが、点群の背景差分を行った上で投影面で切り取った映像に対して領域分割や矩形検出等を加えることで映像のどの位置にモノが映っているかわかります。
複数台のKinectをそれぞれ位置合わせするだけでも少し手間な作業ですが、プロジェクターの投影面にマーカーを用いることで映像への位置合わせも同時に解決できました。
#手順
① プロジェクターでChArUcoマーカーを平面に投影する
② Kinectで撮影したChArUcoマーカーのコーナーの位置(A)を取得する
③ 3D空間上にChArUcoマーカーを配置してコーナーの位置(B)を取得する
④ (A)と(B)から変換行列を推定する
##① プロジェクターでChArUcoマーカーを平面に投影する
###ChArUcoマーカーの作成
ChArUcoマーカーは今後も使えそうなのでtoxにしました。
(※ChArUcoを使用するためにはTouchDesignerのパッケージにopencv-contribが必要です)
Board :ChArUcoマーカーの設定を入力して指定のフォルダにChArUcoマーカー画像を作成する
Window:プロジェクターで投影するための機能
中身はDATでChArUcoマーカーを作成するコードを書いてます。
###ChArUcoマーカーの投影
これをプロジェクターで投影します。プロジェクターのアスペクトが16:9なのでマーカーも16x9で作成してます。
##② Kinectで撮影したChArUcoマーカーのコーナーの位置を取得する
TouchDesigner上でポイントクラウドを可視化するとこんな感じ。
###Kinectのカラー画像でChArUcoマーカーのコーナーの座標(x, y)を取得する
Kinectのカラー画像からChArUcoマーカーのコーナーを検出します。
・soruce
カラー画像をグレースケール化した入力画像
・get_2d_corners
ChArUcoマーカーのコーナーの座標検出部分
・draw_image
ChArUcoマーカーのコーナーを描画した検出結果画像(デバッグ用)
・checkervoard_2d_corners
検出したコーナーの番号と座標(ピクセルの位置)
###検出したコーナーの座標を元に位置(x, y, z)を取得する
検出したコーナーの座標(ピクセル位置)を元にKinectのデプス画像から深度値を取得します。
・checkerboard_2d_corners
検出したコーナーの番号と座標
・pointcloud
Kinectのポイントクラウド
・get_3d_corners
ChArUcoマーカーのコーナーの位置検出部分
・checkervoard_3d_corners
検出したコーナーの番号と位置
検出したコーナーの位置(checkerboard_3d_corners)を可視化するとこうなります。
(黄緑の点が検出したコーナー)
##③ 3D空間上にChArUcoマーカーを配置してコーナーの位置を取得する
charuco.toxからChArUcoマーカーの情報を取得してcheckerboard, grid, checkerboard_cornersを配置した図です。
赤い点が各コーナーの位置を示しており、checkerboard_corners_worldPositionに位置を格納しています。
レジストレーションの際にはKinect側で検出したコーナーのみ使用するため、使わないコーナーを取り除きます。
(黄緑がKinectで検出したコーナー、赤が3D空間上に配置したコーナー)
##④ 変換行列を推定する
2つの点群セットを入力として変換行列を推定します。
・in1
Kinectで撮影したChArUcoマーカーのコーナーの位置
・in2
3D空間上にChArUcoマーカーを配置してコーナーの位置
caluculate_matrix4x4でOpen3Dのレジストレーションによる変換行列の推定を行っています。
http://www.open3d.org/docs/0.7.0/python_api/open3d.registration.TransformationEstimationPointToPoint.html
(※使用する場合はOpen3Dのパッケージを追加する必要があります)
import open3d as o3d
import numpy as np
sourcePoints = np.empty((0,3), float)
targetPoints = np.empty((0,3), float)
for point in op('in1').rows():
sourcePoints = np.append(sourcePoints, np.array([[float(point[0].val), float(point[1].val), float(point[2].val)]]), axis=0)
for point in op('in2').rows():
targetPoints = np.append(targetPoints, np.array([[float(point[0].val), float(point[1].val), float(point[2].val)]]), axis=0)
source = o3d.geometry.PointCloud()
target = o3d.geometry.PointCloud()
source.points = o3d.utility.Vector3dVector(sourcePoints.tolist())
target.points = o3d.utility.Vector3dVector(targetPoints.tolist())
transformEstimation = o3d.registration.TransformationEstimationPointToPoint()
transformEstimation.with_scaling = True
corres = o3d.utility.Vector2iVector(np.repeat(np.arange(op('in1').numRows), 2).reshape(op('in1').numRows,2))
m = transformEstimation.compute_transformation(source, target, corres)
matrix = tdu.Matrix(m[0],m[1],m[2],m[3])
matrix.fillTable(op('result'))
##おわり
もう少し整理できればよかったんですが時間がきたのでここまで..
また時間があるときに説明だけでも追加しようと思います。
(ここを詳しくなどリクエストがあればご連絡ください)