先贴出一段代码,这是opencv1.0版本给出的sample,之前本人在vc6.0+opencv1.0的条件下做过实验,完全成功的。识别时间在50ms左右。
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:
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:
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特征检测耗时问题也是一直迷惑着我,希望有高人能够点拨。