CascadeClassifier::setImageとrunAt

これは、OpenCV Advent Calendar 2015 19日目の記事です。関連記事は[目次](http://qiita.com/advent-calendar/2015/opencv)にまとめられています。
一部修正しました。12/20 00:50

#はじめに
多くの人がOpenCVでためしてみる機能は、もちろんCascade Classifierの**detectMultiScale**を使用した顔検出ですね。

この関数のチュートリアルは、[ここ](http://docs.opencv.org/2.4/doc/tutorials/objdetect/cascade_classifier/cascade_classifier.html)です。
Cascade Classifierクラスの説明は[ここ](http://docs.opencv.org/2.4/modules/objdetect/doc/cascade_classification.html)で見ることができます。detectMultiScaleの説明は[ここ](http://docs.opencv.org/2.4/modules/objdetect/doc/cascade_classification.html#cascadeclassifier-detectmultiscale)です。

detectMultiScaleはこのように使用します、以下の例は最小限の使い方です。(細かいところは気にしないでください）

```cpp:sample.cpp
cv::CascadeClassifier face_cascade;
std::vector<Rect> faces;
cv::Mat image = cv::imread("lena.jpg");
face_cascade.load( "haarcascade_frontalface_alt2.xml") 
face_cascade.detectMultiScale( image, faces);
```

これだけでOKです。検出結果の領域データは、facesに格納されています。

##サンプルの補足
本来なら、imageをグレースケールに変換し、equalizeHistを行うべきでしょうが省略しています
また、equalizeHistを行い内のであれば、グレースケールへの変換も省略できます。
detectMultiScaleはカラー画像にも対応しており、内部でchannelを調べ、カラーの場合必要は、変換して処理します。

このコードを実行すると、１つの候補が見つかります。lenaさんに描画するとこうなります。

![lena - コピー.PNG](https://qiita-image-store.s3.amazonaws.com/0/105738/1dc6dfd2-27ff-1f8f-0330-366b2c517d41.png)

左上の座標は、219,203でサイズは 174x174です。


#前置きが長くなりましたがここからが本題です。
本日のとりあげる知られざる機能はsetImageです。説明は[ここ](http://docs.opencv.org/2.4/modules/objdetect/doc/cascade_classification.html#cascadeclassifier-setimage)です。

この関数は、detectMultiScaleの内部で使用されており、通常使う機会はないと思います。
ただ一部の人には、使えると検出結果のさらなる絞込みができるので、うれしいことがあるかと思いますが、その話は別の機会に

setImageは、runAtと対で使用されるものです。runAtの説明は[ここ](http://docs.opencv.org/2.4/modules/objdetect/doc/cascade_classification.html#cascadeclassifier-runat)です
setImageを簡単に説明すると、設定されたimageに対し、FeatureEvaluatorで指定した特徴量を作成しています。
runAtは、設定されたFeatureEvaluatorで、指定した場所を左上とし、辞書サイズの大きさの部分領域の画像の分類を行います。

**runAtを実行した結果**
対象の領域がpositiveの場合は戻り値が1となります。negativeの場合は、0 および　マイナスになります。戻り値の解釈は、使用する特徴量により異なります。また、ｗは判定に使用した評価値であり、この解釈も特徴量により異なります。

**不思議なことに**
setImageとrunAtは対で使用するはずですが、なぜかsetImageはpublicで、runAtは protectedの属性が指定されており、CascadeClassifierからは、runAtを呼び出すことはできません

そこで、CascadeClassifierクラスをpublic 継承した CMyCascadeクラスを作成し、自作のdetect関数内でこの２つにアクセスすることになります。

```cpp:CMyCascade.h
#pragma once
#include <opencv2/opencv.hpp>

class CMyCascade : public cv::CascadeClassifier
{
	cv::CascadeClassifier cascade;

public:
	CMyCascade();
	~CMyCascade();

	void	detect(cv::Mat);
};
```

```cpp:CMyCascade.cpp
void CMyCascade::detect(cv::Mat image)
{
	load("haarcascade_frontalface_alt2.xml");

	cv::Mat gray;
	cv::cvtColor(image, gray, CV_BGR2GRAY);

	cv::Ptr<cv::FeatureEvaluator> evaluator = this->featureEvaluator;
	setImage(evaluator, gray);

	double w;
	
	for (int j = 0; j < image.size().height - 20; j++){
		for (int i = 0; i < image.size().width - 20; i++) {
			cv::Point p(i, j);
			int r = this->runAt(evaluator, p, w);
			if (r > 0) {
				std::cout << i << "," << j << " " << w << std::endl;
			}
		}
	}
}
```

CMyCascadeを呼び出すメイン関数

```cpp:main.cpp
#include "MyCascade.h"

	cv::Mat image = cv::imread("lena.jpg");
	cv::Mat small;
	cv::resize(image, small, cv::Size(59,59));

	CMyCascade cascade2;
	cascade2.detect(small);
```




今回のサンプルで入力されるimageは,lenaの画像そのものではなく、512x512の大きさの元画像を59x59に縮尺した画像を使用しています。このサイズは、検出された174x174のサイズから辞書サイズ20x20の比率に合わせて縮小しています。

サンプルを実行すると、５点の座標がpositive（顔領域）として判定されます。



detectMultiScale内では、複数の処理が行われております。物体検出の基本は、setImageとrunAtで実現されていますが、
ここに至る前処理と、後処理の説明はそれなりの量になりますので省略しています。
そのため上のサンプルだけでは、まったく理解できないかと思います。

#詳しく知りたい場合は
cascadedetectのソースを読めば全体の流れがわかるかと思います。ソースは
opencv2411\sources\modules\objdetect\src\cascadedetect.cpp です

**余談ですが**
高速化のために、並列処理が行われており、１３日に取り上げられた parallel_for_が使用されています.
（並列化の処理内でpositive発見ごとに、mutexのlockを行っているのはどうかと思いますが）


**今回の話は、**
OpenCV 2.4.11
OS Windows 7 Home 64bit
Visual Studio 2013 update 5
で動作確認したものです。

#補足
**18日の記事でICFが取り上げられています。
[3.1のDraft](http://code.opencv.org/projects/opencv/wiki/ChangeLog#31)が公開されています。そこで
 the results are available as a part of OpenCV 3.1 のリストに
Improved ICF detector, waldboost implementation - opencv_contrib/xobjdetect 
が含まれており、プロジェクトの場所が移動しただけだと思います。
~~（すいません　裏はまだ取っていませんので本当になくなったかも知れません）~~

**調べてみました。**
12/20の[マスター](https://github.com/Itseez/opencv_contrib/tree/master/modules)にはありません。
[Tagの3.0.0](https://github.com/Itseez/opencv_contrib/tree/3.0.0/modules)には含まれています。