openFrameworks × OpenCV で動体検出するiOSアプリをつくる

openFrameworksとofxOpenCvを使って、 **iPhoneのカメラ入力をリアルタイム解析し、動いている物体を検出する** サンプルを作成する手順です。

ofxOpenCVと、ofVideoGrabberを使用します。

関連記事：<a href="http://qiita.com/shu223/items/8a357b92b50eaeafcf12" >openFrameworks - 「iPhoneのカメラからの入力をリアルタイム処理する」ための実装を超簡単にする ofVideoGrabber - Qiita [キータ]</a>


##プロジェクトの準備

###プロジェクト作成

videoGrabberExampleをコピー＆リネームする

###アドオンの導入

1. プロジェクトのaddonsグループに、ofxOpenCVを追加
2. [User Header Search Paths] に "../../../addons/ofxOpenCv/libs" を recursive で追加する。


##実装

###ヘッダ

ヘッダをinclude

````
#include "ofxOpenCv.h"
````

メンバ変数を追加

````
ofxCvColorImage     colorImg;
ofxCvGrayscaleImage grayImage;
ofxCvGrayscaleImage grayBg;
ofxCvGrayscaleImage grayDiff;
ofxCvContourFinder  contourFinder;

float   capW;
float   capH;
int     threshold;
bool    bLearnBakground;
````

###タッチイベントのハンドラ

画面をタップしたときにフラグが `true` になるようにする。

````
void testApp::touchDown(ofTouchEventArgs & touch){

	bLearnBakground = true;
}
````

###セットアップ

````
void testApp::setup(){	

    // フレームレート
	ofSetFrameRate(20);

    // ofVideoGrabber初期化
	grabber.initGrabber(320, 568, OF_PIXELS_BGRA);
    
    // 幅、高さ
    capW = grabber.getWidth();
	capH = grabber.getHeight();

    // 各Imageオブジェクトのメモリ確保
    colorImg.allocate(capW,capH);
    grayImage.allocate(capW,capH);
    grayBg.allocate(capW,capH);
    grayDiff.allocate(capW,capH);

    // フラグ、閾値を初期化
	bLearnBakground = true;
	threshold = 80;
}
````

###画像解析

毎フレーム呼ばれる処理。ここで画像解析を行う。

````
void testApp::update(){

	ofBackground(255,255,255);
	
    // カメラ入力取得
	grabber.update();
    colorImg.setFromPixels(grabber.getPixels(), capW, capH);
    grayImage = colorImg;

    // 背景画像取得
    if (bLearnBakground == true) {
        
        grayBg = grayImage;
        bLearnBakground = false;
    }
    
    // 差分画像を計算
    grayDiff.absDiff(grayBg, grayImage);
    grayDiff.threshold(threshold);

    // 輪郭抽出
    contourFinder.findContours(grayDiff,        // input
                               20,              // minArea
                               (capW*capH)/3,   // maxArea
                               10,              // nあered
                               true);           // find holes
}
````

ここでやってる画像解析のアルゴリズム

1. カメラ入力取得
2. 背景画像取得
3. 1と2の差分画像を計算
4. 輪郭抽出

輪郭抽出には [ofxCvContourFinder::findContours()](http://openframeworks.cc/documentation/ofxOpenCv/ofxCvContourFinder.html#!show_findContours) を利用。


##描画

得られた各種画像を画面上に描画。

````
void testApp::draw(){	

	ofPushMatrix();
	ofScale(0.5, 0.5, 1);
	ofSetHexColor(0xffffff);
    
    // カメラ入力を描画
    grabber.draw(0, 0);
    
    // カメラ入力をグレー化した画像を描画
	grayImage.draw(320, 0);
    
    // 背景画像を描画
	grayBg.draw(0, capH);
    
    // 差分画像を描画
	grayDiff.draw(320, capH);
    
    // blob領域を描画
    for (int i = 0; i < contourFinder.blobs.size(); i++){
        
        contourFinder.blobs[i].draw(capW, capH);
    }
    
	ofPopMatrix();
}
````


###できあがり

![](http://f.cl.ly/items/1A2L1i1B3h1D2i332K1l/opencv.jpg)


左上から、右下に向かって、

- 元画像
- 元画像をグレー化した画像
- 背景画像
- 差分画像

が表示されていて、差分画像上に水色で輪郭が、ピンクでblog領域が描画されています。

（本来は **カメラに手をかざすと、手の輪郭を検出し、指をblobとして抽出してくれる** サンプルなのですが、スクショを取るのに両手が必要で、自動録画をいれるのも面倒だったので、結局手もかざさないスクショとなっています。あと、スクショとるときにぶれています。）


あと、ここまで書いておいてなんですが、実は、opencvExample で

````
#define _USE_LIVE_VIDEO
````

すれば、これとほぼ同じ実装になります。


##いずれ調べる

###ofVideoGrabber で iPhone のフロントカメラを使う方法

- ofVideoGrabberのヘッダをみても、直接セットできそうなメソッドや定数の定義は見当たらなかった。
- `setDeviceID(int _deviceID)` と `listDevices()` っていうメソッドがある
  - `listDevices` でフロントカメラのデバイスIDを探して、 `setDeviceID` でセットすると使えるかも
- 最悪、 <a href="http://qiita.com/shu223/items/8a357b92b50eaeafcf12" >AVFoundationVideoGrabber</a> を修正する