USE_FACE 定数を define してから NtKinect.h を include すると、NtKinectの
がどちらも有効になります。
「顔の詳細情報 (HDFace)」の認識においては、個別の顔モデルを作成していない状態では 認識した顔の部品の位置は少しずれていることがあります。 この状態ではHDFaceの情報に基づいて顔の部品を切り出しても、 正しい部分画像は得られないことになります。
一方、「HDFaceではない普通の顔認識」においては、 取得できるのは「左目、右目、鼻、口の左端、口の右端」 の位置だけですが、かなり正確なデータを取得できるようです。 そこで、これらに関しては「顔の詳細情報(HDFace)の認識」で得られた情報を 「顔認識」で得られた情報で補正すればよいのではないか、というアイディアが浮かびます。
本稿では、顔の詳細情報の認識で得られたデータを、顔認識のデータで補正して利用する方法について解説します。
具体的には、目を縦横2倍、つまり面積4倍にした画像を生成するプログラムを示します。 目の画像を切り出す際に、「目の大きさ」は詳細情報(HDFace)の認識で得られたデータをそのまま利用し、 「目の位置」は「顔認識」で得られたデータで補正しています。
[注意] 「顔認識」で得られた情報と「顔の詳細情報(HDFace)認識」で得られた情報を併用するためには、 それが同じ人間の顔の情報であることを
faceTrackingId[i] == hdfaceTrackingId[j]で確認する必要があります。
Kinect for Windows SDK 2.0 では顔認識に関しては次のように定義されています。
Kinect for Windows SDK 2.0 の Kinect.Face.h(抜粋) |
---|
enum _FacePointType { FacePointType_None= -1, FacePointType_EyeLeft= 0, FacePointType_EyeRight= 1, FacePointType_Nose= 2, FacePointType_MouthCornerLeft= 3, FacePointType_MouthCornerRight= 4, FacePointType_Count= ( FacePointType_MouthCornerRight + 1 ) }; enum _FaceProperty { FaceProperty_Happy= 0, FaceProperty_Engaged= 1, FaceProperty_WearingGlasses= 2, FaceProperty_LeftEyeClosed= 3, FaceProperty_RightEyeClosed= 4, FaceProperty_MouthOpen= 5, FaceProperty_MouthMoved= 6, FaceProperty_LookingAway= 7, FaceProperty_Count= ( FaceProperty_LookingAway + 1 ) }; |
Kinect for Windows SDK 2.0 の Kinect.h(抜粋) |
---|
enum _DetectionResult { DetectionResult_Unknown= 0, DetectionResult_No= 1, DetectionResult_Maybe= 2, DetectionResult_Yes= 3 }; |
Kinect for Windows SDK 2.0 の Kinect.h(抜粋) |
---|
typedef struct _PointF { float X; float Y; } PointF; |
setSkeleton() 関数を呼び出して 骨格情報を取得した後に、setFace()メソッドを呼び出して顔を認識することができます。
返り値の型 | メソッド名 | 説明 | |||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
void | setFace() | version1.2以前。 setSkeleton()を呼び出した後に呼び出して顔認識をすることができる。 次のメンバ変数に値が設定される。
|
|||||||||||||||
void | setFace(bool isColorSpace = true) | version1.3以降。 setSkeleton()を呼び出した後に呼び出して顔認識をすることができる。 次のメンバ変数に値が設定される。
第1引数をfalseで呼び出した場合はDepthSpace座標系における位置が変数にセットされる。 |
型 | 変数名 | 説明 |
---|---|---|
vector<vector<PointF>> | facePoint | 顔の部品の位置。 一人の人間の「左目、右目、鼻、口の左端、口の右端」の位置が vector<PointF> であり、 複数の人間を扱うため vector<vector<PointF>> となる。 (version1.2以前)ColorSpace 座標系における位置である。 (version1.3以降)ColorSpace座標系またはDepthSpace座標系における位置である。 |
vector<cv::Rect> | faceRect | 顔の矩形領域のベクタ。 (version 1.2以前)ColorSpace 座標系における位置である。 (version1.3以降)ColorSpace座標系またはDepthSpace座標系における位置である。 |
vector<cv::Vec3f> | faceDirection | 顔の向き (pitch, yaw, roll) のベクタ |
vector<vector<DetectionResult>> | faceProperty | 顔の状態。 一人の人間の「笑顔、正面、眼鏡、左目閉じる、右目閉じる、口が開く、口が動く、目をそらす」が vector<DetectionResult> であり、 複数の人間を扱うため vector<vector<DetectionResult>> となる。 |
vector<UINT64> | faceTrackingId |
version 1.4 以降で利用できる。 trackingIDのベクタ。 顔情報 faceRect[index ] などに対応する trackingIdは faceTrackingId[index ] です。 |
モーションキャプチャ用に顔の詳細なデータ (HDFace) を取得します。
setSkeleton() 関数を呼び出して 骨格情報を取得した後に、setHDFace()メソッドを呼び出して顔の詳細情報(HDFace)を認識することができます。
返り値の型 | メソッド名 | 説明 | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
void | setHDFace() | version1.4以降。 setSkeleton()を呼び出した後に呼び出して顔の詳細情報 HDFace を認識することができる。 次のメンバ変数に値が設定される。
|
||||||||||||
pair<string,string> | hdfaceStatusToString(pair<int,int>) | version1.4以降。 顔モデルを作成するのに必要なデータの収集状況 hdfaceStatus[index ] を 引数として渡されると、状態を表す文字列のペアに変換する。 |
||||||||||||
bool | setHDFaceModelFlag(bool flag=false) | version1.8以降。 「顔モデルを作成するのに必要なデータが十分に収集された時点で 顔のモデルを生成するかどうか」のフラグを設定する。 デフォルト値はfalseで「顔モデルを生成しない」。 関数の返り値はそれまでの設定値である。 この関数を引数trueで呼び出した後で setHDFace()関数を複数回呼び出すと、 必要な情報が収集された時点で個別の顔モデルを生成するようになる。 個別の顔モデルを生成すると詳細な顔(HDFace)認識の精度が上ることが期待される。 プログラムが不安定になることがあるので、この関数は実験的な扱いとする。 |
型 | 変数名 | 説明 | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
vector<vector<CameraSpacePoint>> | hdfaceVertices | version1.4以降。 CameraSpace座標系における顔の部品の位置。 一人の人間の顔上の1347点の位置が vector<CameraSpacePoint> であり、 複数の人間を扱うため vector<vector<CameraSpacePoint>> となる。 |
||||||||||||||||||||||||||||||||
vector<UINT64> | hdfaceTrackingId | version1.4以降。 trackingIdのベクタ。 hdfaceVertices[index ]に対応するtrackingIdが hdfaceTrackingId[index ]である。 |
||||||||||||||||||||||||||||||||
vector<pair<int,int>> | hdfaceStatus | version1.4以降。 顔の認識状態。 一人の人間の顔情報 HDFace の認識状態が FaceModelBuilderCollectionStatus と FaceModelBuilderCaptureStatus のペアである pair<int,int>であり、 複数の人間を扱うため vector<pair<int,int>> となる。 FaceModelBuilderCollectionStatus次の状態のORとなる。
FaceModelBuilderCaptureStatus次の状態のどれかとなる。
|
このプロジェクトは次のように変更されているはずです。
青い文字部分が今回の内容に直接関係している部分ですので、 ここをよく読んで理解して下さい。 緑色の文字部分は"work"画面の表示に関する部分ですので、 実行が確認できた後は消しても構いません。
main.cpp |
#include <iostream> #include <sstream> #define USE_FACE #include "NtKinect.h" using namespace std; int getFaceIndex(NtKinect& kinect, UINT64 trackingId) { for (int i=0; i< kinect.faceTrackingId.size(); i++) { if (kinect.faceTrackingId[i] == trackingId) return i; } return -1; } void copyRect(cv::Mat& src, cv::Mat& dst, int sx, int sy, int w, int h, int dx, int dy) { if (sx+w < 0 || sx >= src.cols || sy+h < 0 || sy >= src.rows) return; if (sx < 0) { w += sx; dx -= sx; sx=0; } if (sx+w > src.cols) w = src.cols - sx; if (sy < 0) { h += sy; dy -= sy; sy=0; } if (sy+h > src.rows) h = src.rows - sy; if (dx+w < 0 || dx >= dst.cols || dy+h < 0 || dy >= dst.rows) return; if (dx < 0) { w += dx; sx -= dx; dx = 0; } if (dx+w > dst.cols) w = dst.cols - dx; if (dy < 0) { h += dy; sy -= dy; dy = 0; } if (dy+h > dst.rows) h = dst.rows - dy; cv::Mat roiSrc(src,cv::Rect(sx,sy,w,h)); cv::Mat roiDst(dst,cv::Rect(dx,dy,w,h)); roiSrc.copyTo(roiDst); } void bigEye(NtKinect& kinect,cv::Mat& result,cv::Mat& work,vector<CameraSpacePoint>& hdEye,PointF& fEye) { cv::Rect rect = kinect.boundingBoxInColorSpace(hdEye); double cx = rect.x + rect.width/2, cy = rect.y + rect.height/2; double dx = fEye.X - cx, dy = fEye.Y - cy; cv::Rect rect2((int)(rect.x+dx), (int)(rect.y+dy), rect.width, rect.height); double margin = 0.5, mw = rect2.width * margin, mh = rect2.height * margin; cv::Rect rect3 ((int)(rect2.x-mw/2), (int)(rect2.y-mh/2), (int)(rect2.width+mw), (int)(rect2.height+mh)); if (rect3.x < 0 || rect3.y < 0 || rect3.x+rect3.width >= kinect.rgbImage.cols || rect3.y+rect3.height >= kinect.rgbImage.rows) { cerr << "rect3: " << rect3 << endl; return; } cv::Mat eyeImg(kinect.rgbImage, rect3); double scale = 2.0; cv::resize(eyeImg,eyeImg,cv::Size((int)(eyeImg.cols*scale), (int)(eyeImg.rows*scale))); copyRect(eyeImg, result, 0, 0, eyeImg.cols, eyeImg.rows, (int)(rect3.x-(scale-1)*rect3.width/2), (int)(rect3.y-(scale-1)*rect3.height/2)); cv::rectangle(work, rect, cv::Scalar(0,255,0), 2); cv::rectangle(work, rect2, cv::Scalar(0,0,255), 2); cv::rectangle(work, rect3, cv::Scalar(255,0,0), 2); cv::rectangle(work, cv::Rect((int)(fEye.X-2), (int)(fEye.Y-2), 4, 4), cv::Scalar(0,255,255), -1); cv::rectangle(work, cv::Rect((int)(cx-2), (int)(cy-2), 4, 4), cv::Scalar(255,0,255), -1); } void doJob() { NtKinect kinect; while (1) { kinect.setRGB(); kinect.setSkeleton(); kinect.setFace(); kinect.setHDFace(); cv::Mat result = kinect.rgbImage.clone(); cv::Mat work = kinect.rgbImage.clone(); for (int i=0; i<kinect.hdfaceTrackingId.size(); i++) { int idx = getFaceIndex(kinect,kinect.hdfaceTrackingId[i]); if (idx < 0) continue; auto& hdFace = kinect.hdfaceVertices[i]; vector<CameraSpacePoint> hdLeft({ hdFace[HighDetailFacePoints_LefteyeInnercorner], hdFace[HighDetailFacePoints_LefteyeOutercorner], hdFace[HighDetailFacePoints_LefteyeMidtop], hdFace[HighDetailFacePoints_LefteyeMidbottom] }); vector<CameraSpacePoint> hdRight({ hdFace[HighDetailFacePoints_RighteyeInnercorner], hdFace[HighDetailFacePoints_RighteyeOutercorner], hdFace[HighDetailFacePoints_RighteyeMidtop], hdFace[HighDetailFacePoints_RighteyeMidbottom] }); bigEye(kinect,result,work,hdLeft,kinect.facePoint[idx][0]); // left eye bigEye(kinect,result,work,hdRight,kinect.facePoint[idx][1]); // right eye } for (int i=0; i<kinect.hdfaceVertices.size(); i++) { for (CameraSpacePoint sp : kinect.hdfaceVertices[i]) { ColorSpacePoint cp; kinect.coordinateMapper->MapCameraPointToColorSpace(sp,&cp); cv::rectangle(work, cv::Rect((int)cp.X-1, (int)cp.Y-1, 2, 2), cv::Scalar(0,192, 0), 1); } } cv::resize(work,work,cv::Size(work.cols/2,work.rows/2)); cv::imshow("work", work); cv::imshow("result", result); auto key = cv::waitKey(1); if (key == 'q') break; } cv::destroyAllWindows(); } int main(int argc, char** argv) { try { doJob(); } catch (exception &ex) { cout << ex.what() << endl; string s; cin >> s; } return 0; } |
2個のウィンドウが表示され、一方は "work" という名前で認識状態が、 他方は "result" という名前で目が拡大された画像が示されます。
"work" ウィンドウには、
"result" ウィンドウには、"work" の青の長方形領域のRGB画像を取り出して、 縦横2倍に拡大(面積は4倍に拡大)してから貼り付け直した画像が表示されます。 貼り付ける前に、拡大した目の画像のアルファ値を周辺領域だけ小さくしておき それを反映するようにブレンドすると自然な合成画像ができますが、 わかりやすさを優先して上記のプログラムではそのような処理は省略しています。
![]() |
![]() |
workウィンドウ (一部) | result ウィンドウ(一部) |
---|
上記のzipファイルには必ずしも最新の NtKinect.h が含まれていない場合があるので、 こちらから最新版をダウンロードして 差し替えてお使い下さい。