まず、それぞれの言葉の意味について回答します。
-
minNeighborsについて
『物体候補領域が何個以上重なっていたらそれを一つの物体として検出する』というイメージで大体あっていると思います。その重なり具合を指定するパラメータは存在します。詳しくは後述します。
-
scaleFactorについて
OpenCVのカスケード分類器は、Sliding Windowという技法を使って候補となる矩形を検出しています。この動画は、われらがマドンナ・レナ姐さんの画像にSliding Windowを適用しているものです。文字通り、窓を滑らせて片端から候補を見つけ出しているんですが、一巡毎にこの窓枠は縮小します。この縮小率がscaleFactorです。(たまにscaleFactorを1.0にして困っている人を見ます。)
次に、「どの程度候補領域が重なっていたら重なりと定義されているか」という点について考えていきたいと思います。私もちょっと気になったもので、今回調べてみました。
- detectMultiScale(コード)が、Sliding Windowとカスケード分類で候補となる矩形のvectorを作る。
- 上記によって得られたvectorが、groupRectangle(コード)に
rectListとして渡される。また、minNeighborsは、同時にgroupThresholdとして渡される。
- groupRectangleは、内部でpartition(コード)を呼び出す。partitionはクラスタリングを行うOpenCVの汎用関数で、第一引数にクラスタリング対象となるvectorを渡すと、第二引数にクラス番号のvectorを代入してくれる。もちろん、第一引数には
rectListが渡される。
ここで、partitionがむにゃむにゃとクラスタリングを行って、その結果を返すことがわかりましたね。その結果をもとに、groupThresholdことminNeighborsを用いて、**『充分な回数検出されている領域か?』**という判別が出来ます。
さて、次の問題は、partitionがどうクラスタリングを行っているかどうかですね。コードを見てみると、どうやら矩形の全通りを比較しているようです。比較して、**『どうやら同じクラスらしい』**と判別するための基準は、partitionの第三引数として与えられるようです。
partitionは、groupRectangle内から、次のように呼び出されます。
C++
1partition(rectList, labels, SimilarRects(eps));
と、いうわけで、SimilarRects(コード)に矩形の同一性の判断が任されていることがわかります。それでは、SimilarRectsのコードを見てみましょう。
C++
1class CV_EXPORTS SimilarRects
2{
3public:
4 SimilarRects(double _eps) : eps(_eps) {}
5 inline bool operator()(const Rect& r1, const Rect& r2) const
6 {
7 double delta = eps * ((std::min)(r1.width, r2.width) + (std::min)(r1.height, r2.height)) * 0.5;
8 return std::abs(r1.x - r2.x) <= delta &&
9 std::abs(r1.y - r2.y) <= delta &&
10 std::abs(r1.x + r1.width - r2.x - r2.width) <= delta &&
11 std::abs(r1.y + r1.height - r2.y - r2.height) <= delta;
12 }
13 double eps;
14};
まさにoperator()の部分が、二つの矩形を引数にとって、それらの同一性を判断しているんですね。たった二文しかないですし、さっそくコードを読み解いてみましょう。
一文目: epsの値を基準に、判断指標deltaを決定しています。
二文目: 全ての辺の距離がdeltaより小さいとき、同一としてtrueを返します。
さて、最後の話題です。
またその重なりの程度のパラメータ等もございましたらそのパラメータ設定の場所も含めお教えいただければと思います。
partition内で判断基準として用いられたdeltaは、第三引数epsに比例して決定されました。また、この値は、もともとgroupRactangleの第三引数epsとして与えられたもので、次のように、デフォルト値は0.2となっています。
C++
1void groupRectangles(vector<Rect>& rectList, int groupThreshold, double eps=0.2);
ですので、groupRectanglesを自ら呼び出すようなコードを書けば、パラメータepsを指定できることになります。次のようにして、実際に実行することが出来ました。(ただし、再現環境はOpenCV3)
C++
1cascade.detectMultiScale(smallImg, faces,
2 1.1, // scaleFactor
3 0, // minNeighbors(仮)
4 CV_HAAR_SCALE_IMAGE,
5 cv::Size(30, 30));
6// 以下の値は自由
7int groupThreshold = 3;
8double eps = 0.5;
9cv::groupRectangles(faces, gourpThreshold, eps);
Python
1facerect = cascade.detectMultiScale(image_gray, scaleFactor=1.04, minNeighbors=0, minSize=(80, 80), maxSize=(200, 200))
2facerect, _ = cv2.groupRectangles(np.array(facerect).tolist(), groupThreshold=3, eps=0.5)
こちらを参考にしました。事前にimport numpy as npするのを忘れずに。
Githubのコードを舐め回して、ようやくここまでわかりました... 私自身もいい勉強になりました。
退会済みユーザー
2017/07/20 08:07
2017/07/20 08:09
2017/07/20 08:09
退会済みユーザー
2017/08/18 10:08 編集
退会済みユーザー
2017/08/18 10:01 編集
2017/07/20 08:54
2017/07/20 08:54
2017/07/20 09:12
退会済みユーザー
2017/08/18 10:07 編集
退会済みユーザー
2017/08/18 10:02 編集
2017/07/20 09:22
2017/07/20 09:24
退会済みユーザー
2017/08/18 10:07 編集
2017/07/20 09:37
2017/07/20 09:45
2017/07/20 09:47
退会済みユーザー
2017/07/20 09:58
2017/07/20 10:02
退会済みユーザー
2017/07/20 10:14
退会済みユーザー
2017/08/18 10:07 編集
退会済みユーザー
2017/07/20 10:16
2017/07/20 10:29
退会済みユーザー
2017/08/18 10:07 編集
退会済みユーザー
2017/07/20 10:38
2017/07/20 10:43
2017/07/20 10:49
2017/07/20 10:55
退会済みユーザー
2017/07/20 10:56
退会済みユーザー
2017/08/18 10:07 編集
2017/07/20 11:01
退会済みユーザー
2017/07/20 11:04
退会済みユーザー
2017/07/20 11:04
2017/07/20 11:07
退会済みユーザー
2017/07/20 11:11
退会済みユーザー
2017/07/21 01:50
退会済みユーザー
2017/08/18 10:08 編集
2017/07/21 04:33
退会済みユーザー
2017/07/21 05:51
2017/09/03 17:54