前段时间使用OpenCV的库函数实现了人脸检测和人脸识别,笔者的实验环境为VS+OpenCV2.4.4,OpenCV的环境配置网上有很多,不再赘述。检测的代码网上很多,记不清楚从哪儿copy的了,识别的代码是从OpenCV官网上找到的:/trunk/modules/contrib/doc/facerec/facerec_api.html
需要注意的是,opencv的FaceRecogizer目前有三个类实现了它,特征脸和fisherface方法最少训练图像为两张,而LBP可以单张图像训练。本人的实验采用的图片是100x100大小的,所以如果要添加自己的图像进行识别的话务必调整为100x100,不然会报错。当然在recog_and_draw这个函数里,笔者也将每次检测到的人脸进行了保存,拖出来重命名就可以,路径自己找吧。使用不同的方法识别时,其阈值设置也不同,LBP大概在100,其他两种方法大概在1000。本人的代码已共享,下载链接:/detail/u010944555/6749725
ps:有人说代码的检测率不高,其实可以归结为两方面的原因,第一人脸检测率不高,这个可以通过嵌套检测嘴角、眼睛等来降低,或者背景、光照固定的话可以通过图像差分来解决;第二是识别方法本身的问题,如果想提高识别率,可以添加多张不同姿态、光照下的人脸作为训练的样本,如果有时间的话可以在采集图像时给出一个人脸框,引导用户对齐人脸进行采集,三星手机解除锁屏就有这么一个功能。
效果图:
废话不多说,上传代码。
main:
[cpp]view plain copy#include"stdafx.h" #include"cv.h" #include"highgui.h" #include<stdio.h> #include<stdlib.h> #include<string.h> #include<assert.h> #include<math.h> #include<float.h> #include<limits.h> #include<time.h> #include<ctype.h> #include<opencv2\contrib\contrib.hpp> #include<opencv2\core\core.hpp> #include<opencv2\highgui\highgui.hpp> #include<iostream> #include<fstream> #include<sstream> #include"detect_recog.h" usingnamespacestd; usingnamespacecv; #ifdef_EiC #defineWIN32 #endif CvMemStorage*storage=0; CvHaarClassifierCascade*cascade=0; CvHaarClassifierCascade*nested_cascade=0; intuse_nested_cascade=0; constchar*cascade_name= "./data/haarcascade_frontalface_alt.xml";//别人已经训练好的人脸检测xml数据 constchar*nested_cascade_name= "./data/haarcascade_eye_tree_eyeglasses.xml"; CvCapture*capture=0; IplImage*frame,*frame_copy=0; IplImage*image=0; constchar*scale_opt="--scale=";//分类器选项指示符号 intscale_opt_len=(int)strlen(scale_opt); constchar*cascade_opt="--cascade="; intcascade_opt_len=(int)strlen(cascade_opt); constchar*nested_cascade_opt="--nested-cascade"; intnested_cascade_opt_len=(int)strlen(nested_cascade_opt); doublescale=1; intnum_components=9; doublefacethreshold=9.0; //opencv的FaceRecogizer目前有三个类实现了他,特征脸和fisherface方法最少训练图像为两张,而LBP可以单张图像训练 //cv::Ptr<cv::FaceRecognizer>model=cv::createEigenFaceRecognizer(); //cv::Ptr<cv::FaceRecognizer>model=cv::createFisherFaceRecognizer(); cv::Ptr<cv::FaceRecognizer>model=cv::createLBPHFaceRecognizer();//LBP的这个方法在单个人脸验证方面效果最好 vector<Mat>images;//两个容器images,labels来存放图像数据和对应的标签 vector<int>labels; intmain(intargc,char**argv) { cascade=(CvHaarClassifierCascade*)cvLoad(cascade_name,0,0,0);//加载分类器 if(!cascade) { fprintf(stderr,"ERROR:Couldnotloadclassifiercascade\n"); getchar(); return-1; } model->set("threshold",2100.0); stringoutput_folder; output_folder=string("./einfacedata"); //读取你的CSV文件路径 stringfn_csv=string("./einfacedata/at.txt"); try { //通过./einfacedata/at.txt这个文件读取里面的训练图像和类别标签 read_csv(fn_csv,images,labels); } catch(cv::Exception&e) { cerr<<"Erroropeningfile"<<fn_csv<<".Reason:"<<e.msg<<endl; exit(1); } /* //read_img这个函数直接从einfacedata/trainingdata目录下读取图像数据并默认将图像置为0 //所以如果用这个函数只能用来单个人脸验证 if(!read_img(images,labels)) {cout<<"Errorinreadingimages!"; images.clear(); labels.clear(); return0; } */ cout<<images.size()<<":"<<labels.size()<<endl; //如果没有读到足够的图片,就退出 if(images.size()<=2) { stringerror_message="Thisdemoneedsatleast2imagestowork."; CV_Error(CV_StsError,error_message); } //得到第一张照片的高度,在下面对图像变形到他们原始大小时需要 //intheight=images[0].rows; //移除最后一张图片,用于做测试 //MattestSample=images[images.size()-1]; //cv::imshow("testSample",testSample); //inttestLabel=labels[labels.size()-1]; //images.pop_back(); //labels.pop_back(); //下面创建一个特征脸模型用于人脸识别, //通过CSV文件读取的图像和标签训练它。 //进行训练 model->train(images,labels); storage=cvCreateMemStorage(0);//创建内存存储器 capture=cvCaptureFromCAM(0);//创建视频读取结构 cvNamedWindow("result",1); if(capture)//如过是视频或摄像头采集图像,则循环处理每一帧 { for(;;) { if(!cvGrabFrame(capture)) break; frame=cvRetrieveFrame(capture); if(!frame) break; if(!frame_copy) frame_copy=cvCreateImage(cvSize(640,480),IPL_DEPTH_8U,frame->nChannels); if(frame->origin==IPL_ORIGIN_TL) cvCopy(frame,frame_copy,0); else cvFlip(frame,frame_copy,0); //detect_and_draw(frame_copy);//如果调用这个函数,只是实现人脸检测 //cout<<frame_copy->width<<"x"<<frame_copy->height<<endl; recog_and_draw(frame_copy);//该函数实现人脸检测和识别 if(cvWaitKey(100)>=0)//esc键值好像是100 goto_cleanup_; } cvWaitKey(0); _cleanup_://标记使用,在汇编里用过,C语言,我还没见用过 cvReleaseImage(&frame_copy); cvReleaseCapture(&capture); } cvDestroyWindow("result"); return0; }
detect_recog.cpp:
[cpp]view plain copy#include"stdafx.h" #include"cv.h" #include"highgui.h" #include<stdio.h> #include<stdlib.h> #include<string.h> #include<assert.h> #include<math.h> #include<float.h> #include<limits.h> #include<time.h> #include<ctype.h> #include"detect_recog.h" #include<opencv2\contrib\contrib.hpp> #include<opencv2\core\core.hpp> #include<opencv2\highgui\highgui.hpp> #include<iostream> #include<fstream> #include<sstream> #include<stdio.h> #include<io.h> #include<direct.h> usingnamespacestd; usingnamespacecv; //检测并圈出人脸,并将检测到的人脸进行判断属于训练图像中的哪一类 voidrecog_and_draw(IplImage*img) { staticCvScalarcolors[]= { {{0,0,255}}, {{0,128,255}}, {{0,255,255}}, {{0,255,0}}, {{255,128,0}}, {{255,255,0}}, {{255,0,0}}, {{255,0,255}} }; IplImage*gray,*small_img; inti,j; gray=cvCreateImage(cvSize(img->width,img->height),8,1); small_img=cvCreateImage(cvSize(cvRound(img->width/scale), cvRound(img->height/scale)),8,1); cvCvtColor(img,gray,CV_BGR2GRAY);//彩色RGB图像转为灰度图像 cvResize(gray,small_img,CV_INTER_LINEAR); cvEqualizeHist(small_img,small_img);//直方图均衡化 cvClearMemStorage(storage); if(cascade) { doublet=(double)cvGetTickCount(); CvSeq*faces=cvHaarDetectObjects(small_img,cascade,storage, 1.1,2,0 //|CV_HAAR_FIND_BIGGEST_OBJECT //|CV_HAAR_DO_ROUGH_SEARCH |CV_HAAR_DO_CANNY_PRUNING //|CV_HAAR_SCALE_IMAGE , cvSize(30,30)); t=(double)cvGetTickCount()-t;//统计检测使用时间 //printf("detectiontime=%gms\n",t/((double)cvGetTickFrequency()*1000.)); for(i=0;i<(faces?faces->total:0);i++) { CvRect*r=(CvRect*)cvGetSeqElem(faces,i);//将faces数据从CvSeq转为CvRect CvMatsmall_img_roi; CvSeq*nested_objects; CvPointcenter; CvScalarcolor=colors[i%8];//使用不同颜色绘制各个face,共八种色 intradius; center.x=cvRound((r->x+r->width*0.5)*scale);//找出faces中心 center.y=cvRound((r->y+r->height*0.5)*scale); radius=cvRound((r->width+r->height)*0.25*scale); cvGetSubRect(small_img,&small_img_roi,*r); //截取检测到的人脸区域作为识别的图像 IplImage*result; CvRectroi; roi=*r; result=cvCreateImage(cvSize(r->width,r->height),img->depth,img->nChannels); cvSetImageROI(img,roi); //创建子图像 cvCopy(img,result); cvResetImageROI(img); IplImage*resizeRes; CvSizedst_cvsize; dst_cvsize.width=(int)(100); dst_cvsize.height=(int)(100); resizeRes=cvCreateImage(dst_cvsize,result->depth,result->nChannels); //检测到的区域可能不是100x100大小,所以需要插值处理到统一大小,图像的大小可以自己指定的 cvResize(result,resizeRes,CV_INTER_NN); IplImage*img1=cvCreateImage(cvGetSize(resizeRes),IPL_DEPTH_8U,1);//创建目标图像 cvCvtColor(resizeRes,img1,CV_BGR2GRAY);//cvCvtColor(src,des,CV_BGR2GRAY) cvShowImage("resize",resizeRes); cvCircle(img,center,radius,color,3,8,0);//从中心位置画圆,圈出脸部区域 intpredictedLabel=-1; Mattest=img1; //images[images.size()-1]=test; model->train(images,labels); //如果调用read_img函数时chdir将默认目录做了更改,所以output.jpg自己找一下吧 imwrite("../ouput.jpg",test); //在这里对人脸进行判别 doublepredicted_confidence=0.0; model->predict(test,predictedLabel,predicted_confidence); if(predictedLabel==0) cvText(img,"yes",r->x+r->width*0.5,r->y); else cvText(img,"no",r->x+r->width*0.5,r->y); //cout<<"predict:"<<model->predict(test)<<endl; cout<<"predict:"<<predictedLabel<<"\nconfidence:"<<predicted_confidence<<endl; if(!nested_cascade) continue; nested_objects=cvHaarDetectObjects(&small_img_roi,nested_cascade,storage, 1.1,2,0 //|CV_HAAR_FIND_BIGGEST_OBJECT //|CV_HAAR_DO_ROUGH_SEARCH //|CV_HAAR_DO_CANNY_PRUNING //|CV_HAAR_SCALE_IMAGE , cvSize(0,0)); for(j=0;j<(nested_objects?nested_objects->total:0);j++) { CvRect*nr=(CvRect*)cvGetSeqElem(nested_objects,j); center.x=cvRound((r->x+nr->x+nr->width*0.5)*scale); center.y=cvRound((r->y+nr->y+nr->height*0.5)*scale); radius=cvRound((nr->width+nr->height)*0.25*scale); cvCircle(img,center,radius,color,3,8,0); } } } cvShowImage("result",img); cvReleaseImage(&gray); cvReleaseImage(&small_img); } voidcvText(IplImage*img,constchar*text,intx,inty) { CvFontfont; doublehscale=1.0; doublevscale=1.0; intlinewidth=2; cvInitFont(&font,CV_FONT_HERSHEY_SIMPLEX|CV_FONT_ITALIC,hscale,vscale,0,linewidth); CvScalartextColor=cvScalar(0,255,255); CvPointtextPos=cvPoint(x,y); cvPutText(img,text,textPos,&font,textColor); } Matnorm_0_255(cv::InputArray_src) { Matsrc=_src.getMat(); Matdst; switch(src.channels()) { case1: cv::normalize(_src,dst,0,255,cv::NORM_MINMAX,CV_8UC1); break; case3: cv::normalize(_src,dst,0,255,cv::NORM_MINMAX,CV_8UC3); break; default: src.copyTo(dst); break; } returndst; } //读取文件中的图像数据和类别,存入images和labels这两个容器 voidread_csv(conststring&filename,vector<Mat>&images,vector<int>&labels,charseparator) { std::ifstreamfile(filename.c_str(),ifstream::in); if(!file) { stringerror_message="Novalidinputfilewasgiven."; CV_Error(CV_StsBadArg,error_message); } stringline,path,classlabel; while(getline(file,line)) { stringstreamliness(line); getline(liness,path,separator);//遇到分号就结束 getline(liness,classlabel);//继续从分号后面开始,遇到换行结束 if(!path.empty()&&!classlabel.empty()) { images.push_back(imread(path,0)); labels.push_back(atoi(classlabel.c_str())); } } } boolread_img(vector<Mat>&images,vector<int>&labels) { longfile; struct_finddata_tfind; _chdir("./einfacedata/trainingdata/"); if((file=_findfirst("*.*",&find))==-1L){ //printf("空白!/n"); returnfalse; } //fileNum=0; //strcpy(fileName[fileNum],find.name); inti=0; while(_findnext(file,&find)==0) { if(i==0) { i++; continue; } images.push_back(imread(find.name,0)); labels.push_back(0); cout<<find.name<<endl; } _findclose(file); returntrue; } //只是检测人脸,并将人脸圈出 voiddetect_and_draw(IplImage*img) { staticCvScalarcolors[]= { {{0,0,255}}, {{0,128,255}}, {{0,255,255}}, {{0,255,0}}, {{255,128,0}}, {{255,255,0}}, {{255,0,0}}, {{255,0,255}} }; IplImage*gray,*small_img; inti,j; gray=cvCreateImage(cvSize(img->width,img->height),8,1); small_img=cvCreateImage(cvSize(cvRound(img->width/scale), cvRound(img->height/scale)),8,1); cvCvtColor(img,gray,CV_BGR2GRAY);//彩色RGB图像转为灰度图像 cvResize(gray,small_img,CV_INTER_LINEAR); cvEqualizeHist(small_img,small_img);//直方图均衡化 cvClearMemStorage(storage); if(cascade) { doublet=(double)cvGetTickCount(); CvSeq*faces=cvHaarDetectObjects(small_img,cascade,storage, 1.1,2,0 //|CV_HAAR_FIND_BIGGEST_OBJECT //|CV_HAAR_DO_ROUGH_SEARCH |CV_HAAR_DO_CANNY_PRUNING //|CV_HAAR_SCALE_IMAGE , cvSize(30,30)); t=(double)cvGetTickCount()-t;//统计检测使用时间 printf("detectiontime=%gms\n",t/((double)cvGetTickFrequency()*1000.)); for(i=0;i<(faces?faces->total:0);i++) { CvRect*r=(CvRect*)cvGetSeqElem(faces,i);//将faces数据从CvSeq转为CvRect CvMatsmall_img_roi; CvSeq*nested_objects; CvPointcenter; CvScalarcolor=colors[i%8];//使用不同颜色绘制各个face,共八种色 intradius; center.x=cvRound((r->x+r->width*0.5)*scale);//找出faces中心 center.y=cvRound((r->y+r->height*0.5)*scale); radius=cvRound((r->width+r->height)*0.25*scale); cvCircle(img,center,radius,color,3,8,0);//从中心位置画圆,圈出脸部区域 if(!nested_cascade) continue; cvGetSubRect(small_img,&small_img_roi,*r); nested_objects=cvHaarDetectObjects(&small_img_roi,nested_cascade,storage, 1.1,2,0 //|CV_HAAR_FIND_BIGGEST_OBJECT //|CV_HAAR_DO_ROUGH_SEARCH //|CV_HAAR_DO_CANNY_PRUNING //|CV_HAAR_SCALE_IMAGE ,cvSize(0,0)); for(j=0;j<(nested_objects?nested_objects->total:0);j++) { CvRect*nr=(CvRect*)cvGetSeqElem(nested_objects,j); center.x=cvRound((r->x+nr->x+nr->width*0.5)*scale); center.y=cvRound((r->y+nr->y+nr->height*0.5)*scale); radius=cvRound((nr->width+nr->height)*0.25*scale); cvCircle(img,center,radius,color,3,8,0); } } } cvShowImage("result",img); cvReleaseImage(&gray); cvReleaseImage(&small_img); }
detect_recog.h:
[cpp]view plain copy#include"stdafx.h" #include"cv.h" #include"highgui.h" #include<stdio.h> #include<stdlib.h> #include<string.h> #include<assert.h> #include<math.h> #include<float.h> #include<limits.h> #include<time.h> #include<ctype.h> //s/// #include<opencv2\contrib\contrib.hpp> #include<opencv2\core\core.hpp> #include<opencv2\highgui\highgui.hpp> #include<iostream> #include<fstream> #include<sstream> usingnamespacestd; usingnamespacecv; #ifndefDETECT_RECOG_H #defineDETECT_RECOG_H externCvMemStorage*storage; externCvHaarClassifierCascade*cascade; externCvHaarClassifierCascade*nested_cascade; externintuse_nested_cascade; externconstchar*cascade_name; externconstchar*nested_cascade_name; externdoublescale; externcv::Ptr<cv::FaceRecognizer>model; externvector<Mat>images; externvector<int>labels; voiddetect_and_draw(IplImage*img);//检测和绘画 voidrecog_and_draw(IplImage*img);//检测和绘画 voidread_csv(conststring&filename,vector<Mat>&images,vector<int>&labels,charseparator=';'); boolread_img(vector<Mat>&images,vector<int>&labels); Matnorm_0_255(cv::InputArray_src); voidcvText(IplImage*img,constchar*text,intx,inty); #endif