ブログ@kaorun55

HoloLensやKinectなどのDepthセンサーを中心に書いています。

Kinect for Windows SDKのFaceTrackerを使ってみる(C++)

C++でFaceTrackerの基本的な部分をやってみました。こちらにあったものを少し日本語にした程度ですが。

全体のコードはこちらにおいてあります。

基本的には「KINECT for Windows SDKプログラミング C++編」のサンプルに追加、修正した感じです。

必要な変数

IFTFaceTracker* pFT; // 顔追跡する人

FT_CAMERA_CONFIG videoCameraConfig; // RGBカメラの設定

FT_CAMERA_CONFIG depthCameraConfig; // 距離カメラの設定

IFTResult* pFTResult; // 顔追跡結果

IFTImage* pColorFrame; // 顔追跡用のRGBデータ

IFTImage* pDepthFrame; // 顔追跡用の距離データ

FT_SENSOR_DATA sensorData; // Kinectセンサーデータ

std::vector<unsigned char> colorCameraFrameBuffer;

std::vector<unsigned char> depthCameraFrameBuffer;

顔追跡の基本機能の初期化

やることは次の通りです。

  1. IFTFaceTrackerのインスタンスを生成する
  2. RGBおよび距離カメラの情報(幅や高さ、焦点距離)を設定する
  3. 結果を格納するIFTResultのインスタンスを生成する

2で設定するFT_CAMERA_CONFIGの焦点距離ですが、RGBカメラは NUI_CAMERA_COLOR_NOMINAL_FOCAL_LENGTH_IN_PIXELS 、 距離カメラは NUI_CAMERA_DEPTH_NOMINAL_FOCAL_LENGTH_IN_PIXELS となっています。NUI_CAMERA_COLOR_~は640x480の値、NUI_CAMERA_DEPTH_~は320x240の値となっていますので、幅と高さが変わった際には、この値を変更する必要があります。

距離カメラで具体的に示すと、NUI_CAMERA_DEPTH_~は320x240の値なので、距離カメラを640x480で動かす場合には NUI_CAMERA_DEPTH_NOMINAL_FOCAL_LENGTH_IN_PIXELS * 2を設定することになります。

// FaceTrackerのインスタンスを生成する

pFT = ::FTCreateFaceTracker();

if( !pFT ) {

throw std::runtime_error( "ERROR:FTCreateFaceTracker" );

}

// RGBカメラおよび距離カメラの設定を行う

videoCameraConfig.Width = width;

videoCameraConfig.Height = height;

videoCameraConfig.FocalLength =

NUI_CAMERA_COLOR_NOMINAL_FOCAL_LENGTH_IN_PIXELS * (width / 640);

depthCameraConfig.Width = width;

depthCameraConfig.Height = height;

depthCameraConfig.FocalLength =

NUI_CAMERA_DEPTH_NOMINAL_FOCAL_LENGTH_IN_PIXELS * (width / 320);

// FaceTrackerを初期化する

HRESULT hr = pFT->Initialize( &videoCameraConfig, &depthCameraConfig, 0, 0) ;

if( FAILED(hr) ) {

throw std::runtime_error( "ERROR:Initialize" );

}

// FaceTrackerの結果を格納先を生成する

hr = pFT->CreateFTResult( &pFTResult );

if(FAILED(hr)) {

throw std::runtime_error( "ERROR:CreateFTResult" );

}

顔追跡用のRGBおよび距離データ情報の作成

やることは次の通りです。

  1. IFTImageのインスタンスRGBおよび距離について、それぞれ生成する
  2. IFTImageにRGBおよび距離のデータを格納するためのバッファを関連付ける
  3. FT_SENSOR_DATAにそれぞれのIFTImageを設定する

// FaceTrackerで利用するRGBおよび距離データのインスタンスを生成する

pColorFrame = FTCreateImage();

pDepthFrame = FTCreateImage();

if( !pColorFrame || !pDepthFrame ) {

throw std::runtime_error( "ERROR:FTCreateImage" );

}

// RGBおよび距離データのバッファサイズを設定する

// RGBは1pixelあたり4バイト。距離は1pixelあたり2バイト

colorCameraFrameBuffer.resize( width*4 * height );

depthCameraFrameBuffer.resize( width*2 * height );

// フレームデータにバッファを設定する

// You can also use Allocate() method in which case

// IFTImage interfaces own their memory.

// In this case use CopyTo() method to copy buffers

// CopyToでもOK?

pColorFrame->Attach(width, height, &colorCameraFrameBuffer[0],

FTIMAGEFORMAT_UINT8_B8G8R8X8, width*4);

pDepthFrame->Attach(width, height, &depthCameraFrameBuffer[0],

FTIMAGEFORMAT_UINT16_D13P3, width*2);

// センサーデータを作成する

sensorData.pVideoFrame = pColorFrame;

sensorData.pDepthFrame = pDepthFrame;

sensorData.ZoomFactor = 1.0f; // Not used must be 1.0

sensorData.ViewOffset.x = 0; // Not used must be (0,0)

sensorData.ViewOffset.y = 0; // Not used must be (0,0)

顔の追跡を行う

顔の追跡はIFTFaceTracker::StartTracking()およびIFTFaceTracker::ContinueTracking()で行います。この2つは顔の追跡状態によって使い分け、追跡を開始するときはIFTFaceTracker::StartTracking()を、追跡を開始した後に継続する場合にはIFTFaceTracker::ContinueTracking()を呼び出します。

これらを呼び出す前には、初期化時に作成したcolorCameraFrameBufferおよびdepthCameraFrameBufferにKinectから取得された最新のデータを設定してください。

結果はIFTResultに格納され、IFTResult::GetStatus()で処理結果を、IFTResult::GetFaceRect()で検出した顔の矩形領域をそれぞれ取得します。またIFTResultのほかのメソッドを利用することで、2Dや3Dでの部位の位置を取得することもできるようです。

// 追跡中、未追跡によって処理が変わる

if(!isFaceTracked) {

// FaceTrakingを開始する

auto hr = pFT->StartTracking(&sensorData, NULL, NULL, pFTResult);

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

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

::OutputDebugString( L"StartTracking\n" );

isFaceTracked = true;

// 顔の領域を取得する

pFTResult->GetFaceRect( &faceRect );

}

else {

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

isFaceTracked = false;

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

}

}

else {

// FaceTrakingを継続する

auto hr = pFT->ContinueTracking(&sensorData, NULL, pFTResult);

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

// 顔を見つけたので、追跡状態のまま

::OutputDebugString( L"ContinueTracking\n" );

// 顔の領域を取得する

pFTResult->GetFaceRect( &faceRect );

}

else {

// 顔を見失ったので、未追跡状態へ遷移

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

isFaceTracked = false;

}

}

後片づけ

それぞれのRelease()を呼びます。

pFTResult->Release();

pColorFrame->Release();

pDepthFrame->Release();

pFT->Release();

こんな感じで顔を検出することができます。

FaceTrackerのライブラリを見てみると、Kinect SDKからは独立したライブラリになっていること、入力フォーマットがいくつか設定できることから、Kinect SDK以外(具体的にはOpenNI)でも使えるような気がします(想像ですけど)。

[amazon asin='9784798033709']