読者です 読者をやめる 読者になる 読者になる

Natural Software

KinectなどのDepthセンサーを中心に活動しています

Kinect for Windows SDKのFaceTrackerで取れる3D座標を表示してみる(C++)

Kinect for Windows

最近FaceTrakingをいろいろやっていますが、やっとメインともいえる3Dデータを表示してみます。今回はOpenGLを利用するので、直前のエントリの環境を作っていることを前提としています。

以前までで顔が検出できる状態なっており、ここから3Dの点を取得および、構成する三角形のインデックスを取得します。具体的には次のような手順になります。

  1. IFTFaceTracker::GetFaceModel()で検出されたモデルをIFTModelに取得する
  2. 3Dの頂点をIFTModel::Get3DShape()で取得する
  3. 三角形の構成するための頂点のインデックスをIFTModel::GetTriangles()で取得する
  4. 取得した三角形の情報をもとにOpenGL上に表示する

全体のコードはこちらになります。

検出されたモデルをIFTModelに取得する

最初に検出されたモデルをIFTModelに取得します。IFTModelはIFTFaceTracker::GetFaceModel()で取得できますので、これを呼び出します。

// 顔追跡(公開用)

void faceTracking()

{

// 顔追跡(抜粋)

HRESULT hr = faceTracking_p();

// 顔を見つけたので、追跡状態へ遷移

if(SUCCEEDED(hr) && SUCCEEDED(pFTResult->GetStatus())) {

::OutputDebugString( L"FaceTracking Success\n" );

isFaceTracked = true;

pFT->GetFaceModel( &pFTModel );

}

// 顔を見失ったので、未追跡状態のまま

else {

::OutputDebugString( L"FaceTracking failed\n" );

isFaceTracked = false;

}

}

3Dの点を取得する

点の取得は3段階になります。

  1. IFTModel::Get3DShape()ではIFTResult::Get3DPose()で得られるパラメーター(顔の角度など)が必要になるので、これらを取得する
  2. 頂点を表すVertex、SU(Shape Unit)、AU(Animation Unit)の配列を作成する
  3. 上記パラメータを引数にして、IFTModel::Get3DShape()を呼び出す

これによって、顔を構成する頂点データが取得できます。

FLOAT scale = 0.0f;

FLOAT rotation[3] = { 0 };

FLOAT transration[3] = { 0 };

kinect.getResult()->Get3DPose( &scale, rotation, transration );

auto model = kinect.getFaceModel();

std::vector vectors(model->GetVertexCount());

std::vector su( model->GetSUCount() );

std::vector au( model->GetAUCount() );

auto hr = model->Get3DShape(&su[0], su.size(), &au[0], au.size(),

scale, rotation, transration, &vectors[0], vectors.size());

三角形を構成するためのインデックスを取得する

続いて三角形の頂点情報を取得します。これは内部のデータ列を利用するので、それらを格納する器を渡します。

UINT triangleCount = 0; 

FT_TRIANGLE* triangles = 0;

hr = model->GetTriangles( &triangles, &triangleCount );

取得した三角形の情報をもとにOpenGL上に表示する

ここまでで表示するためのデータがそろいました。取得した三角形の頂点データをすべて描画することで、3Dのモデルを表示することができます。

auto vertex = []( FT_VECTOR3D& v ){

auto scale = 5;

GLdouble vertex[3] = { v.x * scale, v.y * scale, v.z };

glVertex3dv(vertex);

};

for ( int i = 0; i < triangleCount; ++i ) {

vertex( vectors[triangles[i].i] );

vertex( vectors[triangles[i].j] );

vertex( vectors[triangles[i].k] );

}

ここでは触れなかったパラメーターや、このデータでは方向は取れるのですが、口などの動きが取れていないようなので、それらのパラメータは別途調査という感じですね。

OpenGLの使い方で改善点などあればコメントいただけると嬉しいです。