今天总结一下前段时间实践的基于opencv实时人脸识别软件的实现。
利用opencv来做人脸识别,对于想快速上手学习opencv以及机器学习方面知识的同学是个不错的选择。人脸识别,一般分为两个步骤,第一个就是人脸检测,第二步才是识别。
首先,人脸检测,opencv常用方法为基于adaboost的haar特征分类器,如何利用其提供的api训练自己的分类器,可参考这篇文章。
然后,找到人脸后,利用人脸区域的一些特征(LBP?HOG?),通过MLP或者CNN然后训练自己的识别资料库,最后进行识别。当然,opencv为我们提高了便捷快速的api,你甚至可以不去了解它怎么工作的,就可以训练自己的资料库进行识别。
以下为opencv提供的三种算法:
Eigenfaces特征脸createEigenFaceRecognizer()
Fisherfaces createFisherFaceRecognizer()
LocalBinary Patterns Histograms局部二值直方图 createLBPHFaceRecognizer()
以下为python版本和c++版本的实现:
(实现了一个按键人脸识别考勤系统,当然如果要一直进行识别,去掉按键触发就好了)
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'
(实现了实现人脸检测软体,集采集、训练、识别为一体)
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;
vectorfaces;
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" <images;
vectorlabels;
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"< 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!"< 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;
vectorfaces;
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 m;
std::vector l;
read_names(m,l);
if(predictedLabel
C++版本
这里分享树莓派端的实现代码,硬件用光电开关控制是否开启(即实现人走进时自动识别)
树莓派端实现代码