UnityでOpenCVを利用した顔検出・画像処理アプリ事始め2 サンプルコード詳解


# はじめに

[前回の記事](http://qiita.com/utibenkei/items/fb93623a7ccaabb342f2)では、UnityでOpenCVを利用したアプリが簡単に作れるようになるAssetプラグイン**「[OpenCV for Unity](https://assetstore.unity.com/packages/tools/integration/opencv-for-unity-21088)」 (https://assetstore.unity.com/packages/tools/integration/opencv-for-unity-21088)**のセットアップ方法を解説しましたが、今回の記事ではAssetに付属しているサンプルアプリのコードの中身を詳しく解説します。

**特にUnityとOpenCVプラグインの連携のために用意されたユーティリティクラスやStreamingAssetsフォルダの使い方について解説したいと思います**

#サンプルアプリのコードを見る

AssetStoreでダウンロードできるAssetの中に付属しているサンプルアプリと同じものが、**[作者のGitHub](https://github.com/EnoxSoftware/OpenCVForUnity)**でも公開されています。

収録サンプルの内容は**[チュートリアルビデオ](https://www.youtube.com/watch?v=yVl4HF2I4yc)**の後半部分を見ていただければ雰囲気で解ると思いますが、一応内容の一覧を載せておきます。

* ArUcoSample	(ArUcoクラスを利用したマーカーベースARサンプル)
* BackgroundSubtractorMOG2Sample	(背景差分取得のサンプル)
* CamShiftSample	(CamShift法による物体追跡のサンプル)
* ComicFilterSample	(いわゆる漫画風加工カメラのサンプル)
* ConvexHullSample	(凸包取得のサンプル)
* DetectFaceSample	(Haar-like特徴分類器を利用した顔検出のサンプル)
* DrawingSample　　　　　(Matに対する図形や文字の描画のサンプル)
* FaceRecognizerSample	(顔認識のサンプル)
* Feature2DSample	(二つの画像の特徴点の対応付け結果を可視化するサンプル)
* GrabCutSample	(前景抽出サンプル)
* HandPoseEstimationSample	(HSV色空間による肌色検出や凸包と凸欠陥を利用して映っている手の指の本数を推定するサンプル)
* HOGDescriptorSample	(HOG特徴識別器を利用した人物検出のサンプル)
* HoughLinesPSample	(直線検出のサンプル)
* InpaintSample	(Inpaint関数を利用して画像内の不要な部分の除去をするサンプル)
* MatchShapesSample	(形状情報を用いたマッチング処理のサンプル)
* MatchTemplateSample	(テンプレートマッチング処理のサンプル)
* MSERSample	(MSER特徴量を利用した領域分割のサンプル)
* MultiObjectTrackingBasedOnColorSample	(色情報を基にした領域検出のサンプル)
* OpticalFlowSample	(Lucas-Kanade法によるオプティカルフローのサンプル)
* PlotSample	(Plotモジュールの利用サンプル)
* SeamlessCloneSample	(SeamlessCloneメソッドによる画像合成サンプル)
* SimpleBlobSample	(SimpleBlob検出器を使用した特徴点検出のサンプル)
* StereoBMSample	(BM法による視差画像の作成サンプル)
* SVMSample	(シンプルなサポートベクターマシンのサンプル)
* Texture2DToMatSample	(画像をMatに変換するサンプル)
* ThresholdSample	(閾値法による二値化処理のサンプル)
* VideoCaptureSample	(mjepg形式の動画ファイルを読み込んで表示するサンプル)
* WebCamTextureAsyncDetectFaceSample	(検出処理を別スレッド化した顔検出カメラのサンプル)
* WebCamTextureDetectCirclesSample	(円形検出カメラのサンプル)
* WebCamTextureDetectFaceSample	(顔検出カメラのサンプル)
* WebCamTextureToMatHelperSample	(ウェブカメラ画像をMatに変換するヘルパークラスの使い方サンプル)
* WebCamTextureToMatSample	(ウェブカメラ画像をMatに変換するサンプル)
* WrapPerspectiveSample	(透視変換処理のサンプル)

## OpenCVを使った顔検出カメラのサンプルの解説

OpenCVの主な機能を使ったサンプルがいろいろ用意されていますが、OpenCVといったら**顔検出**(正確にはObjectDetection)が一番有名な機能だと思います。
そこで今回の記事ではウェブカメラのリアルタイム顔検出のサンプルである**[WebCamTextureDetectFaceExampleのコード](https://github.com/EnoxSoftware/OpenCVForUnity/blob/master/Assets/OpenCVForUnity/Examples/MainModules/objdetect/FaceDetectionExample/FaceDetectionWebCamTextureExample.cs)**を例にして、「OpenCV for Unity」の具体的な使い方を解説したいと思います。
**※最新のバージョンでサンプルの内容が更新されたため、以降の説明の内容が実際のコードの内容と違う部分が存在します。（顔検出の処理部分は[FaceDetectionWebCamTextureExample](https://github.com/EnoxSoftware/OpenCVForUnity/blob/master/Assets/OpenCVForUnity/Examples/MainModules/objdetect/FaceDetectionExample/FaceDetectionWebCamTextureExample.cs)のコード、WebCamTextureのセットアップとMat変換部分は[WebCamTextureToMatExample](https://github.com/EnoxSoftware/OpenCVForUnity/blob/master/Assets/OpenCVForUnity/Examples/Basic/WebCamTextureToMatExample/WebCamTextureToMatExample.cs)のコードを参照してください。）**




|        |     顔検出カメラの処理手順      |
|:----------------|:------------|
|  init() | (初期化処理)|
|       |       webCamTextureを取得   |
|        |      顔検出用のカスケード分類器("lbpcascade_frontalface.xml")をロード    |
|  | |
| Update ()     | (毎フレーム実行される処理)|
|        |        webCamTextureのフレーム画像をMat形式に変換 |
|  	    |      	画像をグレースケールに変換 |
|        |        画像に対して分類器で顔検出を行う |
|        |        検出された顔部分に赤い矩形を描画する |
|        |        Mat形式をTexture2D形式に変換して画面に表示 |
|  | |


![QiitaOpenCV2_screenshot.jpg](https://qiita-image-store.s3.amazonaws.com/0/28558/9db8f37a-72ad-c8f4-d581-3dc0dbb8eb15.jpeg)

### 顔検出用のカスケード分類器("lbpcascade_frontalface.xml")をロード

``` csharp:WebCamTextureDetectFaceSample.cs(160行目付近)
cascade = new CascadeClassifier (Utils.getFilePath ("lbpcascade_frontalface.xml"));
```

オブジェクト検出をするための事前準備として、検出器の読み込みが必要になります。
**検出器とは検出対象とするオブジェクトの特徴を機械学習によって蓄積させた学習データ**のことです。（カスケードファイルと呼ばれる.xmlファイル）
（検出器について詳しく知りたい方は、**[このスライド](http://www.slideshare.net/takmin/20141008-40019861)**が解りやすいと思います）
今回のサンプルでは"lbpcascade_frontalface.xml"という正面顔検出用のカスケードファイルを読み込みます。

```
※オブジェクト検出に使用する検出器ファイルは、Assets/StreamingAssets/フォルダに入れておいてください。
「OpenCV for Unity」では実行時に動的に読み込むデータファイル類は、StreamingAssets/フォルダによってアプリのパッケージ内に含める必要があります。
```

### WebCamTextureのフレーム画像をMat形式に変換 ～ 画像をグレースケールに変換

``` csharp:WebCamTextureDetectFaceSample.cs(195行目付近)
Utils.webCamTextureToMat (webCamTexture, rgbaMat, colors);
```

WebCamTextureの画像データをOpenCV内で取り扱えるようにするために、**Asset独自のユーティリティクラス**の中のUtils.webCamTextureToMat()メソッドを使って、Matという型に変換します。
※ユーティリティクラスの詳しい使い方について、新しい記事を公開しました。
[OpenCV for Unityの使い方講座２　Texture2DとMatの相互変換編](https://qiita.com/utibenkei/items/d1050253fc586f17f0ec)

```
※「OpenCV for Unity」にはUnity内で使用するの画像データクラス（Texture2D）とOpenCV内で使用する画像データクラス(Mat)を相互変換するためのユーティリティクラスが用意されています。
画像を加工するOpenCVアプリを作成する場合は、基本的にはこれらのクラスを使用して
（画像入力 → Texture →変換→ Mat(OpenCVで画像処理)→変換→ Texture → 表示出力）
というような流れで画像データを変換する必要があります。
```

``` csharp:WebCamTextureDetectFaceSample.cs(232行目付近)
Imgproc.cvtColor (rgbaMat, grayMat, Imgproc.COLOR_RGBA2GRAY);
Imgproc.equalizeHist (grayMat, grayMat);
```

グレースケールに変換するのは顔検出に不要な色情報を捨てて処理を高速化するためです。
さらにヒストグラムの平坦化を行い検出処理に適した画像情報に加工しています。


### 画像に対して分類器で顔検出を行う ～ 検出された顔部分に赤い矩形を描画する

``` csharp:WebCamTextureDetectFaceSample.cs(237行目付近)
cascade.detectMultiScale (grayMat, faces, 1.1, 2, 2, // TODO: objdetect.CV_HAAR_SCALE_IMAGE
	                          new Size (webCamTexture.height * 0.2, webCamTexture.height * 0.2), new Size ());
```

入力画像、検出結果を格納するMatOfRectオブジェクト、スケールファクタ、オブジェクトを構成する近傍矩形の最小数、処理モード、最小ウィンドウサイズを引数に入れて顔検出処理をします。

``` csharp:WebCamTextureDetectFaceSample.cs(241行目付近)
OpenCVForUnity.Rect[] rects = faces.toArray ();
for (int i = 0; i < rects.Length; i++) {
	Core.rectangle (rgbaMat, new Point (rects [i].x, rects [i].y), new Point (rects [i].x + rects [i].width, rects [i].y + rects [i].height), new Scalar (255, 0, 0, 255), 2);
}
```

検出された座標情報をもとに赤い矩形を描画します。

### Mat形式をTexture2D形式に変換して画面に表示

``` csharp:WebCamTextureDetectFaceSample.cs(248行目付近)
Utils.matToTexture2D (rgbaMat, texture, colors);
```

``` csharp:WebCamTextureDetectFaceSample.cs(166行目付近)
gameObject.GetComponent<Renderer> ().material.mainTexture = texture;
```

顔検出処理を行ったMat形式の結果画像をAsset独自のユーティリティクラスを使ってTexture2D形式に戻します。
init()時にtextureを表示用のQuadのマテリアルのテクスチャにセットしてあるので、**Update()の処理が毎フレーム繰り返されることで画面の表示が最新の画像に更新され続けます。**

![QiitaOpenCV2_result.jpg](https://qiita-image-store.s3.amazonaws.com/0/28558/663ab3e2-880b-045c-8ddf-ffb978b9b04e.jpeg)


# まとめ
* 「OpenCV for Unity」では**実行時に動的に読み込むデータファイルは、StreamingAssets/フォルダによってアプリのパッケージ内に含める必要がある**
* 「OpenCV for Unity」には**Unity内で使用するの画像データクラス（Texture2D）とOpenCV内で使用する画像データクラス(Mat)を相互変換するためのユーティリティクラスが存在するので、それを利用して画像処理を行う**

今回の記事で取り上げなかった、他のサンプルコードについても基本的な処理の流れは共通しています。
**OpenCVにおけるMat関連クラスの取り扱い方や数多く用意されている画像処理関数などについては、既に多くの解説サイトやQiitaの記事に情報が公開されています**ので、そちらも参考にしてみてください。

# 関連記事
[UnityでOpenCVを利用した顔検出・画像処理アプリ事始め](http://qiita.com/utibenkei/items/fb93623a7ccaabb342f2)
UnityでOpenCVを利用した顔検出・画像処理アプリ事始め2 サンプルコード詳解
[OpenCV for Unityの使い方講座１　エラーハンドリング編](https://qiita.com/utibenkei/items/83c172f91c9c2b2be19d)
[OpenCV for Unityの使い方講座２　Texture2DとMatの相互変換編](https://qiita.com/utibenkei/items/d1050253fc586f17f0ec)
[「OpenCV for Unity」Assetのインポートサイズを削減する方法](http://qiita.com/utibenkei/items/eee141cd2f1656b19cc2)
[UnityでOpneCVを利用した動画再生をしてみた](http://qiita.com/utibenkei/items/aaaa6cf5b470aa4f69d5)
[UnityでOpneCVを利用したハコスコVR体験](http://qiita.com/utibenkei/items/a9c4e96a395666f9bf54)