Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
連載:Intel Perceptual Computing SDK(現:RealSense SDK)入門(3)

連載:Intel Perceptual Computing SDK(現:RealSense SDK)入門(3)

Colorカメラを使って顔を検出する

2014年1月29日

Intel Perceptual Computing SDKを使ったアプリの開発方法を解説。Webカメラでも行える顔検出を実装してみよう。

Natural Software 中村 薫
  • このエントリーをはてなブックマークに追加

 いよいよIntel Perceptual Computing SDK(以下、PerC SDK。現在は「Intel RealSense SDK」)を使ったアプリケーションの開発方法について解説する。

 本連載のコードは次の環境での動作を確認している。

 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のプロパティシートを選択する

 以上で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から取得できるようになる。

NuGetの更新
OpenCVをNuGetから取得する

 準備が整ったので、OpenCVをNuGetから取得しよう。ソリューションエクスプローラーで、プロジェクト項目を右クリックし、表示されるコンテキストメニューから[NuGet パッケージの管理]を選択する(次の画面)。

NuGetの管理画面を表示する

 これにより表示されるダイアログの右上にある検索窓に「opencv」と入力すると、OpenCVのパッケージが表示されるので、これを[インストール]する(次の画面)。

NuGetからOpenCVをインストールする

 インストールが正常に終わればOKだ。

最初のプログラム

 最初に環境構築の確認も含めて、PerC SDKのバージョンを表示するプログラムを作成する。下記のプログラムが実行できれば環境の構築はOKだ。

C++
// 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;
}
最初のPerC SDKプログラム

 上記のプログラム実行すると、次の画面のようにバージョン番号が表示される。本稿ではRelease7を利用しているので「1.7」が表示される。

実行結果

Colorカメラの画像を表示する

 次にColorカメラの画像を表示する。

 全体のコードは下記のリンク先を参照してほしい。

1ヘッダーファイル

 UtilPipelineクラス(前回解説)をベースに開発する。UtilPipelineクラスを使うための「util_pipeline.h」および、OpenCVを利用するための「<opencv2\opencv.hpp>」をインクルードする(次のコード)。

C++
#include "util_pipeline.h"
#include <opencv2¥opencv.hpp>
ヘッダーファイル

2UtilPipelineクラスから継承したクラス

 UtilPipelineクラスには、センサーからのイベントを処理するハンドラーが定義されている。これをオーバーライドすることで、アプリケーションがセンサーイベントを受け取れるようになる。そのためのクラスとして、UtilPipelineクラスを継承したPipelineクラスを定義する(次のコード)。

C++
class Pipeline: public UtilPipeline { ……省略……
UtilPipelineクラスから継承したクラス

3定数/変数の宣言

 Colorカメラの解像度および画像フォーマットを定義する。

 解像度は、int型の定数として「Width」(幅)変数と「Height」(高さ)変数、画像フォーマットはPXCImage::ColorFormat列挙体の変数として「colorFormat」変数を宣言する(次のコード)。Senze3D(Creative Interactive Gesture Camera Developer Kit)は640×480および1280×720の解像度をサポートしているので、使いたい解像度を設定する。Webカメラを利用する際は、そのWebカメラが対応する解像度を設定すればよい。

C++
//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()(※本連載では関数は「()」で表現する)を呼び出す。ここで画像フォーマットおよび解像度を設定する。

C++
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」から選択でき、違いはアルファチャネルがあるかないかとなる。

C++
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,
};
ColorFormat列挙体の定義

5新しいフレームの更新イベント

 Color画像などの更新関数である、OnNewFrame()をオーバーライドする(次のコードを参照)。処理の流れは次の通りだ。

  • QueryXxx()でフレームを取得する
  • データを取得する
  • データを処理する
  • データを表示する

 Color画像の処理を例にすると、QueryImage()でこのフレームのデータを取得する。後述するgetColorData()でColorデータを取得、処理として必要であればColorをcv::flip()で反転し鏡像にする。最後にcv::imshow()で表示する。

C++
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::COLOR_FORMAT_RGB32のColorデータフォーマット

 最後に取得したデータをPXCImage::ReleaseAccess()で解放して終了だ。

C++
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 );
}
Colorデータを表示する

7main関数

 main()内では、Pipeline型の変数を宣言し、UtilPipeline::LoopFrames()を呼び出してメインループを実行する(次のコード)。メインループ内でフレームの更新を行うことで、前述のOnNewFrame()が呼び出される仕組みだ。

C++
int _tmain(int argc, _TCHAR* argv[])
{
  try {
    Pipeline pipeline;
    pipeline.LoopFrames();
  }
  catch ( std::exception& ex ) {
    std::cout << ex.what() << std::endl;
  }
 
  return 0;
}
main関数

 このプログラムを実行し、Color画像が表示されればOKだ。

Color画像を表示する

顔を検出する

 続いて顔の検出を行う。プログラムはColor画像の表示に追加する形で行う。全体のコードは下記のリンク先を参照してほしい。

コンストラクター

 顔の位置検出を開始するためのEnableFaceLocation()を呼び出す(次のコード)。設定などは不要だ。

C++
Pipeline()
  : UtilPipeline()
  , colorFormat( PXCImage::COLOR_FORMAT_RGB32 )
{
  // 必要なデータを有効にする
  EnableImage( colorFormat, Width, Height );
 
  // 顔の検出を有効にする
  EnableFaceLocation();   // 追加
}
コンストラクター

OnNewFrame()

 顔の検出を有効にすると、OnNewFrame()で更新されたフレームを取得できるようになる。QueryFace()を呼び出すことで、PXCFaceAnalysis型の顔の解析フレームが取得できる。フレームデータを基に、drawFaceDetection()でデータ取得およびColor画像への位置表示を行っている。

C++
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オブジェクトに顔の位置が格納されているので、それを使って顔の位置を表示する
C++
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の最大の特長である手指の検出を行う。

連載:Intel Perceptual Computing SDK(現:RealSense SDK)入門(3)
1. Intel Perceptual Computing(PerC) SDKの全体像

Intel Perceptual Computing SDKの概要と、それを利用したアプリの開発方法について解説する連載スタート。今回はセンサーモジュールの仕様や、SDKの概要、Intel社の3Dセンシング技術などについて紹介。

連載:Intel Perceptual Computing SDK(現:RealSense SDK)入門(3)
2. Intel Perceptual Computing(PerC) SDKの概要と環境構築

PerC SDKの開発環境やアーキテクチャ、インストール方法について解説。またSDKに含まれているサンプルを紹介することで、PerCが提供する機能について見ていく。

連載:Intel Perceptual Computing SDK(現:RealSense SDK)入門(3)
3. 【現在、表示中】≫ Colorカメラを使って顔を検出する

Intel Perceptual Computing SDKを使ったアプリの開発方法を解説。Webカメラでも行える顔検出を実装してみよう。

連載:Intel Perceptual Computing SDK(現:RealSense SDK)入門(3)
4. Depthカメラを使って手指を検出する

PerC SDKの最大の特長である「手指の検出」を解説。Depthカメラのデータを取得する方法も説明する。

連載:Intel Perceptual Computing SDK(現:RealSense SDK)入門(3)
5. 無償で簡単にアプリに組み込める「音声認識&音声合成」

マイクに向かってしゃべると音声をテキスト化する「音声認識」や、テキストを音声データに変換する「音声合成」をPC上のアプリで実現したい場合、無償のPerC SDKが便利だ。その開発方法を解説。

サイトからのお知らせ

Twitterでつぶやこう!