`
jgsj
  • 浏览: 960759 次
文章分类
社区版块
存档分类
最新评论

基于OpenCV读取摄像头进行人脸检测和人脸识别

 
阅读更多

前段时间使用OpenCV的库函数实现了人脸检测和人脸识别,笔者的实验环境为VS2010+OpenCV2.4.4,OpenCV的环境配置网上有很多,不再赘述。检测的代码网上很多,记不清楚从哪儿copy的了,识别的代码是从OpenCV官网上找到的:http://docs.opencv.org/trunk/modules/contrib/doc/facerec/facerec_api.html

需要注意的是,opencv的FaceRecogizer目前有三个类实现了它,特征脸和fisherface方法最少训练图像为两张,而LBP可以单张图像训练。本人的实验采用的图片是100x100大小的,所以如果要添加自己的图像进行识别的话务必调整为100x100,不然会报错。当然在recog_and_draw这个函数里,笔者也将每次检测到的人脸进行了保存,拖出来重命名就可以,路径自己找吧。使用不同的方法识别时,其阈值设置也不同,LBP大概在100,其他两种方法大概在1000。本人的代码已共享,下载链接:http://download.csdn.net/detail/u010944555/6749725

ps:有人说代码的检测率不高,其实可以归结为两方面的原因,第一人脸检测率不高,这个可以通过嵌套检测嘴角、眼睛等来降低,或者背景、光照固定的话可以通过图像差分来解决;第二是识别方法本身的问题,如果想提高识别率,可以添加多张不同姿态、光照下的人脸作为训练的样本,如果有时间的话可以在采集图像时给出一个人脸框,引导用户对齐人脸进行采集,三星手机解除锁屏就有这么一个功能。

效果图:

废话不多说,上传代码。

main:

#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"

using namespace std;
using namespace cv;
#ifdef _EiC
#define WIN32
#endif

CvMemStorage* storage = 0;
CvHaarClassifierCascade* cascade = 0;
CvHaarClassifierCascade* nested_cascade = 0;
int use_nested_cascade = 0;
const char* cascade_name =
    "./data/haarcascade_frontalface_alt.xml";//别人已经训练好的人脸检测xml数据
const char* nested_cascade_name =
    "./data/haarcascade_eye_tree_eyeglasses.xml";
CvCapture* capture = 0;
IplImage *frame, *frame_copy = 0;
IplImage *image = 0;
const char* scale_opt = "--scale="; // 分类器选项指示符号 
int scale_opt_len = (int)strlen(scale_opt);
const char* cascade_opt = "--cascade=";
int cascade_opt_len = (int)strlen(cascade_opt);
const char* nested_cascade_opt = "--nested-cascade";
int nested_cascade_opt_len = (int)strlen(nested_cascade_opt);
double scale = 1;
int num_components = 9;
double facethreshold = 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;


int main( int argc, char** argv )
{
	cascade = (CvHaarClassifierCascade*)cvLoad(cascade_name, 0, 0, 0); //加载分类器 
    if(!cascade) 
    {
        fprintf( stderr, "ERROR: Could not load classifier cascade\n" );
		getchar();
        return -1;
    }
	model->set("threshold", 2100.0);
	string output_folder;
	output_folder = string("./einfacedata");

	//读取你的CSV文件路径
	string fn_csv = string("./einfacedata/at.txt");
	try
	{
		//通过./einfacedata/at.txt这个文件读取里面的训练图像和类别标签
		read_csv(fn_csv, images, labels);	
	}
	catch(cv::Exception &e)
	{
		cerr<<"Error opening file "<<fn_csv<<". Reason: "<<e.msg<<endl;
		exit(1);
	}
	/*
	//read_img这个函数直接从einfacedata/trainingdata目录下读取图像数据并默认将图像置为0
	//所以如果用这个函数只能用来单个人脸验证
	if(!read_img(images, labels))
	{
		cout<< "Error in reading images!";
		images.clear();
		labels.clear();
		return 0;
	}
	*/
	cout << images.size() << ":" << labels.size()<<endl;
	//如果没有读到足够的图片,就退出
	if(images.size() <= 2)
	{
		string error_message = "This demo needs at least 2 images to work.";
		CV_Error(CV_StsError, error_message);
	}

	//得到第一张照片的高度,在下面对图像变形到他们原始大小时需要
	//int height = images[0].rows;
	//移除最后一张图片,用于做测试
	//Mat testSample = images[images.size() - 1];
	//cv::imshow("testSample", testSample);
	//int testLabel = 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");
    return 0;
}

detect_recog.cpp:

#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> 

using namespace std;
using namespace cv;

//检测并圈出人脸,并将检测到的人脸进行判断属于训练图像中的哪一类
void recog_and_draw( IplImage* img ) 
{
    static CvScalar colors[] = 
    {
        {{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;
    int i, 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 )
    {
        double t = (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( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) );
        for( i = 0; i < (faces ? faces->total : 0); i++ )
        {
            CvRect* r = (CvRect*)cvGetSeqElem( faces, i ); // 将faces数据从CvSeq转为CvRect 
            CvMat small_img_roi;
            CvSeq* nested_objects;
            CvPoint center;
            CvScalar color = colors[i%8]; // 使用不同颜色绘制各个face,共八种色 
            int radius;
            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;
			CvRect roi;
			roi = *r;
			result = cvCreateImage( cvSize(r->width, r->height), img->depth, img->nChannels );
			cvSetImageROI(img,roi);
			// 创建子图像
			cvCopy(img,result);
			cvResetImageROI(img);
			
			IplImage *resizeRes;
			CvSize dst_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 ); // 从中心位置画圆,圈出脸部区域
			int predictedLabel = -1;
			Mat test = img1;
			//images[images.size() - 1] = test;
			model->train(images, labels);
			
			//如果调用read_img函数时 chdir将默认目录做了更改,所以output.jpg自己找一下吧
			imwrite("../ouput.jpg",test);

			//在这里对人脸进行判别
			double predicted_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 );
}
void cvText(IplImage* img, const char* text, int x, int y)  
{  
    CvFont font;  
    double hscale = 1.0;  
    double vscale = 1.0;  
    int linewidth = 2;  
    cvInitFont(&font,CV_FONT_HERSHEY_SIMPLEX | CV_FONT_ITALIC,hscale,vscale,0,linewidth);  
    CvScalar textColor =cvScalar(0,255,255);  
    CvPoint textPos =cvPoint(x, y);  
    cvPutText(img, text, textPos, &font,textColor);  
}

Mat norm_0_255(cv::InputArray _src)
{
	Mat src = _src.getMat();
	Mat dst;

	switch(src.channels())
	{
	case 1:
		cv::normalize(_src, dst, 0, 255, cv::NORM_MINMAX, CV_8UC1);
		break;
	case 3:
		cv::normalize(_src, dst, 0, 255, cv::NORM_MINMAX, CV_8UC3);
		break;
	default:
		src.copyTo(dst);
		break;
	}

	return dst;
}
//读取文件中的图像数据和类别,存入images和labels这两个容器
void read_csv(const string &filename, vector<Mat> &images, vector<int> &labels, char separator)
{
	std::ifstream file(filename.c_str(), ifstream::in);
	if(!file)
	{
		string error_message = "No valid input file was given.";
		CV_Error(CV_StsBadArg, error_message);
	}

	string line, path, classlabel;
	while(getline(file, line))
	{
		stringstream liness(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()));
		}
	}
}
bool read_img(vector<Mat> &images, vector<int> &labels)
{
	
	long file;  
    struct _finddata_t find;  
  
    _chdir("./einfacedata/trainingdata/");  
    if((file=_findfirst("*.*", &find))==-1L) {  
        //printf("空白!/n");  
        return false;  
    }  
    //fileNum = 0;  
    //strcpy(fileName[fileNum], find.name);
	int i = 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);
	return true;
}
// 只是检测人脸,并将人脸圈出 
void detect_and_draw( IplImage* img ) 
{
    static CvScalar colors[] = 
    {
        {{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;
    int i, 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 )
    {
        double t = (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( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) );
        for( i = 0; i < (faces ? faces->total : 0); i++ )
        {
            CvRect* r = (CvRect*)cvGetSeqElem( faces, i ); // 将faces数据从CvSeq转为CvRect 
            CvMat small_img_roi;
            CvSeq* nested_objects;
            CvPoint center;
            CvScalar color = colors[i%8]; // 使用不同颜色绘制各个face,共八种色 
            int radius;
            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:

#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>
using namespace std;
using namespace cv;

#ifndef DETECT_RECOG_H
#define DETECT_RECOG_H

extern CvMemStorage* storage;
extern CvHaarClassifierCascade* cascade;
extern CvHaarClassifierCascade* nested_cascade;
extern int use_nested_cascade;
extern const char* cascade_name;
extern const char* nested_cascade_name;
extern double scale;

extern cv::Ptr<cv::FaceRecognizer> model;
extern vector<Mat> images;
extern vector<int> labels;

void detect_and_draw( IplImage* img ); // 检测和绘画 
void recog_and_draw( IplImage* img ); // 检测和绘画 
void read_csv(const string &filename, vector<Mat> &images, vector<int> &labels, char separator = ';');
bool read_img(vector<Mat> &images, vector<int> &labels);
Mat norm_0_255(cv::InputArray _src);
void cvText(IplImage* img, const char* text, int x, int y);
#endif

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics