传送门
首先你需要安装安装 Cmake 程序包,该工具主要是对 dlib 进行编译,安装命令与其他包类似
pip install Cmake
然后安装
pip install dlib
一般情况就是可以了,如果没有成功,可能是因为你的电脑没有安装Microsoft Visual Studio,你可能还需要安装boost(安装dlib前需要先安装cmake 和boost。然后才能正确安装dlib)
pip install boost
PS:编译时间可能较长,没有报错的话,就可能要多等待一会,如果实在安装不好可以在CSDN多搜索看看
使用dlib进行人脸关键点定位,分为5点和68点,这里使用的是68点。首先你需要到下面这网站下载shape_predictor_68_face_landmarks.dat文件
http://dlib.net/files/
考虑到有的小伙伴可能下载不下来,下面附上该文件的百度网盘链接
链接:https://pan.baidu.com/s/1hfSzGjC9gg-oqOnpCPy49Q
提取码:iswa
下面代码就不一一分析了,基本上都有注释。值得注意的是下面这行代码,你可以将下面这行代码理解为解码过程(举个例子:战争中,士兵截获了别人情报信息,但无法直接理解其中的信息,那么你就需要相应情报部门帮助你破解其中的信息,将其转换为你可以理解的信息)
result = predictor(gray, rect)
#print(result)#PS:打印结果<dlib.full_object_detection object at 0x00000265E25B3CF0>
result=result.parts()#result = result.parts(),这里必须对上面的result进行转换,否则无法直接使用。小伙伴们可以将result.parts视为解码过程
print(type(result))#打印结果:<class 'dlib.points'>
points = [[p.x, p.y] for p in result]#上面的result还需进行结果提取
#print(points)
我不知道这样解释,小伙伴们能不能理解。(注:上面这一小段代码是从下面的程序中拿出来的),如果不理解,就多打印打印
这里附上68人脸关键点的图片
下面的程序是先对图像进行人脸识别,然后再人脸识别的基础上进行人脸关键定位
from collections import OrderedDict
import dlib
import cv2
#http://dlib.net/files/
dict={
"right_eyebrow": (17, 22),
"left_eyebrow": (22, 27),
"right_eye": (36, 42),
"left_eye": (42, 48),
"nose": (27, 36),
"mouth": (48, 68),
"jaw": (0, 17)
}
FACIAL_LANDMARKS_68_IDXS=OrderedDict(dict)#OrderedDict是dict的子类,它记住了内容添加的顺序。这里使字典中的元素不会乱序
def line_points(image, points):
output = image.copy()
# 遍历每一个区域
for (i, name) in enumerate(FACIAL_LANDMARKS_68_IDXS.keys()):
# 得到每一个点的坐标
(j, k) = FACIAL_LANDMARKS_68_IDXS[name]
pts = points[j:k]
for l in range(1, len(pts)):
ptA = tuple(pts[l - 1])
ptB = tuple(pts[l])
cv2.line(output, ptA, ptB, (0, 255, 0), 1)#总是原谅色
return output
# 加载人脸检测与关键点定位
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor("shape_predictor_68_face_landmarks.dat")
# 读取输入数据,预处理
image = cv2.imread('face.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 这里先识别人脸,然后下面在识别到人脸的基础上进行人脸关键点的分别展示
rects = detector(gray, 1)#第一个参数是传入的图像,第二个参数 1 代表的是图片上采样倍数,值越大,最终识别得到的结果越好,但耗时会越长
# 遍历检测到的框
for (i, rect) in enumerate(rects):
result = predictor(gray, rect)
#print(result)#PS:打印结果<dlib.full_object_detection object at 0x00000265E25B3CF0>
result=result.parts()#result = result.parts(),这里必须对上面的result进行转换,否则无法直接使用。小伙伴们可以将result.parts视为解码过程
print(type(result))#打印结果:<class 'dlib.points'>
points = [[p.x, p.y] for p in result]#上面的result还需进行结果提取
# 遍历每一个部分
for (name, (i, j)) in FACIAL_LANDMARKS_68_IDXS.items():#这里涉及python字典的用法
copy = image.copy()
cv2.putText(copy, name, (10, 30), cv2.FONT_HERSHEY_SIMPLEX,
0.7, (0, 255, 0), 2)#原谅色
print(i,j)
#print(result[i:j])
# 根据位置画点
for (x, y) in points[i:j]:
#print(rects.parts())
cv2.circle(copy, (x, y), 3, (0, 255, 0), -1)#还是原谅色
# 显示每一部分
cv2.imshow("output", copy)
cv2.waitKey(0)
output = line_points(image, points)#这里将每一部分人脸关键点连线
cv2.imshow("output", output)
cv2.waitKey(0)
在得到了人脸关键点后,就可以做眨眼检测了
下面是技术文章的PDF文档(我给你们下载了)
链接:https://pan.baidu.com/s/1TV32qbNpJ0NKn3Tqh91QJw
提取码:e9rl
因为文章是英文,这里推荐一个PDF在线翻译网站
传送门
这里建议小伙伴们大致读一下上面的PDF文档,可以更好的帮助你理解
通过文章发现正常情况下,人眼在睁开时,左右眼的ratio的值基本上都大于0.3.所以我们认为当ratio小于0.3时,认为开始闭眼。而后检测如果接下来连续2帧(也就是一共3帧)的ratio都小于0.3即认为是一次眨眼
PS:如果不理解,建议将程序中的**cv2.waitKey(1)**里的“1”改为“0”,然后按回车键一帧一帧的跑程序
(下面程序,是在别人基础上改的)
#导入工具包
from scipy.spatial import distance
from collections import OrderedDict
import numpy as np
import dlib
import cv2
dict={
"right_eyebrow": (17, 22),
"left_eyebrow": (22, 27),
"right_eye": (36, 42),
"left_eye": (42, 48),
"nose": (27, 36),
"mouth": (48, 68),
"jaw": (0, 17)
}
FACIAL_LANDMARKS_68_IDXS=OrderedDict(dict)#OrderedDict是dict的子类,它记住了内容添加的顺序。这里使字典中的元素不会乱序
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
(left_Start, left_End) = FACIAL_LANDMARKS_68_IDXS["left_eye"]#取出字典中的左眼部分 (22, 27)
(right_Start, right_End) = FACIAL_LANDMARKS_68_IDXS["right_eye"]#取出字典中的右眼部分(36, 42)
cap = cv2.VideoCapture('1.mp4')
# 初始化计数器
count = 0
time = 0
# 遍历每一帧
while True:
ret,frame = cap.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 检测人脸
rects = detector(gray, 0)
# 遍历每一个检测到的人脸
for rect in rects:
# 获取坐标
result = predictor(gray, rect)
result=result.parts()
points = [[p.x, p.y] for p in result]
#print(type(points))#类型为list
points=np.array(points)#这里需将上面的points转化为数组
#计算左眼,这里的计算发法就是从上面的文章里得到的
#####################################################################
left_eye = points[left_Start:left_End]
X1 = distance.euclidean(left_eye[1], left_eye[5]) # 计算P2到P6的欧氏距离
Y1 = distance.euclidean(left_eye[2], left_eye[4]) # 计算P3到P5的欧氏距离
# 计算距离,水平的
Z1 = distance.euclidean(left_eye[0], left_eye[3]) # 计算P1到P4的欧氏距离
# 计算比值
left_ratio = (X1 + Y1) / (2.0 * Z1)
######################################################################
# 计算右眼
#####################################################################
right_eye = points[right_Start:right_End]
X2 = distance.euclidean(right_eye[1], right_eye[5]) # 计算P2到P6的欧氏距离
Y2 = distance.euclidean(right_eye[2], right_eye[4]) # 计算P3到P5的欧氏距离
# 计算距离,水平的
Z2 = distance.euclidean(right_eye[0], right_eye[3]) # 计算P1到P4的欧氏距离
# 计算比值
right_ratio = (X2 + Y2) / (2.0 * Z2)
#####################################################################
ratio = (left_ratio + right_ratio) / 2.0# 取平均值,因为一般人都是同时闭眼,所以这里直接取平均
leftEyeHull = cv2.convexHull(left_eye)#获得左眼的凸包
rightEyeHull = cv2.convexHull(right_eye)#获得右眼的凸包
cv2.drawContours(frame, [leftEyeHull], -1, (0, 255, 0), 1)
cv2.drawContours(frame, [rightEyeHull], -1, (0, 255, 0), 1)
# 检查是否满足阈值
if ratio < 0.3:#如果ratio小于0.3则视为开始闭眼,PS:0.3只是经验值,并不绝对,如果你调用电脑摄像头的话,这个值可能就要设置的小一点
count += 1
else:
# 如果连续几帧都是闭眼的,总数算一次
if count >= 3:#这里设置如果连续3帧都低于阈值,则为1次眨眼
time += 1
count = 0#这里必须将上面的count重置为0
# 显示
cv2.putText(frame, "Blinks: {}".format(time), (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
cv2.putText(frame, "ratio: {:.2f}".format(ratio), (300, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
cv2.imshow("Frame", frame)
key = cv2.waitKey(1) & 0xFF
if key == 27:
break
cap.release()
cv2.destroyAllWindows()
传送门
上面是跑程序的视频,感兴趣的小伙伴可以看看
这里给小伙伴们提供一个有意思的思路,就是如果想做疲劳检测的话,在上面这个程序基础上稍微修改一下就可以简单实现。这里仅从眼睛上判断,如果一个人它很疲劳,那么它一次眨眼的时间可能就比较长。也就是,正常人一次眨眼可能程序的时间不过为上面的3帧,疲劳的人他一次眨眼的时间应该是大于三帧或者说持续的帧数可能更多。通过这样判断,即可实现疲劳的简单判断。
学习opencv有很多的方法,我的建议是你可以加一些群,可以充分利用B站,CSDN,和百度。
在我的博客中,我不会讲解opencv的算法实现(当然我也不太会),我只会讲解一些函数的调用,不理解就多改一些参数,多尝试尝试,慢慢你就理解来。相信你总有一天可以说opencv不过“Ctrl+C,Crtl+V”
PS:如果有什么错误的地方,还请大家批评指正,不过错了也没啥关系,反正也没什么人看
最后,希望小伙伴们都能有所收获。码字不易,喜欢的话,关注一波在走吧