基于opencv的人脸识别:分别在1.0和2.4.3版本下的对比

时间:2024-03-09 13:22:14

先贴出一段代码,这是opencv1.0版本给出的sample,之前本人在vc6.0+opencv1.0的条件下做过实验,完全成功的。识别时间在50ms左右。

View Code
  1 #include "stdafx.h"
  2 #include "cv.h"
  3 #include "highgui.h"
  4 using namespace std;
  5 using namespace cv;
  6 static CvMemStorage* storage = 0;
  7 static CvHaarClassifierCascade* cascade = 0;
  8 
  9 void detect_and_draw( IplImage* image );
 10 
 11 const char* cascade_name =
 12     "E:\\CV\\src\\haarcascade_frontalface_alt.xml";
 13 
 14 int main( int argc, char** argv )
 15 {
 16     CvCapture* capture = 0;
 17     IplImage *frame, *frame_copy = 0;
 18     int optlen = strlen("--cascade=");
 19     const char* input_name;
 20 
 21     if( argc > 1 && strncmp( argv[1], "--cascade=", optlen ) == 0 )
 22     {
 23         cascade_name = argv[1] + optlen;
 24         input_name = argc > 2 ? argv[2] : 0;
 25     }
 26     else
 27     {
 28         cascade_name = "E:\\CV\\src\\haarcascade_frontalface_alt.xml";
 29         input_name = argc > 1 ? argv[1] : 0;
 30     }
 31 
 32     cascade = (CvHaarClassifierCascade*)cvLoad( cascade_name, 0, 0, 0 );
 33     
 34     if( !cascade )
 35     {
 36        cout<<"load cascade file error"<<endl;
 37         system("pause");
 38         return -1;
 39     }
 40     storage = cvCreateMemStorage(0);
 41     
 42     if( !input_name || (isdigit(input_name[0]) && input_name[1] == \'\0\') )
 43         capture = cvCaptureFromCAM( !input_name ? 0 : input_name[0] - \'0\' );
 44     else
 45         capture = cvCaptureFromAVI( input_name ); 
 46 
 47     cvNamedWindow( "result", 1 );
 48 
 49     if( capture )
 50     {
 51         for(;;)
 52         {
 53             if( !cvGrabFrame( capture ))
 54                 break;
 55             frame = cvRetrieveFrame( capture );
 56             if( !frame )
 57                 break;
 58             if( !frame_copy )
 59                 frame_copy = cvCreateImage( cvSize(frame->width,frame->height),
 60                                             IPL_DEPTH_8U, frame->nChannels );
 61             if( frame->origin == IPL_ORIGIN_TL )
 62                 cvCopy( frame, frame_copy, 0 );
 63             else
 64                 cvFlip( frame, frame_copy, 0 );
 65             
 66             detect_and_draw( frame_copy );
 67 
 68             if( cvWaitKey( 10 ) >= 0 )
 69                 break;
 70         }
 71 
 72         cvReleaseImage( &frame_copy );
 73         cvReleaseCapture( &capture );
 74     }
 75     else
 76     {
 77         const char* filename = input_name ? input_name : (char*)"lena.jpg";
 78         IplImage* image = cvLoadImage( filename, 1 );
 79 
 80         if( image )
 81         {
 82             detect_and_draw( image );
 83             cvWaitKey(0);
 84             cvReleaseImage( &image );
 85         }
 86         else
 87         {
 88             /* assume it is a text file containing the
 89                list of the image filenames to be processed - one per line */
 90             FILE* f = fopen( filename, "rt" );
 91             if( f )
 92             {
 93                 char buf[1000+1];
 94                 while( fgets( buf, 1000, f ) )
 95                 {
 96                     int len = (int)strlen(buf);
 97                     while( len > 0 && isspace(buf[len-1]) )
 98                         len--;
 99                     buf[len] = \'\0\';
100                     image = cvLoadImage( buf, 1 );
101                     if( image )
102                     {
103                         detect_and_draw( image );
104                         cvWaitKey(0);
105                         cvReleaseImage( &image );
106                     }
107                 }
108                 fclose(f);
109             }
110         }
111 
112     }
113     
114     cvDestroyWindow("result");
115     system("pause");
116     return 0;
117 }
118 
119 void detect_and_draw( IplImage* img )
120 {
121     static CvScalar colors[] = 
122     {
123         {{0,0,255}},
124         {{0,128,255}},
125         {{0,255,255}},
126         {{0,255,0}},
127         {{255,128,0}},
128         {{255,255,0}},
129         {{255,0,0}},
130         {{255,0,255}}
131     };
132 
133     double scale = 1.3;
134     IplImage* gray = cvCreateImage( cvSize(img->width,img->height), 8, 1 );
135     IplImage* small_img = cvCreateImage( cvSize( cvRound (img->width/scale),
136                          cvRound (img->height/scale)),
137                      8, 1 );
138     int i;
139 
140     cvCvtColor( img, gray, CV_BGR2GRAY );
141     cvResize( gray, small_img, CV_INTER_LINEAR );
142     cvEqualizeHist( small_img, small_img );
143     cvClearMemStorage( storage );
144 
145     if( cascade )
146     {
147         double t = (double)cvGetTickCount();
148         CvSeq* faces = cvHaarDetectObjects( small_img, cascade, storage,
149                                             1.1, 2, 0/*CV_HAAR_DO_CANNY_PRUNING*/,
150                                             cvSize(30, 30) );
151         t = (double)cvGetTickCount() - t;
152         printf( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) );
153         for( i = 0; i < (faces ? faces->total : 0); i++ )
154         {
155             CvRect* r = (CvRect*)cvGetSeqElem( faces, i );
156             CvPoint center;
157             int radius;
158             center.x = cvRound((r->x + r->width*0.5)*scale);
159             center.y = cvRound((r->y + r->height*0.5)*scale);
160             radius = cvRound((r->width + r->height)*0.25*scale);
161             cvCircle( img, center, radius, colors[i%8], 3, 8, 0 );
162         }
163     }
164 
165     cvShowImage( "result", img );
166     cvReleaseImage( &gray );
167     cvReleaseImage( &small_img );
168 }

 

  但是在opencv1.0版本有个很烦的问题,就是摄像头不兼容,不能读取摄像头的图像,窗口是灰的。之前本人用的thinkpad的笔记本,ok的,但是现在换了三星的笔记本,就出现摄像头不兼容的问题,而且据wo在论坛上看的情况,opencv1.0版本摄像头不兼容的情况很普遍。
  为了继续探究,于是我换了最高版本opencv2.4.3,解压后有足足3G,感觉异常臃肿啊,之前的opencv1.0版本才几十兆,在opencv2.4.3版本中出现了CascadeClassifier类用于实现级联分类器识别功能。于是我用新版本和新方法去做人脸识别,同样是基于haar特征的级联分类器方法,代码如下:

主文件main.c:

View Code
 1 // 2013/04/02 copyright cezorzhao
 2 //real-time effect is very unuseable 
 3 //next , i will use pca to de_demontion the pic to update the code
 4 
 5 #include "stdafx.h"
 6 #include"functions.h"
 7 #include "opencv2\opencv.hpp" 
 8 #include "opencv2/objdetect/objdetect.hpp"
 9 #include "opencv2/highgui/highgui.hpp"
10 #include "opencv2/imgproc/imgproc.hpp"
11 #include <iostream>
12 using namespace std;
13 using namespace cv;
14 string face_cascade_name0 = "E:\\CV\\src\\haarcascade_frontalface_alt.xml";
15 string face_cascade_name1 = "E:\\CV\\src\\haarcascade_frontalface_alt2.xml";
16 string face_cascade_name2 = "E:\\CV\\src\\haarcascade_eye_tree_eyeglasses.xml";
17 string face_cascade_name3 = "E:\\CV\\src\\lbpcascade_frontalface.xml";
18 CascadeClassifier face_cascade;
19 extern void detectAndDisplay( Mat frame );
20 TickMeter tm;
21 int main(  )
22 {
23     if( !face_cascade.load( face_cascade_name3 ) )
24     { 
25         printf("[error] 无法加载级联分类器文件!\n");
26         return -1; 
27     }
28     VideoCapture cap(0);
29     if( !cap.isOpened() )
30     {
31         cout<<"cap closed !";
32         return -1;
33     }
34     while(1)
35     {
36         Mat frame;
37         cap>>frame;
38         detectAndDisplay(frame , face_cascade);
39         cvWaitKey(1);
40     }
41     waitKey(0);  
42 }

function.h:

View Code
 1 #include "stdafx.h"
 2 #include "opencv2/opencv.hpp" 
 3 #include "opencv2/objdetect/objdetect.hpp"
 4 #include "opencv2/highgui/highgui.hpp"
 5 #include "opencv2/imgproc/imgproc.hpp"
 6 #include "iostream"
 7 using namespace std;
 8 using namespace cv;
 9 
10 void detectAndDisplay( Mat frame , CascadeClassifier face_cascade )
11 {
12     double scale=2.4;
13     extern TickMeter tm;
14     tm.reset();
15     std::vector<Rect> faces;
16     Mat frame_gray;
17     tm.start();
18     cvtColor( frame, frame_gray, CV_BGR2GRAY );
19     tm.stop();
20     cout<<"color convert time:"<<tm.getTimeMilli()<<"ms"<<endl;
21     tm.reset();
22     tm.start();
23     equalizeHist( frame_gray, frame_gray );
24     tm.stop();
25     cout<<"Hist time :"<<tm.getTimeMilli()<<"ms"<<endl;
26     tm.reset();
27     Mat small;
28     resize(frame_gray,small,Size(frame.cols/scale,frame.rows/scale));
29     tm.start();
30     face_cascade.detectMultiScale( small , faces, 1.1, 2, 0|CV_HAAR_SCALE_IMAGE, Size(30/scale, 30/scale) );
31     tm.stop();
32     cout<<"detect time"<<tm.getTimeMilli()<<"ms"<<endl;
33     for( int i = 0; i < faces.size(); i++ )
34     {
35         Point center( faces[i].x *scale+ faces[i].width*0.5, faces[i].y*scale + faces[i].height*0.5 );
36         ellipse( frame , center, Size( faces[i].width*0.5*scale, faces[i].height*0.5*scale), 0, 0, 360, Scalar( 255, 0, 255 ), 4, 8,  0);
37     }
38     imshow( "small", small );
39     imshow( "face_detect", frame );
40 
41 }

  能够实现人脸的识别,但是有个严重的问题,那就是耗时太长,一张图片基本要1.8秒,完全达不到视频处理实时性的要求。即使用了金字塔降维之后,耗时依然十分严重。

  然后我觉得可能是新方法本身的算法本身的问题,于是用之前那段代码,也就是opencv1.0中的方法,在2.4.3下改了改,结果运行起来发现还是很慢,检测时间还是1秒多。也就是说,检测耗时长这个问题不是新的方法本身的问题,很可能是2.4.3版本结构上有不妥之处。

      我在网上查了点资料,新的CascadeClassifier类不仅支持读入haar特征的训练文件(也就是.xml文件),而且支持lbp特征的训练文件(hog也支持)。于是在opncv安装目录下寻找,果然发现opencv/data目录下有个“lbpcascades”文件夹,里面装有lbp特征的训练文件,而opencv1.0版本的data目录系是仅有haar特征的训练文件的。然后我把代码中的训练文件改为lbp特征的训练文件,发现检测速度可以达到50ms每张,基本能满足实时性了。

  

  以上就是在使用opencv的过程中的一点经验和心得,希望有更多比较精通cv理论的园友一起讨论交流,2.4.3版本的haar特征检测耗时问题也是一直迷惑着我,希望有高人能够点拨。