0. 引言
利用机器学习的方法训练微笑检测模型,给一张人脸照片,判断是否微笑;
使用的数据集中 69 张没笑脸,65 张有笑脸,训练结果识别精度在 95% 附近;
效果:
图 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. 开发环境
OpenCv, numpy, sklearn, pandas, os, csv 等
python: 3.6.3
dlib: 19.7
Get_features.py 中调用的库:
ML_ways.py 中调用的库:
import dlib # 人脸识别的库 dlib
import numpy as np # 数据处理的库 numpy
import cv2 # 图像处理的库 OpenCv
import os # 读取文件
import csv # csv 操作
使用的人脸来自于 The MUCT Face Database(Link: http://www.milbo.org/muct/ ),在此十分感谢!
# pd 读取 CSV
import pandas as pd
# 分割数据
from sklearn.model_selection import train_test_split
# 用于数据预加工标准化
from sklearn.preprocessing import StandardScaler
# 使用的四种 ML 模型
from sklearn.linear_model import LogisticRegression
from sklearn.linear_model import SGDClassifier
from sklearn.svm import LinearSVC
from sklearn.neural_network import MLPClassifier
(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 代表无笑脸))
然后就遍历两个存放有 / 无笑脸的文件夹,读取图像文件,然后利用 returnfeatures() 函数得到特征值,写入 CSV 中:
# ML_smiles
# 2018-1-27
# By TimeStamp
# cnblogs: http://www.cnblogs.com/AdaminXie/
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
# 输入图像文件所在路径,返回一个 41 维数组(包含提取到的 40 维特征和 1 维输出标记)
def returnfeatures(path_pic, XXXpic, features_csv):
# 输入: path_pic: 图像文件所在目录
# XXXpic: 图像文件名
# 输出: features_csv 41 维度的数组,前 40 维为 (提取的 20 个特征点坐标的 40 个值),第 41 维为标记 output
# 比如 path_pic + XXXpic = "F:/code/test.jpg" 精确到 jpg
img = cv2.imread(path_pic + XXXpic)
# 取灰度
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 计算 68 点坐标
pos_68 = []
rects = detector(img_gray, 0)
landmarks = np.matrix([[p.x, p.y] for p in predictor(img, rects[0]).parts()])
for idx, point in enumerate(landmarks):
# 68 点的坐标
pos = (point[0, 0], point[0, 1])
pos_68.append(pos)
# 将点 49-68 写入 csv
# 即 pos_68[48]-pos_68[67]
for i in range(48, 68):
features_csv.append(pos_68[i][0])
features_csv.append(pos_68[i][1])
#print(features_csv)
return features_csv
会得到一个 41 列的 CSV 文件,前 40 列为 40 维的输入特征,第 41 列为笑脸标记.
# ML_smiles
# 2018-1-27
# By TimeStamp
# cnblogs: http://www.cnblogs.com/AdaminXie/
# Get_features.py
# 读取图像所在的路径
path_pic_smile = "F:/code/python/P_ML_smile/pic/database/smile/"
path_pic_nosmile = "F:/code/python/P_ML_smile/pic/database/no/"
# 获取路径下的图像文件
namedir_smile = os.listdir(path_pic_smile)
namedir_nosmile = os.listdir(path_pic_nosmile)
# 存储提取特征数据的 CSV 的路径
path_csv = "F:/code/python/P_ML_smile/data_csv/"
def writeintoCSV():
with open(path_csv+"data.csv", "w", newline="") as csvfile:
writer = csv.writer(csvfile)
# 处理带笑脸的图像
print("######## with smiles #########")
for i in range(len(namedir_smile)):
print("pic:", path_pic_smile, namedir_smile[i])
# 用来存放 41 维特征
features_csv_smiles = []
# 利用 returnfeatures 函数提取特征
returnfeatures(path_pic_smile, namedir_smile[i], features_csv_smiles)
features_csv_smiles.append(1)
print("features:", features_csv_smiles, "\n")
# 写入 CSV
writer.writerow(features_csv_smiles)
# 处理不带笑脸的图像
print("######## no smiles #########")
for i in range(len(namedir_nosmile)):
print("pic:", path_pic_nosmile, namedir_nosmile[i])
# 用来存放 41 维特征
features_csv_nosmiles = []
# 利用 returnfeatures 函数提取特征
returnfeatures(path_pic_nosmile, namedir_nosmile[i], features_csv_nosmiles)
features_csv_nosmiles.append(0)
print("features:", features_csv_nosmiles, "\n")
# 写入 CSV
writer.writerow(features_csv_nosmiles)
2.2 ML 建模和测试
这部分机器学习模型使用比较简单,之前的特征提取已经完成,写入了 CSV 文件中;
接下来就是要从 CSV 中将想要的数据集提取出来,利用 sklearn 进行机器学习建模.
2.2.1 数据预加工
利用 pands.read_csv 读取 CSV 文件,然后利用 train_test_split 进行数据分割;
得到 训练集:X_train, y_train 和 测试集:X_test, y_test
2.2.2 机器学习建模
# ML_smiles
# 2018-1-27
# By TimeStamp
# cnblogs: http://www.cnblogs.com/AdaminXie/
# pre_data() in ML_ways.py
# 从 CSV 读取数据
def pre_data():
# 41 维表头
column_names = []
for i in range(0, 40):
column_names.append("feature_" + str(i+1))
column_names.append("output")
path_csv = "F:/code/python/P_ML_smile/data_csv/"
rd_csv = pd.read_csv(path_csv+"data.csv", names=column_names)
# 输出 CSV 文件的维度
print("shape:", rd_csv.shape)
global X_train, X_test, y_train, y_test
X_train, X_test, y_train, y_test = train_test_split(
rd_csv[column_names[0:40]],
rd_csv[column_names[40]],
test_size=0.25,
random_state=33)
几种建模方法在 sklearn 中实现的代码类似,所以在此只介绍 LR,Logisitic Regression 罗吉斯特回归方法;
返回 ss_LR 和 LR,需要这两个返回值,是因为之后要利用它们对给定图像的进行检测,之后 2.2.3 节会介绍;
我的数据集里面是 69 张没笑脸,65 张有笑脸,测试精度如下,精度在 95% 附近:
# ML_smiles
# 2018-1-27
# By TimeStamp
# cnblogs: http://www.cnblogs.com/AdaminXie/
# way_LR() in ML_ways.py
# 罗吉斯特回归 LR
def way_LR():
X_train_LR = X_train
y_train_LR = y_train
X_test_LR = X_test
y_test_LR = y_test
# 标准化数据预加工
ss_LR = StandardScaler()
X_train_LR = ss_LR.fit_transform(X_train_LR)
X_test_LR = ss_LR.transform(X_test_LR)
# 初始化 LogisticRegression
LR = LogisticRegression()
# 调用 LogisticRegression 中的 fit() 来训练模型参数
LR.fit(X_train_LR, y_train_LR)
# 使用训练好的模型 lr 对 X_test 进行预测,结果储存在 lr_y_predict 中
global y_predict_LR
y_predict_LR = LR.predict(X_test_LR)
global lr_score
lr_score=LR.score(X_test_LR, y_test_LR)
print("The accurary of LR:", LR.score(X_test_LR, y_test_LR))
return ss_LR, LR
2.2.3 测试单张图片
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.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 进行预测;
然后生成图像窗口,将几种模型的结果显示在图像上.
3. 效果
# ML_smiles
# 2018-1-27
# By TimeStamp
# cnblogs: http://www.cnblogs.com/AdaminXie/
# test_single_pic.py
import cv2
from ML_ways import pre_data
from ML_ways import way_LR
from ML_ways import way_MLP
from ML_ways import way_SGD
from ML_ways import way_SVM
# 获得单张人脸的特征点
path_test_pic = "F:/code/python/P_ML_smile/pic/"
#path_test_pic = "F:/code/pic/faces/the_muct_face_database/jpg/"
XXXpic = "test1.jpg"
# 训练 LR 模型
pre_data()
# 使用标准化参数和 ML 模型
ss_LR, LR = way_LR()
ss_SGD, SGD = way_SGD()
ss_SVM, SVM = way_SVM()
ss_MLP, MLP = way_MLP()
# 提取单张 40 维度特征
from Get_features import returnfeatures
single_features = []
returnfeatures(path_test_pic, XXXpic, single_features)
#print("single_40_features:", single_features)
############## LR 模型预测 ##############
# 特征数据预加工
X_single_LR = ss_LR.transform([single_features])
# 利用训练好的 LR 模型预测
y_predict_LR_single = LR.predict(X_single_LR)
con_LR = str(y_predict_LR_single[0]).replace("1", "smiles").replace("0", "no_smiles")
print("LR:", con_LR)
############## SGD 模型预测 ##############
# 特征数据预加工
X_single_SGD = ss_SGD.transform([single_features])
# 利用训练好的 SGD 模型预测
y_predict_SGD_single = SGD.predict(X_single_SGD)
con_SGD = str(y_predict_SGD_single[0]).replace("1", "smiles").replace("0", "no_smiles")
print("SGD:", con_SGD)
############## SVM 模型预测 ##############
# 特征数据预加工
X_single_SVM = ss_SVM.transform([single_features])
# 利用训练好的 SVM 模型预测
y_predict_SVM_single = SVM.predict(X_single_SVM)
con_SVM = str(y_predict_SVM_single[0]).replace("1", "smiles").replace("0", "no_smiles")
print("SVM:", con_SVM)
############## MLP 模型预测 ##############
# 特征数据预加工
X_single_MLP = ss_MLP.transform([single_features])
# 利用训练好的 MLP 模型预测
y_predict_MLP_single = MLP.predict(X_single_MLP)
con_MLP = str(y_predict_MLP_single[0]).replace("1", "smiles").replace("0", "no_smiles")
print("MLP:", con_MLP)
img = cv2.imread(path_test_pic+XXXpic)
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img, "LR:"+con_LR, (20, 50), font, 1, (0, 0, 255), 1, cv2.LINE_AA)
cv2.putText(img, "SGD:"+con_SGD, (20, 100), font, 1, (0, 0, 255), 1, cv2.LINE_AA)
cv2.putText(img, "SVM:"+con_SVM, (20, 150), font, 1, (0, 0, 255), 1, cv2.LINE_AA)
cv2.putText(img, "MLP:"+con_MLP, (20, 200), font, 1, (0, 0, 255), 1, cv2.LINE_AA)
cv2.namedWindow("img")#, 2)
cv2.imshow("img", img)
cv2.waitKey(0)
图 5 同一个人不同表情的笑脸检测结果
4. 总结
自己最近经常使用 dlib 做人脸识别这块,又在学习机器学习,两者结合尝试做到笑容检测这块;
数据集中有无笑脸是自己进行分类的,而且有写的表情不太好界定,所以选取的是一些笑容比较明显的照片作为有笑脸,所以可能出来模型在检测一些微笑上有误差; 笑容检测模型的数据集测试精度在 95% 左右,比较理想; 其实人脸笑容检测的话,光靠嘴部特征去判断不太合适,要结合整张人脸特征点进行训练,改进的话也比较简单;
来源: https://www.cnblogs.com/AdaminXie/p/8367348.html