最近项目略多,其中一个需要找出一些和脸比较像但是不是脸的负样本,想用opencv的人脸检测器检测到的错误脸作为这样的负样本。
但是国内(包括国外)居然几乎没有相关的资料如何输出detectMultiScale()的置信率或者说是人脸得分
所以写一篇小小的总结供有相关需求的人参考。
转载需注明: http://www.cnblogs.com/sciencefans/
看了下人脸识别函数的opencv的源码
\sources\modules\objdetect\src\cascadedetect.cpp
中detectMultiScale有两个重载,第二个重载在opencv的开发文档里居然只字未提:
void CascadeClassifier::detectMultiScale( const Mat& image, vector<Rect>& objects, vector < int >& rejectLevels, vector < double >& levelWeights, double scaleFactor, int minNeighbors, int flags, Size minObjectSize, Size maxObjectSize, bool outputRejectLevels )
发现他有个rejectLevels和levelWeight这两个引用参数,看名字感觉是一种得分输出。
google了一下发现国外问的人不少但是基本没啥解释(或者是我没认真找?)
然后看了下它调用的cvHaarDetectObjectsForROC()的源码实现,大概懂了这俩vectors是在干什么的。
先上结论:确实和人脸得分有关。
首先应该明白一点detectMultiScale()这个方法是一个级联分类器,使用了boosting的方法。所以输入图像要经过层层(级级)选拔,留到最后的才是真汉子(正样本)
rejectLevels就是代表在第几层被out的。如果是最后一层(在lbpcascade_frontalface.xml中是20,具体要看xml中的叙述)被out,则说明很可能是正样本。
为啥说很可能呢?
因为还有个参数:levelWeight。即使是在最后一层被out的,levelWeight很小甚至是负数,也可以看成是负样本。
实际上很多负样本正是在最后一层被out的。
见下图:
我这里只截取了level在20才out的框。输出了他们的levelWeight。是脸的地方最大是4.23多,其他的就很小。不用过多解释了吧~
所以这个函数的原理是这样的(个人理解,有错误请指教):
首先一个level一个level地测试样本,然后每一个level给一个对应的得分,也就是levelWeight,如果这个weight低于或者高于对应level的threshold,则被抛弃。
坚持到最后一个level并且在最后一个level仍然满足threshold的框就是正确的脸(正样本)。
所以,人脸的分应该是这样:level越大,分数越高,在相同的level,levelWeight越大分数越高。
但是实际上真正的人脸都是能坚持到level20(最后一个level)的,所以只比对最后一个level的所有大于1的框的levelWeight进行比对就可以知道脸的得分啦~
这里给出所有level被gg的框的图:
最后给出灰常短小精悍的demo的源代码:
1 #include <opencv2\opencv.hpp> 2 #include <iostream> 3 #include <vector> 4 #include <fstream> 5 #include <math.h> 6 using namespace std; 7 using namespace cv; 8 const string xmlpath = " lbpcascade_frontalface.xml " ; 9 CascadeClassifier face_cc; 10 11 int tic = 0 ; 12 13 void detect(Mat img){ 14 vector<Rect> faces; 15 vector< int > rejLevel; 16 vector< double > levelW; 17 Mat grayimg; 18 cvtColor(img, grayimg, CV_RGB2GRAY); 19 equalizeHist(grayimg, grayimg); 20 int minl = min(img.rows, img.cols); 21 face_cc.detectMultiScale(grayimg, faces, rejLevel, levelW, 1.1 , 3 , 0 , Size(), Size(), true ); 22 // face_cc.detectMultiScale(grayimg, faces, 1.1); 23 for ( int i = 0 ; i < faces.size(); i++ ) 24 { 25 if ( rejLevel[i] < 00 ) 26 { 27 continue ; 28 } 29 stringstream text1, text2; 30 text1 << " rejLevel: " << rejLevel[ i ]; 31 text2 << " levelW: " << levelW[ i ]; 32 string ttt = text1.str(); 33 rectangle(img, faces[ i ], Scalar( 255 , 255 , 0 ), 2 , 8 , 0 ); 34 putText(img, ttt, cvPoint(faces[ i ].x, faces[ i ].y - 3 ), 1 , 1 , Scalar( 0 , 255 , 255 )); 35 ttt = text2.str(); 36 putText(img, ttt, cvPoint(faces[ i ].x, faces[ i ].y + 12 ), 1 , 1 , Scalar( 255 , 0 , 255 )); 37 } 38 imshow( " IMG " , img); 39 waitKey( 0 ); 40 } 41 42 int main(){ 43 if ( ! face_cc.load(xmlpath) ) 44 { 45 cout << " load error!\n " ; 46 return - 1 ; 47 } 48 ifstream pathin; 49 pathin.open( " imgpath.txt " ); 50 string t; 51 while ( pathin >> t && tic < 10000 ) 52 { 53 Mat img = imread(t); 54 detect(img); 55 } 56 pathin.close(); 57 return 0 ; 58 }