Skip to content

Commit 1781530

Browse files
Merge pull request #2 from 1647790440/V1.0
V1.0
2 parents fa50680 + acd5e4e commit 1781530

18 files changed

+153
-56
lines changed

README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# 人物专注性检测
2+
3+
## 1.0版本
4+
在征得原作者的同意之后,进行了部分修改,得到V1.0版本
5+
6+
主要不同地方为:
7+
8+
1、疲劳检测中去掉了点头行为的检测,仅保留闭眼检测和打哈欠检测。
9+
10+
2、Yolov5的权重进行了重新训练,增加了训练轮次。
11+
12+
3、前端UI进行了修改,精简了部分功能。
13+
14+
## 项目介绍
15+
该项目为人物专注性检测,分为两个检测部分,疲劳检测和分心行为检测。
16+
疲劳检测部分,使用Dlib进行人脸关键点检测,然后通过计算眼睛和嘴巴的开合程度来判断是存在否闭眼或者打哈欠,并使用Perclos模型计算疲劳程度。
17+
分心行为检测部分,使用Yolov5,检测是否存在玩手机、抽烟、喝水这三种行为。
18+
19+
## 使用方法
20+
依赖:YoloV5、Dlib、PySide2
21+
22+
直接运行main.py,即可使用本程序,具体效果可以观看演示视频。
23+
24+
各函数的信息,均在代码中写好了注释,如有疑问请联系1647790440@qq.com
25+
26+
## 致谢
27+
十分感谢原作者的支持和帮助,本项目很大部分都基于源项目,项目所使用的数据集也由原作者提
28+
29+
供。
30+
59 Bytes
Binary file not shown.

__pycache__/myframe.cpython-39.pyc

59 Bytes
Binary file not shown.
59 Bytes
Binary file not shown.

main.py

Lines changed: 77 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#主函数
12
import sys
23
import os
34
from glob import glob
@@ -10,26 +11,28 @@
1011
import myframe
1112

1213
# 定义变量
13-
# 眼睛长宽比
14-
# 闪烁阈值
15-
EYE_AR_THRESH = 0.15
16-
EYE_AR_CONSEC_FRAMES = 2
17-
# 打哈欠长宽比
18-
# 闪烁阈值
19-
MAR_THRESH = 0.65
20-
MOUTH_AR_CONSEC_FRAMES = 3
21-
# 初始化帧计数器和眨眼总数
22-
COUNTER = 0
23-
TOTAL = 0
24-
# 初始化帧计数器和打哈欠总数
25-
mCOUNTER = 0
26-
mTOTAL = 0
27-
# 行为帧数变量
28-
ActionCOUNTER = 0
29-
# 周期变量
30-
Roll = 0
31-
Rolleye = 0
32-
Rollmouth = 0
14+
15+
# 眼睛闭合判断
16+
EYE_AR_THRESH = 0.15 # 眼睛长宽比
17+
EYE_AR_CONSEC_FRAMES = 2 # 闪烁阈值
18+
19+
#嘴巴开合判断
20+
MAR_THRESH = 0.65 # 打哈欠长宽比
21+
MOUTH_AR_CONSEC_FRAMES = 3 # 闪烁阈值
22+
23+
# 定义检测变量,并初始化
24+
COUNTER = 0 #眨眼帧计数器
25+
TOTAL = 0 #眨眼总数
26+
mCOUNTER = 0 #打哈欠帧计数器
27+
mTOTAL = 0 #打哈欠总数
28+
ActionCOUNTER = 0 #分心行为计数器器
29+
30+
# 疲劳判断变量
31+
# Perclos模型
32+
# perclos = (Rolleye/Roll) + (Rollmouth/Roll)*0.2
33+
Roll = 0 #整个循环内的帧技术
34+
Rolleye = 0 #循环内闭眼帧数
35+
Rollmouth = 0 #循环内打哈欠数
3336

3437
class MainWindow(QMainWindow, Ui_MainWindow):
3538
def __init__(self):
@@ -40,6 +43,7 @@ def __init__(self):
4043

4144
def window_init(self):
4245
# 设置控件属性
46+
# 设置label的初始值
4347
self.label.setText("请打开摄像头")
4448
self.label_2.setText("疲劳检测:")
4549
self.label_3.setText("眨眼次数:0")
@@ -56,10 +60,6 @@ def window_init(self):
5660
self.actionOpen_camera.triggered.connect(CamConfig_init)
5761
# 自适应窗口缩放
5862
self.label.setScaledContents(True)
59-
# def printf(self, mes):
60-
# self.textBrowser.append(mes) # 在指定的区域显示提示信息
61-
# self.cursot = self.textBrowser.textCursor()
62-
# self.textBrowser.moveCursor(self.cursot.End)
6363

6464
# 定义摄像头类
6565
class CamConfig:
@@ -76,22 +76,34 @@ def __init__(self):
7676
self.v_timer.start(20)
7777
# 连接定时器周期溢出的槽函数,用于显示一帧视频
7878
self.v_timer.timeout.connect(self.show_pic)
79+
# 在前端UI输出提示信息
7980
Ui_MainWindow.printf(window,"载入成功,开始运行程序")
8081
Ui_MainWindow.printf(window,"")
8182
Ui_MainWindow.printf(window,"开始执行疲劳检测...")
8283
window.statusbar.showMessage("正在使用摄像头...")
8384
def show_pic(self):
8485
# 全局变量
86+
# 在函数中引入定义的全局变量
8587
global EYE_AR_THRESH,EYE_AR_CONSEC_FRAMES,MAR_THRESH,MOUTH_AR_CONSEC_FRAMES,COUNTER,TOTAL,mCOUNTER,mTOTAL,ActionCOUNTER,Roll,Rolleye,Rollmouth
86-
# 读取一帧
88+
89+
# 读取摄像头的一帧画面
8790
success, frame = self.cap.read()
8891
if success:
89-
# Mat格式图像转Qt中图像的方法
90-
#检测
91-
ret,frame = myframe.frametest(frame)
92+
# 检测
93+
# 将摄像头读到的frame传入检测函数myframe.frametest()
94+
ret,frame = myframe.frametest(frame)
9295
lab,eye,mouth = ret
93-
#行为判断
96+
# ret和frame,为函数返回
97+
# ret为检测结果,ret的格式为[lab,eye,mouth],lab为yolo的识别结果包含'phone' 'smoke' 'drink',eye为眼睛的开合程度(长宽比),mouth为嘴巴的开合程度
98+
# frame为标注了识别结果的帧画面,画上了标识框
99+
100+
# 分心行为判断
101+
# 分心行为检测以15帧为一个循环
94102
ActionCOUNTER += 1
103+
104+
# 如果检测到分心行为
105+
# 将信息返回到前端ui,使用红色字体来体现
106+
# 并加ActionCOUNTER减1,以延长循环时间
95107
for i in lab:
96108
if(i == "phone"):
97109
window.label_6.setText("<font color=red>正在用手机</font>")
@@ -104,44 +116,62 @@ def show_pic(self):
104116
if ActionCOUNTER > 0:
105117
ActionCOUNTER -= 1
106118
elif(i == "drink"):
107-
window.label_7.setText("<font color=red>正在用喝水</font>")
119+
window.label_8.setText("<font color=red>正在用喝水</font>")
108120
window.label_9.setText("<font color=red>请不要分心</font>")
109121
if ActionCOUNTER > 0:
110122
ActionCOUNTER -= 1
111-
#疲劳判断
112-
if eye < EYE_AR_THRESH: # 眼睛长宽比:0.2
123+
124+
# 如果超过15帧未检测到分心行为,将label修改为平时状态
125+
if ActionCOUNTER == 15:
126+
window.label_6.setText("手机")
127+
window.label_7.setText("抽烟")
128+
window.label_8.setText("喝水")
129+
window.label_9.setText("")
130+
ActionCOUNTER = 0
131+
132+
# 疲劳判断
133+
# 眨眼判断
134+
if eye < EYE_AR_THRESH:
135+
# 如果眼睛开合程度小于设定好的阈值
136+
# 则两个和眼睛相关的计数器加1
113137
COUNTER += 1
114138
Rolleye += 1
115139
else:
116-
# 如果连续3次都小于阈值,则表示进行了一次眨眼活动
117-
if COUNTER >= EYE_AR_CONSEC_FRAMES: # 阈值:3
140+
# 如果连续2次都小于阈值,则表示进行了一次眨眼活动
141+
if COUNTER >= EYE_AR_CONSEC_FRAMES:
118142
TOTAL += 1
119143
window.label_3.setText("眨眼次数:" + str(TOTAL))
120144
# 重置眼帧计数器
121145
COUNTER = 0
122-
if mouth > MAR_THRESH: # 张嘴阈值0.5
146+
147+
# 哈欠判断,同上
148+
if mouth > MAR_THRESH:
123149
mCOUNTER += 1
124150
Rollmouth += 1
125151
else:
126152
# 如果连续3次都小于阈值,则表示打了一次哈欠
127-
if mCOUNTER >= MOUTH_AR_CONSEC_FRAMES: # 阈值:3
153+
if mCOUNTER >= MOUTH_AR_CONSEC_FRAMES:
128154
mTOTAL += 1
129155
window.label_4.setText("哈欠次数:" + str(mTOTAL))
130156
# 重置嘴帧计数器
131157
mCOUNTER = 0
132-
if ActionCOUNTER == 15:
133-
window.label_6.setText("手机")
134-
window.label_7.setText("抽烟")
135-
window.label_8.setText("喝水")
136-
window.label_9.setText("")
137-
ActionCOUNTER = 0
158+
159+
# 将画面显示在前端UI上
138160
show = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
139161
showImage = QImage(show.data, show.shape[1], show.shape[0], QImage.Format_RGB888)
140162
window.label.setPixmap(QPixmap.fromImage(showImage))
163+
164+
# 疲劳模型
165+
# 疲劳模型以150帧为一个循环
166+
# 每一帧Roll加1
141167
Roll += 1
168+
# 当检测满150帧时,计算模型得分
142169
if Roll == 150:
170+
# 计算Perclos模型得分
143171
perclos = (Rolleye/Roll) + (Rollmouth/Roll)*0.2
172+
# 在前端UI输出perclos值
144173
Ui_MainWindow.printf(window,"过去150帧中,Perclos得分为"+str(round(perclos,3)))
174+
# 当过去的150帧中,Perclos模型得分超过0.38时,判断为疲劳状态
145175
if perclos > 0.38:
146176
Ui_MainWindow.printf(window,"当前处于疲劳状态")
147177
window.label_10.setText("<font color=red>疲劳!!!</font>")
@@ -150,11 +180,15 @@ def show_pic(self):
150180
Ui_MainWindow.printf(window,"当前处于清醒状态")
151181
window.label_10.setText("清醒")
152182
Ui_MainWindow.printf(window,"")
153-
#归零
183+
184+
# 归零
185+
# 将三个计数器归零
186+
# 重新开始新一轮的检测
154187
Roll = 0
155188
Rolleye = 0
156189
Rollmouth = 0
157190
Ui_MainWindow.printf(window,"重新开始执行疲劳检测...")
191+
158192
def CamConfig_init():
159193
window.f_type = CamConfig()
160194

13.9 KB
Binary file not shown.
5.57 KB
Binary file not shown.

mydetect.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# yolo检测的接口函数
2+
# 详细信息请参考 https://blog.csdn.net/qq_20241587/article/details/113349874?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-6.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-6.control
13

24
import numpy as np
35
import cv2
@@ -101,4 +103,8 @@ def predict(im0s):
101103
prob = round(float(conf) * 100, 2) # round 2
102104
ret_i = [label, prob, xyxy]
103105
ret.append(ret_i)
106+
# 返回信息
107+
# label 标签信息 'face' 'smoke' 'drink' 'phone'
108+
# prob 为对应的置信度
109+
# xyxy 为对应的位置信息(外框)
104110
return ret

myfatigue.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
# -*- coding: utf-8 -*-
2-
# import the necessary packages
1+
# 疲劳检测,检测眼睛和嘴巴的开合程度
2+
33
from scipy.spatial import distance as dist
44
from imutils.video import FileVideoStream
55
from imutils.video import VideoStream
@@ -80,16 +80,15 @@ def detfatigue(frame):
8080
cv2.drawContours(frame, [rightEyeHull], -1, (0, 255, 0), 1)
8181
mouthHull = cv2.convexHull(mouth)
8282
cv2.drawContours(frame, [mouthHull], -1, (0, 255, 0), 1)
83-
# 进行画图操作,用矩形框标注人脸
84-
# left = rect.left()
85-
# top = rect.top()
86-
# right = rect.right()
87-
# bottom = rect.bottom()
88-
# cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 1)
83+
8984
# 画出眼睛、嘴巴竖直线
9085
cv2.line(frame,tuple(shape[38]),tuple(shape[40]),(0, 255, 0), 1)
9186
cv2.line(frame,tuple(shape[43]),tuple(shape[47]),(0, 255, 0), 1)
9287
cv2.line(frame,tuple(shape[51]),tuple(shape[57]),(0, 255, 0), 1)
9388
cv2.line(frame,tuple(shape[48]),tuple(shape[54]),(0, 255, 0), 1)
9489

90+
# 返回信息
91+
# frame已经标注出眼睛和嘴巴的框线
92+
# eyeae为眼睛的长宽比
93+
# mouthar为嘴巴的长宽比
9594
return(frame,eyear,mouthar)

myframe.py

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,59 @@
1+
# 检测的接口函数
2+
13
import cv2
2-
import mydetect
3-
import myfatigue
4+
import mydetect #yolo检测
5+
import myfatigue #疲劳检测
46
import time
57

68
cap = cv2.VideoCapture(0)
79

810
def frametest(frame):
11+
# frame为帧输入
12+
13+
# 定义返回变量
914
ret = []
1015
labellist = []
16+
17+
# 计时开始,用于计算fps
1118
tstart = time.time()
19+
20+
# Dlib疲劳检测
21+
# eye 眼睛开合程度
22+
# mouth 嘴巴开合程度
23+
frame,eye,mouth = myfatigue.detfatigue(frame)
24+
25+
26+
# yolo检测
1227
action = mydetect.predict(frame)
1328
for label, prob, xyxy in action:
29+
# 在labellist加入当前label
1430
labellist.append(label)
31+
32+
# 将标签和置信度何在一起
1533
text = label + str(prob)
34+
35+
# 画出识别框
1636
left = int(xyxy[0])
1737
top = int(xyxy[1])
1838
right = int(xyxy[2])
1939
bottom = int(xyxy[3])
20-
cv2.putText(frame,text,(left, top-5),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 1)
2140
cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 1)
41+
42+
# 在框的左上角画出标签和置信度
43+
cv2.putText(frame,text,(left, top-5),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 1)
44+
45+
# 将信息加入到ret中
2246
ret.append(labellist)
23-
frame,eye,mouth = myfatigue.detfatigue(frame)
2447
ret.append(round(eye,3))
2548
ret.append(round(mouth,3))
26-
#print(eye,mouth)
49+
50+
# 计时结束
2751
tend = time.time()
52+
# 计算fps
2853
fps=1/(tend-tstart)
2954
fps = "%.2f fps" % fps
55+
# 在图片的左上角标出Fps
3056
cv2.putText(frame,fps,(10, 20),cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 1)
57+
58+
# 返回ret 和 frame
3159
return ret,frame

0 commit comments

Comments
 (0)