传统方法眼角定位和dilb 进行疲劳检测(眨眼检测)

安装第三方库文件

  1. opencv
  2. Dlib,安装方法见https://www.learnopencv.com/install-opencv-3-and-dlib-on-windows-python-only/ 
  3. Numpy
  4. Imutils (一系列使得opencv 便利的功能,包括图像旋转、缩放、平移,骨架化、边缘检测、显示matplotlib 图像(imutils.opencv2matplotlib(image))

传统方法进行眼角定位

思路是:

1、利用opencv 自带训练好的 haarcascade_frontalface_default.xml 和 haarcascade_eye.xml

来检测脸部和眼睛

以脸部加载xml为例:

face=cv2.CascadeClassifier('haarcascade_frontalface_default.xml')

face.load(脸部xml的完整路径)

以上是加载xml方法,有很多介绍直接是face=cv2.CascadeClassifier(脸部xml的完整路径),通常会报错。

2、加载完xml 文件后,下一步就是detect,opencv 函数为detectMultiScale(img,scaleFactor,minNeighbors,minsize,maxsize)

scaleFactor 是压缩率,越小金字塔数目越多,相应的检测准确相对好点

minNeighbors 是同一张脸至少被检测n次才认为是真正的

Minszie以元组方式,(检测眼睛时候会用到,避免鼻子和其他部位产生的假阳性)

3、先检测脸,然后再脸的基础上再检测眼睛,到了这一步后,就是自己设计检测的眼角方法:

     1)找轮廓,取最大轮廓

      2)凸缺陷检测,得到所有凸点

      3)眼角如何确定?----将凸点的x,y值相加,排序,最小的为左边眼角,最大的为右边眼角(见测试图)

dlib 疲劳检测

  1. 下载shape_predictor_68_face_landmarks.dat 文件,这是68个眼部特征点
  2. 初始化检测器和预测器
detector=dlib.get_frontal_face_detector()

predictor = dlib.shape_predictor(“dat文件路径”)

     3 如何确定疲劳?

          1) 计算眼睛的宽高比

          2)当前帧两双眼睛宽高比与前一帧的差值的绝对值大于0.2,则认为是疲劳

程序如下:

# -*- coding: utf-8 -*-
import os
import numpy as np
import cv2
import dlib
import sys
from imutils import face_utils
class fatigue(object):
    def __init__(self,file_dictory,landmask_path,facehaar_path,eyehaar_path):
        self.file=os.path.abspath(str(file_dictory))
        os.chdir(self.file)
        self.roi_face=[]
        self.roi_eye=[]
#        self.predictor_path=r'C:\Users\Y\Desktop\shape_predictor_68_face_landmarks.dat'
        self.predictor_path=os.path.abspath(str(landmask_path))
#        self.face_haar_path=r'E:\opencv\opencv\sources\data\haarcascades\haarcascade_frontalface_default.xml'
        self.face_haar_path=os.path.abspath(str(facehaar_path))
#        self.eye_haar_path=r'E:\opencv\opencv\sources\data\haarcascades\haarcascade_eye.xml'
        self.eye_haar_path=os.path.abspath(str(eyehaar_path))
    def detect_face(self):
        face=cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
        face.load(self.face_haar_path)
        i=1
        for f in os.listdir(self.file):
            face_image=cv2.imread(f)
            face_image=cv2.medianBlur(face_image,3)
            if face_image.ndim==3:
                face_image_gray=cv2.cvtColor(face_image,cv2.COLOR_BGR2GRAY)
            else:
                face_image_gray=face_image
            faces=face.detectMultiScale(face_image_gray,1.3,5)
            if len(faces)!=0:
                for (x,y,w,h) in faces:
                    self.roi_face.append(face_image[y:y+h,x:x+w,:])
#                    cv2.imwrite(self.file+"\\%g.jpg"%i,face_image_gray[y:y+h,x:x+w])
                    i+=1
        print("人脸数%g"%len(self.roi_face))
    def detect_eye(self):
        eye=cv2.CascadeClassifier('haarcascade_eye.xml')
        eye.load(self.eye_haar_path)
        i=1
        for face in self.roi_face:
            eyes=eye.detectMultiScale(face,1.03,20,0,(40,40))#(40,40)限制眼睛搜索最小尺寸,避免鼻子或者其他的阴影产生的假阳性
            if len(eyes)!=0:
                for (x,y,w,h) in eyes:
                    self.roi_eye.append(face[y:y+h,x:x+w,:])
#                    cv2.imwrite(self.file+"\\%g.jpg"%i,face[y+10:y+h,x+10:x+w,:])
                    i+=1
        print("眼睛个数%g"%len(self.roi_eye))
    #传统
    def feature_eye(self):
        i=1
        for e in self.roi_eye:
            e_g=cv2.cvtColor(e,cv2.COLOR_BGR2GRAY)
            _,thresh=cv2.threshold(e_g,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
            _,cnts,h=cv2.findContours(thresh,0,1)
            cnt_max=sorted(cnts,key=lambda x:cv2.contourArea(x),reverse=True)[0]
            con_hull=cv2.convexHull(cnt_max)
            hull_index=cv2.convexHull(cnt_max,returnPoints = False)
            defects = cv2.convexityDefects(cnt_max,hull_index)
            temp=[]
            point=[]
            for j in range(defects.shape[0]):
                _,_,f,d=defects[j,0]
                point.append(tuple(cnt_max[f][0]))
            for t in point:
                temp.append(sum(t))
            index=np.argsort(temp)
            close=point[index[0]]#两个眼角,colse,far
            far=point[index[-1]]
#            np.sort()
            cv2.circle(e,close,5,(0,255,0),-1)
            cv2.circle(e,far,5,(0,255,0),-1)
            cv2.drawContours(e,[con_hull],0,(0,0,255),2)
            cv2.putText(e,str(cv2.contourArea(cnt_max)),(10,10),cv2.FONT_HERSHEY_SIMPLEX,0.5,(255,0,0))
            cv2.imwrite(self.file+"\\%g.jpg"%i,e)
            i+=1
    def dlib_detect(self):
        detector=dlib.get_frontal_face_detector()
        predictor = dlib.shape_predictor(self.predictor_path)
        cap=cv2.VideoCapture(0)#也可以其它视频,打开本地摄像头
        if cap.isOpened() is False:
            raise("IO error") #抛出异常
        cap.set(cv2.CAP_PROP_FPS,60)
#        cv2.namedWindow("Capture", cv2.WINDOW_NORMAL)
        forward_left_eye_ratio=None
        forward_right_eye_ratio=None
        flag=0 #根据faces个数来初始化forward_left....
        while 1:
            ret,frame=cap.read()
            frame=cv2.medianBlur(frame,3)
#            frame=cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
            if ret==False:
                sys.exit()
            faces=detector(frame,1)#1 代表图像升采样一次,以便我们检测更多的人脸
            if len(faces)>0:
                 if flag==0: #以第一帧检测到人脸个数为准,也就是程序每一帧检测到人脸个数相同,不然有错
                     temp=np.zeros((len(faces),1)) #初始化成一个数组
                     forward_left_eye_ratio,forward_right_eye_ratio=temp,temp
            else:
#                sys.exit()
#                print("当前帧人脸消失,退出")
                print("当前帧人脸消失,继续下一帧")
#                break
                continue
            flag=1 #flag=1,标识着第二帧的时候不再预分配内存,temp            
            if len(faces)>0:
                for i,d in enumerate(faces):
                    cv2.rectangle(frame,(d.left(),d.top()),(d.right(),d.bottom()),(0,255,0),2)
                    cv2.putText(frame,str(i+1),(d.left()-10,d.top()-10),cv2.FONT_HERSHEY_SIMPLEX,0.5,(0,0,255),2)
                    shape=predictor(frame,d)#检测68特征点
#                    print(shape,type(shape))
                    points = face_utils.shape_to_np(shape) #将脸部特征点转为坐标(x,y)
                    long_left=self.distance(points[36,:],points[39,:])
                    short_left=self.distance((points[37,:]+points[38,:])/2,(points[41,:]+points[40,:])/2)
                    long_right=self.distance(points[42,:],points[45,:])
                    short_right=self.distance((points[43,:]+points[44,:])/2,(points[46,:]+points[47,:])/2)
                    if forward_left_eye_ratio[i]==0 and forward_right_eye_ratio[i]==0:#取第一帧的人脸眼睛宽高比,取完后,进行下一帧,continue 后的语句不再执行
                        forward_left_eye_ratio[i]=short_left/long_left
                        forward_right_eye_ratio[i]=short_right/long_right
                        continue #跳转下一个人脸眼睛的宽高比
                    ##下一帧
                    left_eye_ratio_now=np.zeros((forward_left_eye_ratio.shape))
                    right_eye_ratio_now=np.zeros((forward_right_eye_ratio.shape))
                    left_eye_ratio_now[i]=short_left/long_left
                    right_eye_ratio_now[i]=short_right/long_right
                    print("第%g个人脸当前左眼宽高比与前一帧的差值:%g"%(i+1,abs(left_eye_ratio_now[i]-forward_left_eye_ratio[i])))
                    if abs(left_eye_ratio_now[i]-forward_left_eye_ratio[i])>0.2:
                        print("第%g个人左眼变化%g"%(i+1,abs(left_eye_ratio_now-forward_left_eye_ratio)))
                    if abs(right_eye_ratio_now[i]-forward_right_eye_ratio[i])>0.2:
                        print("第%g个人左眼变化%g"%(i+1,abs(right_eye_ratio_now-forward_right_eye_ratio)))
                    if abs(left_eye_ratio_now[i]-forward_left_eye_ratio[i])>0.2 and abs(right_eye_ratio_now[i]-forward_right_eye_ratio[i])>0.2:
                        print("%g号先生您很疲劳了,请注意休息"%(i+1))
                    forward_left_eye_ratio[i]=left_eye_ratio_now[i]
                    forward_right_eye_ratio[i]=right_eye_ratio_now[i]
            cv2.imshow("Capture",frame)
            k=cv2.waitKey(10)
            if k==27:
                break
        cap.release()
        cv2.destroyAllWindows()
    def distance(self,p1,p2):
        return np.sqrt(np.sum((p1-p2)*(p1-p2)))
                         
if __name__=="__main__":
#    param=sys.argv[1]
    print("cmd---运行格式--python '****.py' 图像demo1文件夹路径 68_face_landmarks.dat路径 haarcascade_frontalface_default.xml路径 haarcascade_eye.xml路径")
    if len(sys.argv)!=5:
        print("参数不够")
#    fold_param=r'C:\Users\Y\Desktop\demo1'
#    fatigue_drive=fatigue(fold_param)
    fatigue_drive=fatigue(sys.argv[1],sys.argv[2],sys.argv[3],sys.argv[4])
    print("**********传统方法人眼角定位*************\n")
    fatigue_drive.detect_face()
    fatigue_drive.detect_eye()
    fatigue_drive.feature_eye()
    print("*************疲劳测试************\n")
    fatigue_drive.dlib_detect()
                

测试图:

    

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值