Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
書籍転載:Intel RealSense SDKセンサープログラミング(6)

書籍転載:Intel RealSense SDKセンサープログラミング(6)

Visual Studioで活用するRealSenseセンシング機能
― Chapter 9 Visual Studioで作る「WPFデスクトップ」アプリケーション ―

2015年9月1日

RealSenseとVisual Studioを使ったサンプルアプリケーションで、RealSenseのセンサーから情報を取得してみる。

初音 玲
  • このエントリーをはてなブックマークに追加

 前回は、RealSenseとVisual Studioを使ったサンプルアプリケーションを実際に作成しました。今回はその続きです。

書籍転載について

 本コーナーは、翔泳社発行の書籍『Intel RealSense SDKセンサープログラミング』の中から、特にBuild Insiderの読者に有用だと考えられる項目を編集部が選び、同社の許可を得て転載したものです。

 

 『Intel RealSense SDKセンサープログラミング』の詳細や購入は翔泳社のサイト目次ページをご覧ください。プログラムのダウンロードも、翔泳社のサイトから行えます。

ご注意

本記事は、書籍の内容を改変することなく、そのまま転載したものです。このため用字用語の統一ルールなどはBuild Insiderのそれとは一致しません。あらかじめご了承ください。

9-5 センシングロジック

 RealSenseからアプリが必要な情報を取得する方法を説明します。

9-5-1 センシングロジック用クラスの作成

 RealSenseとやりとりするためのクラスを作成しましょう。ソリューションエクスプローラでModelsフォルダを右クリックして[追加]→[クラス]と選択し、ダイアログで「RSModel.cs」という名前のクラスを追加します。

 このアプリには心臓部といえる部分が2つあり、1つは先に説明したCameraPageで、もう1つがこのRSModuleクラスです。本節では、このクラスの内容を順番に説明していきます。

9-5-2 RSModelの外部仕様

 RSModelクラスの外部仕様を説明します。RealSenseから得られた値は、この外部仕様を通してのみ、のぞき見ることができます。

>画像データを出力するImageSourceプロパティの定義

 RSModel.csの中で、RealSenseからの画像データを出力するプロパティを定義します。

C#
private ImageSource _ColorImageElement;
public ImageSource ColorImageElement
{
  get { return this._ColorImageElement; }
  set
  {
    this._ColorImageElement = value;
    OnPropertyChanged();
  }
}
リスト9.6 画像データを出力するプロパティ(RSModel.csより抜粋)
>表情データを出力するResultプロパティの定義

 RSModel.csの中で、RealSenseからの表情データを出力するプロパティを定義します。

C#
private TResult _Result = null;
public TResult Result
{
  get { return this._Result; }
  set
  {
    this._Result = value;
    OnPropertyChanged();
    this.IsResult = (this._Result != null);
  }
}
リスト9.6 画像データを出力するプロパティ(RSModel.csより抜粋)

 TResult型はリスト9.8のように定義しておきます。

C#
public class TResult
{
  public int Face { get; set; }
  public int Score { get; set; }
  public int Sentiment { get; set; }
  public int SScore { get; set; }
}
リスト9.8 TResult型の定義(RSModel.csより抜粋)
>クラス生成時の初期処理

 本サンプルでは、定期的にRealSenseからのデータを取得する方法を採用しています。初期処理では定期的に処理をするためのタイマーの準備を行います。タイマー値は30ミリ秒間隔とします。

C#
private DispatcherTimer Timer = new DispatcherTimer();

public RSModel()
{
  this.Timer.Interval = new TimeSpan(0, 0, 0, 0, 30);
  this.Timer.Tick += Timer_Tick;
}
リスト9.9 タイマーの準備(RSModel.csより抜粋)
>センシング開始処理

 RSStartメソッドを実行するとセンシング開始するように実装します。

C#
private PXCMSenseManager SenseManager;
private PXCMFaceData FaceData;

public void RSStart()
{
  try
  {
    this.SenseManager = PXCMSenseManager.CreateInstance(); // ……1
    /* */
    if (InitializeFace() >= pxcmStatus.PXCM_STATUS_NO_ERROR) // ……2
    {
      this.Timer.Start(); // ……3
    }
  }
  catch (Exception ex)
  {
    this.Message = ex.Message;
  }
}
リスト9.10 センシングの開始処理(RSModel.csより抜粋)

 CreateInstanceでRealSenseと接続します(1)。InitializeFace内部メソッド*2の中でセンシング機能を有効化し(2)、有効化が成功したらタイマーをスタートして定期的にセンシングデータを取得します(3)。

  • *2 センシング機能の有効化は9-5-3項で詳しく説明する。

 PXCMSenseManagerは、libpxccle.cs.dllに定義されたクラスです。このクラスを通して、インテル RealSense SDKの本体である「libpxccpp2c.dll」を経由してRealSenseにつながります。

>センシング終了処理

 センシングする必要がなくなったらセンシング終了処理を行います。このサンプルでは、タイマーの停止やRealSenseとの切断、画面表示用データのクリアなどを行っています。

C#
public void RSStop()
{
  this.Timer.Stop();
  this.SenseManager.Dispose();
  this.Result = null;
  this.ColorImageElement = null;
}
リスト9.11 センシングの終了処理(RSModel.csより抜粋)

9-5-3 センシング機能を有効にする

 センシング開始時の機能有効化の部分を少し詳しく見ていきましょう。InitializeFace内部メソッドは大きく分けて次のような処理の流れになっています。

図9.12 処理の流れ

図9.12 処理の流れ
>顔検出を有効にする

 今回のアプリでは表情検出が必要です。そのためには顔検出が必要なので、顔検出を有効にします。顔検出を有効にするには、EnableFaceメソッドを実行します。

C#
result = this.SenseManager.EnableFace();
リスト9.12 顔検出の有効化(RSModel.csより抜粋)
>表情検出を有効にする

 顔検出が有効にできたら、次に表情検出を有効にします。表情検出を有効にするには、EnableEmotionメソッドを実行します。

C#
result = this.SenseManager.EnableEmotion();
リスト9.13 表情検出の有効化(RSModel.csより抜粋)
>パイプラインを初期化する

 検出するものを有効化したら、パイプラインを初期化してRealSenseからの測定値受信の準備を行います。

C#
result = this.SenseManager.Init();
リスト9.14 パイプラインの初期化(RSModel.csより抜粋)
>ミラー表示にする

 今回のサンプルは対面で使うため、鏡と同じように顔の右側が画面に向かって右側に来るようにデータをSDK側でミラー表示に変更するように指示します。

C#
this.SenseManager.QueryCaptureManager().QueryDevice().
  SetMirrorMode(PXCMCapture.Device.MirrorMode.MIRROR_MODE_HORIZONTAL);
リスト9.15 ミラー表示にする処理(RSModel.csより抜粋)
>顔検出器を設定する

 顔検出した結果を受信するための顔検出器を生成して設定を行います。

C#
//顔検出器を生成する
var faceModule = this.SenseManager.QueryFace();

// 顔検出器の設定
var device = this.SenseManager.QueryCaptureManager().QueryDevice();
PXCMCapture.DeviceInfo info = null;
device.QueryDeviceInfo(out info);
if (info.model == PXCMCapture.DeviceModel.DEVICE_MODEL_IVCAM)
{
  device.SetDepthConfidenceThreshold(1);
  device.SetIVCAMFilterOption(6);
  device.SetIVCAMMotionRangeTradeOff(21);
}
リスト9.16 顔検出器の生成(RSModel.csより抜粋)
>検出モードをカラー画像モードに設定

 顔のトラッキングをカラー画像で行う旨を指定します。今回のアプリではカラー画像からでも十分な精度でしたが、アプリの特性によってはFACE_MODE_COLOR_PLUS_DEPTHを指定することも検討してください。

C#
var config = faceModule.CreateActiveConfiguration();
config.SetTrackingMode(PXCMFaceConfiguration.TrackingModeType.FACE_MODE_COLOR);
config.ApplyChanges();
config.Update();
this.FaceData = faceModule.CreateOutput();
リスト9.17 顔検出モードをカラー画像モードに設定(RSModel.csより抜粋)

9-5-4 センシングデータ取得処理

 今回のサンプルではタイマーを使って30ミリ秒ごとにセンシングデータを取得しています。

C#
private void UpdateFrame()
{
  // フレームを取得する
  if (this.SenseManager.AcquireFrame(false) >= pxcmStatus.PXCM_STATUS_NO_ERROR) //……1
  {
    // フレームデータを取得する
    var sample = this.SenseManager.QuerySample(); //……2
    if (sample != null)
    {
      // 各データを表示する
      UpdateColorImage(sample.color);   //……3
    }
    updateFaceFrame();                  //……4

    // フレームを解放する
    this.SenseManager.ReleaseFrame();   //……5
  }
}
リスト9.18 30ミリ秒ごとにセンシングデータを取得する(RSModel.csより抜粋)

 AcquireFrameメソッドでその時点のセンシングフレームを取得します(1)。QuerySampleでフレーム中のセンシングデータを取得します(2)。UpdateColorImage内部メソッドで画像データを保存します(3)。UpdateFaceFrame内部メソッドを呼び出して表情データを保存します(4)。ReleaseFrameメソッドでフレームを解放します(5)。

9-5-5 画像データの保存

 画像データを保存するUpdateColorImage内部メソッドは、リスト9.19のようになっています。

C#
private void UpdateColorImage(PXCMImage colorFrame)
{
  if (colorFrame != null)
  {
    PXCMImage.ImageData data = null;
    var ret = colorFrame.AcquireAccess(PXCMImage.Access.ACCESS_READ, PXCMImage.PixelFormat.PIXEL_FORMAT_RGB24, out data); //……1
    if (ret >= pxcmStatus.PXCM_STATUS_NO_ERROR)
    {
      var info = colorFrame.QueryInfo();
      var length = data.pitches[0] * info.height;
      var buffer = data.ToByteArray(0, length);
      this.ColorImageElement = BitmapSource.Create(
        info.width, info.height, 96,
        96,
        PixelFormats.Bgr24,
        null,
        buffer,
        data.pitches[0]);
      colorFrame.ReleaseAccess(data);
    }
  }
}
リスト9.19 画像データの保存処理(RSModel.csより抜粋)

 UpdateColorImage内部メソッドでは、画像フレームデータからAcquireAccessメソッドによりRGB24(8ビット×3原色)の画像データを取り出して、ビットマップに変換してColorImageElementプロパティを更新します(1)。

 ColorImageElementプロパティを更新するとMainViewModelクラス経由でカメラ画面の表示に反映されます。これでRealSenseから送られてきた画像が本アプリのカメラ画面で表示できます。

9-5-6 表情データの保存

 UpdateFaceFrame内部メソッドの中では、表情データを取り出して保存するために図9.13のような流れで処理を行います。

図9.13 表情データの処理の流れ

図9.13 表情データの処理の流れ

 それぞれ、以下のようなコードで実装しています(リスト9.20~9.22)。

C#
var emotionDet = this.SenseManager.QueryEmotion();
リスト9.20 エモーションデータの取得(RSModel.csより抜粋)
C#
this.FaceData.Update();
リスト9.21 顔データ領域の更新(RSModel.csより抜粋)
C#
PXCMEmotion.EmotionData[] datas;
emotionDet.QueryAllEmotionData(index, out datas);
リスト9.22 表情の取得(RSModel.csより抜粋)

 リスト9.22で使用しているQueryAllEmotionDataメソッドでは、検出した顔の1つを指定して、表情や感情の推定値をdatas配列に格納します。datas配列の内容は表9.1のようになります。

要素位置列挙値意味
0 EMOTION_PRIMARY_ANGER 怒っている状態
1 EMOTION_PRIMARY_CONTEMPT 侮辱している状態
2 EMOTION_PRIMARY_DISGUST 嫌悪している状態
3 EMOTION_PRIMARY_FEAR 恐怖している状態
4 EMOTION_PRIMARY_JOY 楽しんでいる状態
5 EMOTION_PRIMARY_SADNESS 悲しんでいる状態
6 EMOTION_PRIMARY_SURPRISE 驚いている状態
7 EMOTION_SENTIMENT_POSTIVE 前向きの表情を浮かべている状態
8 EMOTION_SENTIMENT_NEGATIVE 後向きの表情を浮かべている状態
9 EMOTION_SENTIMENT_NEUTRAL 前向きでも後ろ向きでもない表情を浮かべている状態
表9.1 取得情報の内容
>一番強い表情の保存

 取得した指定値の要素位置0~6の中で値が一番大きいものを表情として保存します。

C#
PXCMEmotion.EmotionData[] datas;
emotionDet.QueryAllEmotionData(index, out datas);
//追加:表情(PRIMARY)を推定するif (datas != null)
{
  int primaryDataInxex = int.MinValue;
  float maxscoreI = 0;
  for (var emotionIndex = 0; emotionIndex <= NUM_PRIMARY_EMOTIONS - 1; emotionIndex++)
  {
    if (datas[emotionIndex].intensity > maxscoreI)
    {
      maxscoreI = datas[emotionIndex].intensity;
      primaryDataIndex = emotionIndex;
    }
  }
  if (primaryDataIndex >=0)
  {
    this.Result = new TResult
    {
      Face = primaryDataIndex,
      Score = (int)Math.Truncate((maxscoreI * 100))
    };
  }
}
リスト9.23 一番強い表情の保存(RSModel.csより抜粋)
>一番強い感情の保存

 取得した指定値の要素位置7~9の中で値が一番大きいものを感情として保存します。

C#
 
if (Result != null)
{
  //追加:感情(Sentiment)を推定する
  //表情(PRIMARY)の推定と同様なので、コメントは省略
  int primaryDataIndex = int.MinValue;
  float maxscoreI = 0;
  for (var sentimentIndex = NUM_PRIMARY_EMOTIONS;
       sentimentIndex <= NUM_PRIMARY_EMOTIONS + NUM_SENTIMENT_EMOTIONS - 1;
       sentimentIndex++)
  {
    if (datas[sentimentIndex].intensity > maxscoreI)
    {
      maxscoreI = datas[sentimentIndex].intensity;
      primaryDataIndex = sentimentIndex - NUM_PRIMARY_EMOTIONS;
    }
  }
  if (primaryDataIndex >= 0)
  {
    this.Result.Sentiment = primaryDataIndex;
    this.Result.SScore = (int)Math.Truncate((maxscoreI * 100));
  }
}
リスト9.24 一番強い感情の保存

9-5-7 まとめ

 以上でSensor & Coffeeアプリの完成です。紙面の都合上、コードを抜粋した部分もありますし、デザイン時のサンプルデータなどの説明も省略しています。ぜひ、サンプルコードをVisual Studioで読み込んで、実際に見て頂き理解を深めて頂ければ幸いです。

 次回は、インテル RealSenseを活用したサンプルアプリケーション開発の第3弾として、openFrameworksとRealSenseを組み合わせる方法を紹介します。

※以下では、本稿の前後を合わせて5回分(第4回~第8回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

書籍転載:Intel RealSense SDKセンサープログラミング(6)
4. Unity+RealSenseで作るノンゲームアプリ「スマイルトレーニング」

インテル RealSense SDKとUnityを使ったサンプルアプリケーションを実際に作成してみる。

書籍転載:Intel RealSense SDKセンサープログラミング(6)
5. WPF(Visual Studio)+RealSenseで作る表情感知アプリ

インテル RealSenseを活用したサンプルアプリケーション開発の第2弾として、Visual StudioとRealSenseを組み合わせる方法を紹介する。

書籍転載:Intel RealSense SDKセンサープログラミング(6)
6. 【現在、表示中】≫ Visual Studioで活用するRealSenseセンシング機能

RealSenseとVisual Studioを使ったサンプルアプリケーションで、RealSenseのセンサーから情報を取得してみる。

書籍転載:Intel RealSense SDKセンサープログラミング(6)
7. openFrameworksで作成するメディアアート入門

インテル RealSenseを活用したサンプルアプリケーション開発の第3弾として、openFrameworksとRealSenseを組み合わせる方法を紹介する。

書籍転載:Intel RealSense SDKセンサープログラミング(6)
8. openFrameworksによるメディアアートとRealSenseを組み合わせよう!

RealSenseとopenFrameworksを使ったサンプルアプリケーションで、RealSenseのセンサーから情報を取得してみる。

サイトからのお知らせ

Twitterでつぶやこう!