本文给大家分享一下使用opencv2.4.3版本和1.0版本下集中不同特征和方法人脸检测实现过程中的一些方法和经验,并附上完整代码。同时也有一些未能解决的问题在此一并提出。
先贴出一段代码,这是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特征检测耗时问题也是一直迷惑着我,希望有高人能够点拨。