Python项目开发实例之眨眼检测实验报告

一、实验要求:

检测眨眼次数。要求做成一个pyqt5 GUI 界面,点击按键“开始”时,就开始显示视频帧,并检测图像中是否有人的眨眼动作,有眨眼时将眨眼次数加1,并显示在图像的左上角位置。点击按键“停止”时,就停止显示视频帧。

二、设计思路描述

为了构建我们的眨眼检测器,我们将计算一个称为 眼睛纵横比(EAR) 的指标。当确定视频流中是否发生眨眼时,我们需要计算眼睛的长宽比。如果眼睛长宽比低于一定的阈值,然后超过阈值,那么我们将记录一个“眨眼” - EYE_AR_THRESH 是这个阈值,我们默认它的值为 0.3,您也可以为自己的应用程序调整它。另外,我们有一个重要的常量,EYE_AR_CONSEC_FRAME ,这个值被设置为 3,表明眼睛长宽比小于3时,接着三个连续的帧一定发生眨眼动作。同样,取决于视频的帧处理吞吐率,数字大小可以自己调控。

  1. 利用opencv首先对每一帧进行人脸检测
  2. 检测眼睛的区域的六个特征点,计算EAR,这些点在眼睛开和闭时有所不同,可用来检测
  3. 设置阈值,比较EAR和阈值的大小,当EAR大小小于阈值时,判定接连若干帧一定发生眨眼动作,使计数器数量加一

三、关键代码

class Wink_Window(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Wink_Window, self).__init__(parent)
        self.timer_camera = QtCore.QTimer()
        self.cap = cv2.VideoCapture()
        self.cam_num = 0
        self.set_ui()
        self.slot_init()
        self.__flag_work = 0
        self.x = 0
        self.count = 0
        # 调用人脸特征点检测模型
        self.shape_detector_path = '../shape_predictor_68_face_landmarks.dat'
        # 人脸检测器
        self.detector = dlib.get_frontal_face_detector()
        # 人脸特征点检测器
        self.predictor = dlib.shape_predictor(self.shape_detector_path)
        self.EYE_AR_THRESH = 0.35  # 设置阈值
        # 当EAR大小小于阈值时,接连多少帧一定发生眨眼动作
self.EYE_AR_CONSEC_FRAMES = 8  
        # 对应特征点的序号
        self.RIGHT_EYE_START = 37 - 1
        self.RIGHT_EYE_END = 42 - 1
        self.LEFT_EYE_START = 43 - 1
        self.LEFT_EYE_END = 48 - 1
        self.face =True

    def set_ui(self):
        self.__layout_main = QtWidgets.QHBoxLayout()
        self.__layout_fun_button = QtWidgets.QVBoxLayout()
        self.__layout_data_show = QtWidgets.QVBoxLayout()
        self.button_open_camera = QtWidgets.QPushButton(u'开始检测')
        self.button_close = QtWidgets.QPushButton(u'停止检测')
        # Button 的颜色修改
        button_color = [self.button_open_camera, self.button_close]
        for i in range(2):
            button_color[i].setStyleSheet("QPushButton{color:black}"
                                          "QPushButton:hover{color:red}"
                                          "QPushButton{background-color:rgb(78,255,255)}"
                                          "QPushButton{border:2px}"
                                          "QPushButton{border-radius:10px}"
                                          "QPushButton{padding:2px 5px}")
        # move()方法移动窗口在屏幕上的位置到x = 500,y = 300坐标。
        self.move(500, 300)
        # 信息显示
        self.button_open_camera.setMinimumHeight(50)
        self.button_close.setMinimumHeight(50)
        self.label_show_camera = QtWidgets.QLabel()
        self.label_move = QtWidgets.QLabel()
        self.label_move.setFixedSize(100, 50)
        self.label_show_camera.setFixedSize(641, 481)
        self.label_show_camera.setAutoFillBackground(False)
        self.__layout_fun_button.addWidget(self.button_open_camera)
        self.__layout_fun_button.addWidget(self.button_close)
        self.__layout_fun_button.addWidget(self.label_move)
        self.__layout_main.addLayout(self.__layout_fun_button)
        self.__layout_main.addWidget(self.label_show_camera)
        self.setLayout(self.__layout_main)
        self.label_move.raise_()
        self.setWindowTitle(u'眨眼检测')

    def slot_init(self):
        self.button_open_camera.clicked.connect(self.button_open_camera_click)
        self.timer_camera.timeout.connect(self.show_camera)
        self.button_close.clicked.connect(self.close)

    def button_open_camera_click(self):
        if self.timer_camera.isActive() == False:
            self.frame_counter = 0  # 连续帧计数
            self.blink_counter = 0  # 眨眼计数
            flag = self.cap.open(self.cam_num)
            if flag == False:  # 错误警告
                msg = QtWidgets.QMessageBox.warning(self, u"⚠警告", u"请检测相机是否连接正确",
                                                    buttons=QtWidgets.QMessageBox.Ok,
                                                    defaultButton=QtWidgets.QMessageBox.Ok)
            else:
                self.timer_camera.start(30)
                self.button_open_camera.setText(u'关闭摄像头')
        else:
            self.timer_camera.stop()
            self.cap.release()
            self.label_show_camera.clear()
            self.button_open_camera.setText(u'打开摄像头')

    def show_camera(self):
        flag, self.image = self.cap.read()
        if self.face:
             self.detect_eyes()
        self.show = cv2.resize(self.image, (640, 480))
        self.show = cv2.cvtColor(self.show, cv2.COLOR_BGR2RGB)
        showImage = QtGui.QImage(self.show.data, self.show.shape[1],
                                 self.show.shape[0],
QtGui.QImage.Format_RGB888)
        self.label_show_camera.setPixmap(QtGui.QPixmap.fromImage(showImage))

    def detect_eyes(self):  #寻找并绘出两眼轮廓
        gray = cv2.cvtColor(self.image, cv2.COLOR_BGR2GRAY)
        rects = self.detector(gray, 0)  # 人脸检测
        for rect in rects:  # 遍历每一个人脸
            shape = self.predictor(gray, rect)  # 检测特征点
            points = face_utils.shape_to_np(shape)
            leftEye = points[self.LEFT_EYE_START:self.LEFT_EYE_END + 1]  # 取出左眼对应的特征点
            rightEye = points[self.RIGHT_EYE_START:self.RIGHT_EYE_END + 1]  # 取出右眼对应的特征点
            leftEAR = self.eye_aspect_ratio(leftEye)  # 计算左眼EAR
            rightEAR = self.eye_aspect_ratio(rightEye)  # 计算右眼EAR
            ear = (leftEAR + rightEAR) / 2.0  # 求左右眼EAR的均值
            leftEyeHull = cv2.convexHull(leftEye)  # 寻找左眼轮廓
            rightEyeHull = cv2.convexHull(rightEye)  # 寻找右眼轮廓
            cv2.drawContours(self.image, [leftEyeHull], -1, (0, 255, 0), 1)  # 绘制左眼轮廓
            cv2.drawContours(self.image, [rightEyeHull], -1, (0, 255, 0), 1)  # 绘制右眼轮廓
            # 只有连续帧计数超过EYE_AR_CONSEC_FRAMES时才会计做一次眨眼
            if ear < self.EYE_AR_THRESH:
                self.frame_counter += 1
            else:
                if self.frame_counter >= self.EYE_AR_CONSEC_FRAMES:
                    self.blink_counter += 1
                self.frame_counter = 0
            # 在图像上显示出眨眼次数blink_counter
            cv2.putText(self.image, "blink_counts:{0}".format(self.blink_counter), (250, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
    def eye_aspect_ratio(self,eye):
        A = distance.euclidean(eye[1], eye[5])
        B = distance.euclidean(eye[2], eye[4])
        C = distance.euclidean(eye[0], eye[3])
        ear = (A + B) / (2.0 * C)
        return ear

四、实验结果

开始运行:

运行结果初始状态

五、经验和教训

当确定视频流中是否发生眨眼时,我们需要计算眼睛的长宽比。所以当检测眨眼的时候会受到很多外界因素的影响,例如光线、眨眼速度、摄像头帧率、清晰度等。

而在实验过程中遇到的第一个问题就是下载安装dlib:

报错:is not a supported wheel on this platform.

原因:安装的不是对应python版本的库

解决办法:在cmd后中输入
①python
②import pip
③print(pip.pep425tags.get_supported())
即可以获取到pip支持的文件名还有版本。在pip 安装中必须要确认自己的版本信息,对应好再安装。

注:检测时眨眼放慢点,那样效果会好一点。眼镜反光也可能会导致检测眼睛轮廓时出错,计算出的结果也会是错的。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值