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

it2022-05-05  142

今天总结一下前段时间实践的基于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 = /.", 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++版本 这里分享树莓派端的实现代码,硬件用光电开关控制是否开启(即实现人走进时自动识别) 树莓派端实现代码


最新回复(0)