0.引言
利用机器学习的方法训练微笑检测模型,输入一张人脸照片,判断是否微笑;
识别精度在95%附近(使用的数据集中69张没笑脸,65张有笑脸);
效果:
图1 测试图像与检测结果
工程利用python 3 开发,借助Dlib进行 人脸嘴部20个特征点坐标(40维特征)的提取;
然后根据这 40维输入特征 作为模型输入, 1维特征(1代表有微笑 / 0代表没微笑)作为输出,进行ML建模;
利用几种机器学习模型进行建模,达到一个二分类(分类有/无笑脸)的目的,然后分析模型识别精度和性能,并且可以识别给定图片的人脸是否微笑;
py文件:
1. Get_features.py :
returnfeatures(): 输入人脸图像路径,利用dlib的“shape_predictor_68_face_landmarks.dat”提取嘴部20个特征点坐标的40个特征值;
writeintoCSV(): 将40维特征输入和1维的输出标记(1代表有微笑/0代表没微笑)写入CSV文件中;
2. ML_ways.py:
pre_data(): 读取CSV中的数据,然后提取出训练集和测试集;
way_LR(): Logistic Regressio, 罗吉斯特回归方法建模;
way_SGD(): Stochastic Gradient Decent, 随机梯度下降法建模;
way_SVM(): Supported Vector Machine, 支持向量机法建模;
way_MLP(): Multi-Layer Perceptron, 多层神经网络法建模;
3. test_single_pic.py:
输入给定测试图像,用ML模型检测其有/无笑脸;
(实现稍微比较复杂,感兴趣的可以结合之前博客看看:
Python 3 利用 Dlib 19.7 进行人脸识别:
http://www.cnblogs.com/AdaminXie/p/7905888.html
Python 3 利用 Dlib 19.7 进行人脸68个特征点的标定:
http://www.cnblogs.com/AdaminXie/p/8137580.html
Python 3 利用机器学习模型进行手写体识别:
http://www.cnblogs.com/AdaminXie/p/8249858.html)
1.开发环境
python: 3.6.3
dlib: 19.7
OpenCv, numpy, sklearn, pandas, os, csv等
Get_features.py中调用的库:
1 import dlib # 人脸识别的库dlib 2 import numpy as np # 数据处理的库numpy 3 import cv2 # 图像处理的库OpenCv 4 import os # 读取文件 5 import csv # csv操作
ML_ways.py中调用的库:
1 # pd读取CSV 2 import pandas as pd 3 4 # 分割数据 5 from sklearn.model_selection import train_test_split 6 7 # 用于数据预加工标准化 8 from sklearn.preprocessing import StandardScaler 9 10 # 使用的四种ML模型 11 from sklearn.linear_model import LogisticRegression 12 from sklearn.linear_model import SGDClassifier 13 from sklearn.svm import LinearSVC 14 from sklearn.neural_network import MLPClassifier
使用的人脸来自于 The MUCT Face Database(Link: http://www.milbo.org/muct/),在此十分感谢!
(The MUCT database was prepared by Stephen Milborrow, John Morkel, and Fred Nicolls in December 2008 at the University Of Cape Town. We would like to send out a thanks to the people who allowed their faces to be used.)
2.设计流程
工作内容主要以下两大块:提取人脸特征 和 ML建模;
整体的设计流程如下图所示:
图2 总体设计流程图
2.1 提取人脸特征:
该部分的设计流程图:
图3 人脸提取特征部分流程图
我先在项目目录下建立两个文件夹,分别存放有笑脸的人脸,和无笑脸的人脸,这样之后读取的时候就可以知道人脸的标记有/无人脸;
关于利用dlib进行人脸68个特征点的提取,在我之前另一篇博客里面介绍过:
(link: http://www.cnblogs.com/AdaminXie/p/7905888.html);
本项目中只使用其中嘴部20个特征点的坐标作为特征输入,20个点的序号如下图所示:
图4 dlib标定的嘴部特征点序号
20个特征点40个坐标值,和输出标记的获取,由returnfeatures函数实现;
输入图像文件所在路径,返回的的是数组features_csv(前40个为特征点坐标值,第41个为标记(1代表有笑脸,0代表无笑脸))
1 # ML_smiles 2 # 2018-1-27 3 # By TimeStamp 4 # cnblogs: http://www.cnblogs.com/AdaminXie/ 5 6 7 detector = dlib.get_frontal_face_detector() 8 predictor = dlib.shape_predictor(‘shape_predictor_68_face_landmarks.dat‘) 9 10 # 输入图像文件所在路径,返回一个41维数组(包含提取到的40维特征和1维输出标记) 11 def returnfeatures(path_pic, XXXpic, features_csv): 12 13 # 输入: path_pic: 图像文件所在目录 14 # XXXpic: 图像文件名 15 16 # 输出: features_csv 41维度的数组,前40维为(提取的20个特征点坐标的40个值),第41维为标记output 17 18 # 比如 path_pic + XXXpic = "F:/code/test.jpg" 精确到jpg 19 img = cv2.imread(path_pic + XXXpic) 20 # 取灰度 21 img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 22 23 # 计算68点坐标 24 pos_68 = [] 25 rects = detector(img_gray, 0) 26 landmarks = np.matrix([[p.x, p.y] for p in predictor(img, rects[0]).parts()]) 27 for idx, point in enumerate(landmarks): 28 # 68点的坐标 29 pos = (point[0, 0], point[0, 1]) 30 pos_68.append(pos) 31 32 # 将点49-68写入csv 33 # 即pos_68[48]-pos_68[67] 34 for i in range(48, 68): 35 features_csv.append(pos_68[i][0]) 36 features_csv.append(pos_68[i][1]) 37 38 #print(features_csv) 39 return features_csv
然后就遍历两个存放有/无笑脸的文件夹,读取图像文件,然后利用returnfeatures()函数得到特征值,写入CSV中:
1 # ML_smiles 2 # 2018-1-27 3 # By TimeStamp 4 # cnblogs: http://www.cnblogs.com/AdaminXie/ 5 # Get_features.py 6 7 8 # 读取图像所在的路径 9 path_pic_smile = "F:/code/python/P_ML_smile/pic/database/smile/" 10 path_pic_nosmile = "F:/code/python/P_ML_smile/pic/database/no/" 11 12 # 获取路径下的图像文件 13 namedir_smile = os.listdir(path_pic_smile) 14 namedir_nosmile = os.listdir(path_pic_nosmile) 15 16 # 存储提取特征数据的CSV的路径 17 path_csv = "F:/code/python/P_ML_smile/data_csv/" 18 19 def writeintoCSV(): 20 with open(path_csv+"data.csv", "w", newline="") as csvfile: 21 writer = csv.writer(csvfile) 22 23 # 处理带笑脸的图像 24 print("######## with smiles #########") 25 for i in range(len(namedir_smile)): 26 print("pic:", path_pic_smile, namedir_smile[i]) 27 28 # 用来存放41维特征 29 features_csv_smiles = [] 30 31 # 利用 returnfeatures 函数提取特征 32 returnfeatures(path_pic_smile, namedir_smile[i], features_csv_smiles) 33 features_csv_smiles.append(1) 34 print("features:", features_csv_smiles, "\n") 35 36 # 写入CSV 37 writer.writerow(features_csv_smiles) 38 39 # 处理不带笑脸的图像 40 print("######## no smiles #########") 41 for i in range(len(namedir_nosmile)): 42 print("pic:", path_pic_nosmile, namedir_nosmile[i]) 43 44 # 用来存放41维特征 45 features_csv_nosmiles = [] 46 47 # 利用 returnfeatures 函数提取特征 48 returnfeatures(path_pic_nosmile, namedir_nosmile[i], features_csv_nosmiles) 49 features_csv_nosmiles.append(0) 50 print("features:", features_csv_nosmiles, "\n") 51 52 # 写入CSV 53 writer.writerow(features_csv_nosmiles)
会得到一个41列的CSV文件,前40列为40维的输入特征,第41列为笑脸标记。
2.2 ML建模和测试
这部分机器学习模型使用比较简单,之前的特征提取已经完成,写入了CSV文件中;
接下来就是要从CSV中将想要的数据集提取出来,利用sklearn进行机器学习建模。
2.2.1 数据预加工
利用pands.read_csv读取CSV文件,然后利用train_test_split进行数据分割;
得到 训练集:X_train, y_train 和 测试集:X_test, y_test
1 # ML_smiles 2 # 2018-1-27 3 # By TimeStamp 4 # cnblogs: http://www.cnblogs.com/AdaminXie/ 5 # pre_data() in ML_ways.py 6 7 8 # 从CSV读取数据 9 def pre_data(): 10 11 # 41维表头 12 column_names = [] 13 for i in range(0, 40): 14 column_names.append("feature_" + str(i+1)) 15 column_names.append("output") 16 17 path_csv = "F:/code/python/P_ML_smile/data_csv/" 18 19 rd_csv = pd.read_csv(path_csv+"data.csv", names=column_names) 20 21 # 输出CSV文件的维度 22 print("shape:", rd_csv.shape) 23 24 global X_train, X_test, y_train, y_test 25 X_train, X_test, y_train, y_test = train_test_split( 26 rd_csv[column_names[0:40]], 27 rd_csv[column_names[40]], 28 test_size=0.25, 29 random_state=33)
2.2.2 机器学习建模
几种建模方法在sklearn中实现的代码类似,所以在此只介绍LR,Logisitic Regression罗吉斯特回归方法;
返回ss_LR和LR,需要这两个返回值,是因为之后要利用它们对给定图像的进行检测,之后2.2.3节会介绍;
1 # ML_smiles 2 # 2018-1-27 3 # By TimeStamp 4 # cnblogs: http://www.cnblogs.com/AdaminXie/ 5 # way_LR() in ML_ways.py 6 7 # 罗吉斯特回归LR 8 def way_LR(): 9 10 X_train_LR = X_train 11 y_train_LR = y_train 12 13 X_test_LR = X_test 14 y_test_LR = y_test 15 16 # 标准化数据预加工 17 ss_LR = StandardScaler() 18 19 X_train_LR = ss_LR.fit_transform(X_train_LR) 20 X_test_LR = ss_LR.transform(X_test_LR) 21 22 # 初始化LogisticRegression 23 LR = LogisticRegression() 24 25 # 调用LogisticRegression中的fit()来训练模型参数 26 LR.fit(X_train_LR, y_train_LR) 27 28 # 使用训练好的模型lr对X_test进行预测,结果储存在lr_y_predict中 29 global y_predict_LR 30 y_predict_LR = LR.predict(X_test_LR) 31 32 global lr_score 33 lr_score=LR.score(X_test_LR, y_test_LR) 34 print("The accurary of LR:", LR.score(X_test_LR, y_test_LR)) 35 36 return ss_LR, LR
我的数据集里面是69张没笑脸,65张有笑脸,测试精度如下,精度在95%附近:
The accurary of LR: 0.941176470588 The accurary of SGD: 0.882352941176 The accurary of SVM: 0.941176470588 The accurary of MLP: 0.970588235294
2.2.3 测试单张图片
现在我们已经建好机器学习模型,在2.2.2中可以利用sklearn机器学习模型的score函数得到模型精度,但是如果想检测给定图像的笑脸,需要进行该部分工作:
path_test_pic+XXXpic就是需要进行检测的文件路径,需要精确到图像文件,比如“F:/pic/test.pic”;
然后调用Get.features.py中的returnfeatures()函数进行特征提取,得到给定图像的40维特征数组single_features;
如果想利用LR模型测试,接受way_LR()的返回值ss_LR和LR,利用ss_LR对single_features进行标准化处理,然后调用LR.predict进行预测;
然后生成图像窗口,将几种模型的结果显示在图像上。
1 # ML_smiles 2 # 2018-1-27 3 # By TimeStamp 4 # cnblogs: http://www.cnblogs.com/AdaminXie/ 5 # test_single_pic.py 6 7 import cv2 8 9 from ML_ways import pre_data 10 from ML_ways import way_LR 11 from ML_ways import way_MLP 12 from ML_ways import way_SGD 13 from ML_ways import way_SVM 14 15 # 获得单张人脸的特征点 16 path_test_pic = "F:/code/python/P_ML_smile/pic/" 17 #path_test_pic = "F:/code/pic/faces/the_muct_face_database/jpg/" 18 19 XXXpic = "test1.jpg" 20 21 # 训练LR模型 22 pre_data() 23 24 # 使用标准化参数和ML模型 25 ss_LR, LR = way_LR() 26 ss_SGD, SGD = way_SGD() 27 ss_SVM, SVM = way_SVM() 28 ss_MLP, MLP = way_MLP() 29 30 # 提取单张40维度特征 31 from Get_features import returnfeatures 32 single_features = [] 33 returnfeatures(path_test_pic, XXXpic, single_features) 34 #print("single_40_features: ", single_features) 35 36 ############## LR模型预测 ############## 37 38 # 特征数据预加工 39 X_single_LR = ss_LR.transform([single_features]) 40 # 利用训练好的LR模型预测 41 y_predict_LR_single = LR.predict(X_single_LR) 42 43 con_LR = str(y_predict_LR_single[0]).replace("1", "smiles").replace("0", "no_smiles") 44 print("LR:", con_LR) 45 46 ############## SGD模型预测 ############## 47 48 # 特征数据预加工 49 X_single_SGD = ss_SGD.transform([single_features]) 50 # 利用训练好的SGD模型预测 51 y_predict_SGD_single = SGD.predict(X_single_SGD) 52 53 con_SGD = str(y_predict_SGD_single[0]).replace("1", "smiles").replace("0", "no_smiles") 54 print("SGD:", con_SGD) 55 56 ############## SVM模型预测 ############## 57 58 # 特征数据预加工 59 X_single_SVM = ss_SVM.transform([single_features]) 60 # 利用训练好的SVM模型预测 61 y_predict_SVM_single = SVM.predict(X_single_SVM) 62 63 con_SVM = str(y_predict_SVM_single[0]).replace("1", "smiles").replace("0", "no_smiles") 64 print("SVM:", con_SVM) 65 66 ############## MLP模型预测 ############## 67 68 # 特征数据预加工 69 X_single_MLP = ss_MLP.transform([single_features]) 70 # 利用训练好的MLP模型预测 71 y_predict_MLP_single = MLP.predict(X_single_MLP) 72 73 con_MLP = str(y_predict_MLP_single[0]).replace("1", "smiles").replace("0", "no_smiles") 74 print("MLP:", con_MLP) 75 76 img = cv2.imread(path_test_pic+XXXpic) 77 78 font = cv2.FONT_HERSHEY_SIMPLEX 79 80 cv2.putText(img, "LR: "+con_LR, (20, 50), font, 1, (0, 0, 255), 1, cv2.LINE_AA) 81 cv2.putText(img, "SGD: "+con_SGD, (20, 100), font, 1, (0, 0, 255), 1, cv2.LINE_AA) 82 cv2.putText(img, "SVM: "+con_SVM, (20, 150), font, 1, (0, 0, 255), 1, cv2.LINE_AA) 83 cv2.putText(img, "MLP: "+con_MLP, (20, 200), font, 1, (0, 0, 255), 1, cv2.LINE_AA) 84 85 cv2.namedWindow("img")#, 2) 86 cv2.imshow("img", img) 87 cv2.waitKey(0)
3.效果
图5 同一个人不同表情的笑脸检测结果
4.总结
自己最近经常使用dlib做人脸识别这块,又在学习机器学习,两者结合尝试做到笑容检测这块;
数据集中有无笑脸是自己进行分类的,而且有写的表情不太好界定,所以选取的是一些笑容比较明显的照片作为有笑脸,所以可能出来模型在检测一些微笑上有误差;
笑容检测模型的数据集测试精度在95%左右,比较理想;
其实人脸笑容检测的话,光靠嘴部特征去判断不太合适,要结合整张人脸特征点进行训练,改进的话也比较简单;
# 请尊重他人劳动成果,转载或者使用源码请注明出处(http://www.cnblogs.com/AdaminXie/)
# 交流学习可以联系邮箱 coneypo@foxmail.com