实时人脸识别的实现-基于opencv(7-18)

今天总结一下前段时间实践的基于opencv实时人脸识别软件的实现。

利用opencv来做人脸识别,对于想快速上手学习opencv以及机器学习方面知识的同学是个不错的选择。人脸识别,一般分为两个步骤,第一个就是人脸检测,第二步才是识别
首先,人脸检测,opencv常用方法为基于adaboost的haar特征分类器,如何利用其提供的api训练自己的分类器,可参考这篇文章。
然后,找到人脸后,利用人脸区域的一些特征(LBP?HOG?),通过MLP或者CNN然后训练自己的识别资料库,最后进行识别。当然,opencv为我们提高了便捷快速的api,你甚至可以不去了解它怎么工作的,就可以训练自己的资料库进行识别。
以下为opencv提供的三种算法:

        Eigenfaces特征脸createEigenFaceRecognizer()
         Fisherfaces  createFisherFaceRecognizer()
         LocalBinary Patterns Histograms局部二值直方图 createLBPHFaceRecognizer()

以下为python版本和c++版本的实现:

Python版本的实现:

(实现了一个按键人脸识别考勤系统,当然如果要一直进行识别,去掉按键触发就好了)
pyqt5
opencv-python3.4.4
在这里插入图片描述
在这里插入图片描述由于手机中的照片为样本拍的,所以准确率还算可以。我用的是lbp特征,置信度在80内认为识别到了,但多场景以及光线变化明显效果就不是很好。

贴出界面外识别线程的程序,多线程为了在qt-label中实时显示画面。

class Thread(QThread):#采用线程来播放视频

    global id, minH, minW, font, recognizer, faceCascade, names
    changePixmap = pyqtSignal(QtGui.QImage)
    
    #线程主函数
    def run(self):
        #HardWare.IO_Init()            
        global none_save                            
        none_save = 0
        if_dist = 1				   
   #pdb.set_trace()  # start debug
        while 1:
            #time.sleep(2)
            if_dist = HardWare.if_distance()	   
            print(if_dist)
            if if_dist == 0:			    
                flag = self.if_recognize(100,1)     #用户自己修改,100代表检测一百帧,1代表识别到就跳出
                print(flag)
		#识别失败,保存图片
                if flag == 'False':
                    none_save += 1
                    
		#识别到用户开门
                else:					
                    HardWare.openDoor()
                    time.sleep(5)
                    HardWare.closeDoor()
            else:
                HardWare.closeDoor()            #没人始终关门

  #封装人脸识别函数,实现功能: 输入 指定帧数 图像,凡是指定帧有 n张 识别成功 or 识别到连续的为同一个人,则返回 name;否则返回 false。
    def if_recognize(self,in_nums,ok_nums):
        cap = cv2.VideoCapture(0)
        last_id = 0
        i = 0
        ok_i = 0
        
        while 1:
            if cap.isOpened()==True:
                ret, img = cap.read()
                i += 1
                #img = cv2.resize(img, (320, 240), cv2.INTER_CUBIC)  # 缩小图像处理,增加帧率
                gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
                faces = faceCascade.detectMultiScale(  # 人脸检测
                    gray,
                    scaleFactor=1.2,
                    minNeighbors=5,
                    minSize=(int(minW), int(minH)),
                )
                if i >= in_nums:																			
					cv2.imwrite('none/'+str(none_save)+'.png',img,[int(cv2.IMWRITE_PNG_COMPRESSION),9])     
                    cap.release()																			
                    img.fill(255)																			
                    cv2.putText(img,' False !',(20,120),cv2.FONT_HERSHEY_SIMPLEX,2.0,(0,0,0),2)            	
                    rgbImage = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)											
                    convertToQtFormat = QtGui.QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0],	
                                                 QImage.Format_RGB888)
                    p = convertToQtFormat.scaled(gd.show_w, gd.show_h, Qt.KeepAspectRatio)					
                    self.changePixmap.emit(p)																
                    break

                count = 0								
                for (x, y, w, h) in faces:
                    count += 1								
                    if count>=2:							
                        break
                    # cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
                    id, confidence = recognizer.predict(gray[y:y + h, x:x + w])
                    # 置信度 confidence ;  0 最完美
                    if (confidence < 80):
                        ok_i += 1
                        if (last_id == id)|(ok_i >= ok_nums):   
                            id = names[id]
                            #识别完一次之后,释放摄像头,并关闭显示
                            cap.release()
                            img.fill(255)
                            cv2.putText(img,'Success ! ',(50,150),cv2.FONT_HERSHEY_SIMPLEX,2.0,(0,0,0),2)
                            cv2.putText(img,'Welcome '+str(id)+'!',(10,250),cv2.FONT_HERSHEY_SIMPLEX,2.0,(0,0,0),2)
                            rgbImage = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
                            convertToQtFormat = QtGui.QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0],
                                                 QImage.Format_RGB888)  										# 在这里可以对每帧图像进行处理,
                            p = convertToQtFormat.scaled(gd.show_w, gd.show_h, Qt.KeepAspectRatio)
                            self.changePixmap.emit(p)
                            return str(id)
                        last_id = id

                    else:
                        id = "unknown"
                        continue

                rgbImage = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
                convertToQtFormat = QtGui.QImage(rgbImage.data, rgbImage.shape[1], rgbImage.shape[0],
                                                 QImage.Format_RGB888)  
                p = convertToQtFormat.scaled(gd.show_w, gd.show_h, Qt.KeepAspectRatio)
                self.changePixmap.emit(p)


return 'False'

C++版本实现

(实现了实现人脸检测软体,集采集、训练、识别为一体)
Qt5.8
opencv2.4.10
如果是opencv3+的,区别可参考人脸识别FaceRecognizer类的改变【opencv2.4.10——>opencv3.4.4】
在这里插入图片描述
长时间测试还是遇到和python版本一样的问题,LBP特征,训练样本100张,置信度调为90,识别率大概在百分之七十左右,受光线影响大。

采集、训练、预测代码

/*
	人脸检测,保存样本。
	输入参数:样本数量
*/
int save_FaceSamples(int NUMS)
{
	string face_id;
	char s[50];//字符数组,用于存放字符串的每一个字符
	cout << "Please input a name" << endl;

	cin.get(s,50);           //终端输入样本文件夹
	face_id = s;//人的名字
	cout << face_id << endl;
	printf ("\n 看着摄像头,并等待 ...");

	save_samplename(s);              //存样本名字     

	VideoCapture capture(0);//打开摄像头

	//Size S = Size((int)capture.get(CAP_PROP_FRAME_WIDTH), (int)capture.get(CAP_PROP_FRAME_HEIGHT));
	//int fps = capture.get(CAP_PROP_FPS);

	//加载人脸检测分类器
	CascadeClassifier faceDetector;
	faceDetector.load(haar_face_datapath);


	Mat frame;
	vector<Rect>faces;
	int count = 0;
	int	num = 0;

	//检测人脸并将人脸作为样本存入
	while (1)
	{
		 capture.read(frame);
		 faceDetector.detectMultiScale(frame, faces,1.2,2, 0 | CV_HAAR_SCALE_IMAGE, cv::Size(80, 80));         //经测试,最佳参数
		 for (int i = 0; i < faces.size(); i++)
		 {
			 if (count % 10 == 0)
			 {
				 num++;
				 Mat dst;
				 resize(frame(faces[i]), dst, Size(100, 100));
				 cvtColor(dst, dst, COLOR_BGR2GRAY);
				 string path = "../face/" + face_id + "/";	//新文件夹路径
				 mkdir(path.c_str(),S_IRWXU);//创建人名为文件名的新文件夹
				 imwrite( path + face_id +"_"+ to_string(num) + ".jpg",dst);	//在对应文件夹中写入对应人的图片(如:名为‘小明’的文件夹中存入小明的图片)
			 }
			 rectangle(frame, faces[i], Scalar(0, 0, 255), 2, 8, 0);//框出人脸
			 count++;
		 }
		flip(frame, frame, 1);//镜像翻转
		imshow("window", frame);//显示的窗口
		char c = waitKey(1);
		if (c == 27)//Esc键退出
		{
			break;
		}
		if (num >= NUMS )
		{
			break;
		}
	}
	return 0;
}
/*
	返回 0 ,不训练,直接读取成功;
	返回 1 ,进行训练,再预测。
	进行训练之前需要先删除已存在的 xml 文件,或者改名。
*/
bool start_train(bool flag)
{
	fstream xmlfile;
	xmlfile.open(filepath, ios::in);                 //根据自己需要进行适当的选取 ios::in|ios::out|ios::binary
	if(flag == 0){
		if (xmlfile)                  //存在训练好的xml
		{
			std::cout <<"xml is existed" <<std::endl;
			xmlfile.close();
			return 0;
		}
	}

	ifstream file(listpath.c_str(), ifstream::in);
	if (!file)
	{
		printf("could not load file correctly...\n");
		return -1;
	}

	string line, path, classlabel;
	vector<Mat>images;
	vector<int>labels;
	char separator = ' ';
	cv::Mat image_one;
	while (!file.eof())
	{
		getline(file, line);
		//cout << line << endl;
		stringstream lines(line);
		getline(lines, path, separator);//获取样本图片路径
		getline(lines, classlabel);//获取标签
		//printf("%s---\n", classlabel.c_str());

		if (!path.empty() && !classlabel.empty())
		{
			//printf("ok:::path:%s\n", path.c_str());
			image_one = imread(path, 0);
			if(!image_one.data){
				cout << "err1"<<endl;
				break;
			}
			images.push_back(image_one);//样本图片放入容器
			labels.push_back(atoi(classlabel.c_str()));//标签放入容器
		}
	}

	if (images.size() < 1 || labels.size() < 1)
	{
		printf("invalid image path...\n");
		return -1;
	}

	//训练模型
	Ptr<FaceRecognizer> model = createLBPHFaceRecognizer();
	model->train(images, labels);
	model->save(filepath);

	xmlfile.close();
	return 1;
}
/*
	人脸识别预测函数
*/
int start_predict()
{
	bool flag_train = 0;
	flag_train = start_train(0);
	std::cout << "train ok!"<<std::endl;
	//识别分类器
	Ptr<FaceRecognizer> model = createLBPHFaceRecognizer();
	model->load(filepath);

	//加载检测分类器
	CascadeClassifier faceDetector;
	faceDetector.load(haar_face_datapath);

	VideoCapture capture(0);//打开摄像头
	if (!capture.isOpened())
	{
		printf("could not open camera...\n");
		return -1;
	}
	Mat frame;
	vector<Rect>faces;
	namedWindow("face-recognition", WINDOW_AUTOSIZE);//图片显示的窗口
	while (1)
	{
		capture.read(frame);//摄像头获取图片

		flip(frame, frame, 1);//镜像翻转
		faceDetector.detectMultiScale(frame, faces, 1.08, 3, 0, Size(50, 60), Size(380, 400));
		for (int i = 0; i < faces.size(); i++)
		{
			Mat dst;
			resize(frame(faces[i]), dst, Size(100, 100));//规范尺寸用于后续人脸识别
			cvtColor(dst, dst, COLOR_BGR2GRAY);//灰度化
			rectangle(frame, faces[i], Scalar(0, 255, 0), 2, 8, 0);//在窗口中框出人脸
			int predictedLabel = -1;
			double confidence = 0.0;
		    	model->predict(dst, predictedLabel, confidence);//对窗口中人脸进行识别,给出预测标签并赋于predictedLabel
			string result_message = format("Predicted number = %d / confidence = %2f.", predictedLabel, confidence);//查看标签和置信度
			cout << result_message << endl;

			//不同人对应的不同标签
		if(confidence > 90){
			predictedLabel = 100;
		}
		std::vector<std::string> m;
		std::vector<int> l;
		read_names(m,l);
		if(predictedLabel<m.size())
			putText(frame,  m[predictedLabel], faces[i].tl(), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 255, 0), 1, 30);//在人脸旁显示人名
		else
			putText(frame, "unkown", faces[i].tl(), FONT_HERSHEY_PLAIN, 1.0, Scalar(0, 255, 0), 1, 8);
		}
		imshow("face-recognition", frame);
		char c = waitKey(1);
		if (c == 27)
		{
			break;
		}
	}
	return 0;
}

C++版本
这里分享树莓派端的实现代码,硬件用光电开关控制是否开启(即实现人走进时自动识别)
树莓派端实现代码

  • 9
    点赞
  • 134
    收藏
    觉得还不错? 一键收藏
  • 11
    评论
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值