連載:Intel Perceptual Computing SDK(現:RealSense SDK)入門(3)
Colorカメラを使って顔を検出する
Intel Perceptual Computing SDKを使ったアプリの開発方法を解説。Webカメラでも行える顔検出を実装してみよう。
いよいよIntel Perceptual Computing SDK(以下、PerC SDK。現在は「Intel RealSense SDK」)を使ったアプリケーションの開発方法について解説する。
本連載のコードは次の環境での動作を確認している。
- Windows 8.1 Pro(64bit版)
- Visual Studio 2012 Express for Windows Desktop
- Intel Perceptual Computing SDK Release7
- OpenCV(バージョンや環境設定について後述する)
Visual Studioの最新バージョンは「Visual Studio 2013」だが、PerC SDKが2013に対応したライブラリをリリースしていないため「Visual Studio 2012」を使っている。
サンプルコードの全てはこちらで公開しているので、参照してほしい。本サンプルコードのビルドは、下記で解説するNuGetを利用している。ビルド時に必要なパッケージをダウンロードするため、ネットワークが接続されている場所で行っていただきたい。
プロジェクトを作成する
さっそくプロジェクトを作成する。Visual Studioから新しいプロジェクトを作成し、C++でコンソールアプリケーションを作成する(次の画面)。
PerC SDKの環境を設定する
PerC SDKのプロジェクト設定には、ヘッダーファイルパスの指定、リンクするライブラリパスの指定、リンクするライブラリ名の指定があるが、これらの設定をまとめた、「props」ファイルがPerC SDKに含まれている。このpropsファイルを読み込ませるだけで、必要な設定が行われる。
propsファイルの読み込みは、「プロパティマネージャー」から行う。これには、Visual Studioのメニューバーから、[表示]-[その他のウィンドウ]-[プロパティ マネージャー]を選択する(次の画面)。
これにより表示されるプロパティマネージャーの[既存のプロパティシートの追加]ボタンをクリックする(次の画面)。
ファイル選択ダイアログが表示されるので、PerC SDKのインストールフォルダのpropsフォルダーにある「VS2010-12.Integration.MT.props」ファイルを選択する(次の画面)。
以上でPerC SDK側の設定は完了だ。続いてOpenCVの設定を行う。
OpenCVの環境を設定する
今回はPerC SDKから取得された画像の表示には、「OpenCV」というグラフィックスライブラリを利用する。
OpenCVのインストール方法はさまざまな方法があるが、本稿では「NuGet」と呼ばれるパッケージマネージャーから取得する。これによってOpenCVのサイトから取得、設定する方法に比べて簡単に利用できるようになる。一方でヘッダーファイルやバイナリなどがソリューションごとにダウンロードされるため、ディスク容量が増える。自分の環境に応じて使い分けていただきたい。
OpenCVのバージョンについては、NuGetのバージョンに依存するため明記しない。執筆時(2014年1月)に登録されているバージョンは「OpenCV 2.4.8」だ。本稿では基本的な機能しか利用しないため、バージョン2系であれば問題なく動作する。
NuGetのバージョンを確認する
以前のNuGetではC#など.NET用のパッケージのみ利用可能であったが、バージョン2.5からC++でも利用できるようになった。Visual Studio 2012のNuGetは、バージョン2.5より古い場合があるので、バージョンを確認し、最新バージョンに更新する必要がある。
Visual Studioのメニューバーから[ツール]-[拡張機能と更新プログラム]を選択する。これにより表示されるダイアログで、[更新プログラム]-[Visual Studio ギャラリー]を開く(次の画面)。ここに「NuGet」があれば更新してVisual Studioを再起動する。これでOpenCVをNuGetから取得できるようになる。
OpenCVをNuGetから取得する
準備が整ったので、OpenCVをNuGetから取得しよう。ソリューションエクスプローラーで、プロジェクト項目を右クリックし、表示されるコンテキストメニューから[NuGet パッケージの管理]を選択する(次の画面)。
これにより表示されるダイアログの右上にある検索窓に「opencv」と入力すると、OpenCVのパッケージが表示されるので、これを[インストール]する(次の画面)。
インストールが正常に終わればOKだ。
最初のプログラム
最初に環境構築の確認も含めて、PerC SDKのバージョンを表示するプログラムを作成する。下記のプログラムが実行できれば環境の構築はOKだ。
// 01_FirstApp.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//
#include "stdafx.h"
#include <iostream>
#include "util_pipeline.h"
#include <opencv2¥opencv.hpp>
int _tmain(int argc, _TCHAR* argv[])
{
PXCSession *session = 0;
PXCSession_Create( &session );
PXCSession::ImplVersion version = { 0 };
session->QueryVersion( &version );
std::cout << version.major << "." << version.minor << std::endl;
return 0;
}
|
上記のプログラム実行すると、次の画面のようにバージョン番号が表示される。本稿ではRelease7を利用しているので「1.7」が表示される。
Colorカメラの画像を表示する
次にColorカメラの画像を表示する。
全体のコードは下記のリンク先を参照してほしい。
1ヘッダーファイル
UtilPipelineクラス(前回解説)をベースに開発する。UtilPipelineクラスを使うための「util_pipeline.h」および、OpenCVを利用するための「<opencv2\opencv.hpp>」をインクルードする(次のコード)。
#include "util_pipeline.h"
#include <opencv2¥opencv.hpp>
|
2UtilPipelineクラスから継承したクラス
UtilPipelineクラスには、センサーからのイベントを処理するハンドラーが定義されている。これをオーバーライドすることで、アプリケーションがセンサーイベントを受け取れるようになる。そのためのクラスとして、UtilPipelineクラスを継承したPipelineクラスを定義する(次のコード)。
class Pipeline: public UtilPipeline { ……省略……
|
3定数/変数の宣言
Colorカメラの解像度および画像フォーマットを定義する。
解像度は、int型の定数として「Width」(幅)変数と「Height」(高さ)変数、画像フォーマットはPXCImage::ColorFormat列挙体の変数として「colorFormat」変数を宣言する(次のコード)。Senze3D(Creative Interactive Gesture Camera Developer Kit)は640×480および1280×720の解像度をサポートしているので、使いたい解像度を設定する。Webカメラを利用する際は、そのWebカメラが対応する解像度を設定すればよい。
//static const int Width = 640;
//static const int Height = 480;
static const int Width = 1280;
static const int Height = 720;
PXCImage::ColorFormat colorFormat;
|
4コンストラクター
下記のコードに示すように、colorFormat変数に「PXCImage::COLOR_FORMAT_RGB32」を設定する。PXCImage::COLOR_FORMAT_RGB32は、1ピクセル当たり8bitずつのRGBデータ、および未使用の8bitデータの、全32bitで構成される。
次に利用するデータを有効化する。Colorイメージを有効にするので、EnableImage()(※本連載では関数は「()」で表現する)を呼び出す。ここで画像フォーマットおよび解像度を設定する。
Pipeline()
: UtilPipeline()
, colorFormat( PXCImage::COLOR_FORMAT_RGB32 )
{
// 必要なデータを有効にする
EnableImage( colorFormat, Width, Height );
}
|
なお、PXCImage::ColorFormatは、次のように定義されている。「IMAGE_TYPE_COLOR」以下がColor画像に対するフォーマット、「IMAGE_TYPE_DEPTH」以下がDepthデータに対するフォーマットである。RGBデータとして取得する場合、「COLOR_FORMAT_RGB32」および「COLOR_FORMAT_RGB24」から選択でき、違いはアルファチャネルがあるかないかとなる。
typedef pxcEnum ColorFormat;
enum {
COLOR_FORMAT_YUY2 = IMAGE_TYPE_COLOR,
COLOR_FORMAT_NV12,
COLOR_FORMAT_RGB32,
COLOR_FORMAT_RGB24,
COLOR_FORMAT_GRAY,
COLOR_FORMAT_DEPTH = IMAGE_TYPE_DEPTH,
COLOR_FORMAT_VERTICES,
};
|
5新しいフレームの更新イベント
Color画像などの更新関数である、OnNewFrame()をオーバーライドする(次のコードを参照)。処理の流れは次の通りだ。
- QueryXxx()でフレームを取得する
- データを取得する
- データを処理する
- データを表示する
Color画像の処理を例にすると、QueryImage()でこのフレームのデータを取得する。後述するgetColorData()でColorデータを取得、処理として必要であればColorをcv::flip()で反転し鏡像にする。最後にcv::imshow()で表示する。
virtual bool OnNewFrame()
{
try {
// フレームを取得する
auto colorFrame = QueryImage( PXCImage::IMAGE_TYPE_COLOR );
// データを取得する
cv::Mat colorImage( Height, Width, CV_8UC4 );
getColorData( colorImage, colorFrame );
// 画像を左右反転する
//cv::flip( colorImage, colorImage, 1 );
// 表示する
cv::imshow( "Color Camera", colorImage );
}
catch ( std::exception& ex ) {
std::cout << ex.what() << std::endl;
}
auto key = cv::waitKey( 10 );
return key != 'q';
}
|
6Colorデータを取得する
フレームからColorデータを取得するgetColorData()では、先ほどQueryImage()で取得したPXCImage型のcolorFrame変数を引数として受け取る。
getColorData()関数内では(下記のコードを参照)、PXCImage::AcquireAccess()により、データへのアクセス権をPXCImage::ImageData型のdata変数に取得する。
アクセス権を取得できたら、Colorデータをメモリ上にコピーする。Colorデータ列の先頭アドレスがPXCImage::ImageData::planes[0]に格納されており、データ長としてはPXCImage::ImageData::pitches[0]に1ラインのバイト数(=Width×1ピクセルあたりのバイト数)が格納されているので、それにHeightの値を掛けることで、全体のバイト数を算出できる。ちなみに、コンストラクターで指定した「PXCImage::COLOR_FORMAT_RGB32」のColorデータフォーマットは次の図のようになっている。
最後に取得したデータをPXCImage::ReleaseAccess()で解放して終了だ。
void getColorData( cv::Mat& colorImage, PXCImage* colorFrame )
{
// Colorデータを取得する
PXCImage::ImageData data = { 0 };
auto sts = colorFrame->AcquireAccess( PXCImage::ACCESS_READ, colorFormat, &data );
if ( sts < PXC_STATUS_NO_ERROR ) {
return;
}
// Colorデータを可視化する
memcpy( colorImage.data, data.planes[0], data.pitches[0] * Height );
// Colorデータを解放する
colorFrame->ReleaseAccess( &data );
}
|
7main関数
main()内では、Pipeline型の変数を宣言し、UtilPipeline::LoopFrames()を呼び出してメインループを実行する(次のコード)。メインループ内でフレームの更新を行うことで、前述のOnNewFrame()が呼び出される仕組みだ。
int _tmain(int argc, _TCHAR* argv[])
{
try {
Pipeline pipeline;
pipeline.LoopFrames();
}
catch ( std::exception& ex ) {
std::cout << ex.what() << std::endl;
}
return 0;
}
|
このプログラムを実行し、Color画像が表示されればOKだ。
顔を検出する
続いて顔の検出を行う。プログラムはColor画像の表示に追加する形で行う。全体のコードは下記のリンク先を参照してほしい。
コンストラクター
顔の位置検出を開始するためのEnableFaceLocation()を呼び出す(次のコード)。設定などは不要だ。
Pipeline()
: UtilPipeline()
, colorFormat( PXCImage::COLOR_FORMAT_RGB32 )
{
// 必要なデータを有効にする
EnableImage( colorFormat, Width, Height );
// 顔の検出を有効にする
EnableFaceLocation(); // 追加
}
|
OnNewFrame()
顔の検出を有効にすると、OnNewFrame()で更新されたフレームを取得できるようになる。QueryFace()を呼び出すことで、PXCFaceAnalysis型の顔の解析フレームが取得できる。フレームデータを基に、drawFaceDetection()でデータ取得およびColor画像への位置表示を行っている。
virtual bool OnNewFrame()
{
try {
// フレームを取得する
auto colorFrame = QueryImage( PXCImage::IMAGE_TYPE_COLOR );
auto faceFrame = QueryFace(); // 追加
// 取得したデータを表示する
cv::Mat colorImage( Height, Width, CV_8UC4 );
getColorData( colorImage, colorFrame );
drawFaceDetection( colorImage, faceFrame ); // 追加
// 画像を左右反転する
//cv::flip( colorImage, colorImage, 1 );
// 表示
cv::imshow( "Color Camera", colorImage );
}
catch ( std::exception& ex ) {
std::cout << ex.what() << std::endl;
}
auto key = cv::waitKey( 10 );
return key != 'q';
}
|
顔の位置を検出する
検出された顔の位置を取得する。
顔の検出は画像処理で行うため、人数の制限はない。そのため、内部のインデックスから、検出した顔のIDを取得し、IDが取得できたら顔のデータを取得するという手順を踏む。また、顔検出のデータはPXCFaceAnalysis::DynamicCast()でPXCFaceAnalysis::Detection型のインスタンスを取得し、そこからデータを取り出す(次のコードを参照)。
少しややこしいが、次のようになっている。
- PXCFaceAnalysis::QueryFace()で「0」から始まるインデックスから、顔のIDを取得する
- 顔のIDが取得できれば(=顔が検出できれば)、顔のIDからPXCFaceAnalysis::Detection::QueryData()で顔のデータ(=PXCFaceAnalysis::Detection::Dataオブジェクト)を取得する
- PXCFaceAnalysis::Detection::Dataオブジェクトに顔の位置が格納されているので、それを使って顔の位置を表示する
void drawFaceDetection( cv::Mat& colorImage, PXCFaceAnalysis* faceFrame )
{
auto detector = faceFrame->DynamicCast<PXCFaceAnalysis::Detection>();
for ( int i = 0 ; ; ++i ) {
// ユーザーの顔IDを取得する
pxcUID fid = 0;
pxcU64 timeStampe = 0;
auto sts = faceFrame->QueryFace( i, &fid, &timeStampe );
if ( sts < PXC_STATUS_NO_ERROR ) {
break;
}
// 顔のデータを取得する
PXCFaceAnalysis::Detection::Data data;
detector->QueryData(fid, &data);
// 顔の位置を描画する
cv::rectangle( colorImage,
cv::Rect( data.rectangle.x, data.rectangle.y, data.rectangle.w, data.rectangle.h ),
cv::Scalar( 255, 0, 0 ), 3 );
}
}
|
このプログラムを実行すると、顔の位置に四角形が表示される。
まとめ
このようにPerC SDKでのColor画像および、画像処理ベースの顔検出は比較的簡単にプログラムできる。顔検出はWebカメラでも行えるため、用途は広いだろう。
次回はDepthカメラを使ってPerC SDKの最大の特長である手指の検出を行う。
1. Intel Perceptual Computing(PerC) SDKの全体像
Intel Perceptual Computing SDKの概要と、それを利用したアプリの開発方法について解説する連載スタート。今回はセンサーモジュールの仕様や、SDKの概要、Intel社の3Dセンシング技術などについて紹介。
2. Intel Perceptual Computing(PerC) SDKの概要と環境構築
PerC SDKの開発環境やアーキテクチャ、インストール方法について解説。またSDKに含まれているサンプルを紹介することで、PerCが提供する機能について見ていく。
3. 【現在、表示中】≫ Colorカメラを使って顔を検出する
Intel Perceptual Computing SDKを使ったアプリの開発方法を解説。Webカメラでも行える顔検出を実装してみよう。
5. 無償で簡単にアプリに組み込める「音声認識&音声合成」
マイクに向かってしゃべると音声をテキスト化する「音声認識」や、テキストを音声データに変換する「音声合成」をPC上のアプリで実現したい場合、無償のPerC SDKが便利だ。その開発方法を解説。