基于眨眼状态的在线疲劳检测系统(Matlab-GUI设计)

参考文档

1.参考论文:

      https://ieeexplore.ieee.org/document/8242842

2.基于Matlab的GUI开发:

Matlab-GUI编程:简单计算器的实现

3.基于Matlab的webcam读取:

       https://ww2.mathworks.cn/help/supportpkg/usbwebcams/ug/webcam.html?   searchHighlight=webcam&s_tid=doc_srchtitle

4.基于Matlab的级联分类器检测物体:https://ww2.mathworks.cn/help/vision/ref/vision.cascadeobjectdetector-system-object.html?s_tid=doc_ta

5.基于Matlab的KLT追踪算法实现:

https://ww2.mathworks.cn/help/vision/examples/face-detection-and-tracking-using-the-klt-algorithm.html?searchHighlight=klt&s_tid=doc_srchtitle

6.更多Matlab函数请参考:

https://ww2.mathworks.cn/help/

 

设计GUI界面

基于Matlab的疲劳监测系统GUI如下:

一、启动GUI的初始化

更新device.String为可用webcam设备列表

OpeningFcn函数下实现:

% 更新device.String
handles.device.String=webcamlist;
% 更新handles
guidata(hObject, handles);

二、按照使用步骤,主要介绍以下几个组件功能:

①device:单击菜单返回可用网络摄像头设备列表,并选择设备。(关于webcam使用请参考matlab帮助文档)

%% device_Callback中实现如下:

% handles下添加webcam对象,设备由device.String指定
handles.cam = webcam(hObject.String{hObject.Value});
% 更新handles
guidata(hObject, handles);
% 对下拉菜单resolution的String更新当前设备支持分辨率列表
handles.resolution.String=handles.cam.AvailableResolutions;

 

②resolution:单击选择当前设备的拍摄分辨率

resolution_Callback中实现如下:

% 指定当前设备的拍摄分辨率
handles.cam.Resolution=hObject.String{hObject.Value};

 

 

③preview:显示视频流,并使用级联分类器捕捉人脸,人眼并标注。

该部分内容主要函数有:matlab视觉工具箱中经典人脸检测算法之级联分类器:vision.CascadeObjectDetector

                                图像滤波:imfilter

                                包围盒标注:insertObjectAnnotation 或者 insertShape

需要注意的是preview的回调函数同时也是本系统的Main函数,之后操作多数在该回调里执行,通过读取其他组件信息,控制下一步行动

% preview_Callback中实现如下:

handles.win.String='The Cam has been conformed! Showing now.';
% 定义人脸检测器
faceDetector = vision.CascadeObjectDetector;
% EyePairSmallDetector = vision.CascadeObjectDetector('EyePairSmall');
% 定义左眼检测器并指定在ROI下检测
EyePairSmallDetector = vision.CascadeObjectDetector('LeftEyeCART');
EyePairSmallDetector.UseROI=true;
% 选定1号坐标窗口
axes(handles.axes1);
% 高斯滤波卷积核
h3_5=fspecial('gaussian',3,0.5);

%网络摄像头运行正常(显示视频流) 
while isempty(handles.cam)==0
    if handles.exit.Value==1
       break;
    end
    
   % 获取当前捕捉画面
   shot=snapshot(handles.cam);
   % 对画面高斯滤波
   shot=imfilter(shot,h3_5);
   
   % 在未使用klt追踪前,正常拍摄并捕捉人眼
   if handles.klt.Value==0
          % 定位人脸区域,并获取包围盒
         [~,facebbox,~]=shotfacetrack(shot,faceDetector,[120 120],1); 
          % 如果检测到人脸
         if isempty(facebbox)==0
             % 标记人脸区域
              detectedshot = insertObjectAnnotation(shot,'rectangle',facebbox,'Face','LineWidth',2); 
             % 定位人眼区域,并返回包围盒
              eyesbbox=EyePairSmallDetector(shot,facebbox(1,:));
             % 如果检测到人眼
             if isempty(eyesbbox)==0
                 % 标记人眼区域
                 detectedshot = insertObjectAnnotation(detectedshot,'rectangle',eyesbbox(1,:),'Eyes','LineWidth',2,'Color','blue');                 
             end
             % 显示和刷新
            imshow(detectedshot);
            drawnow;
        end     
   else
       break;
   end 
    
end

 

 

④ klt:光流追踪人眼

      该部分内容采用KLT算法实现对人眼区域的稳定追踪(关于KLT实现可参考另一篇博客:https://blog.csdn.net/Ephemeroptera/article/details/84452439),在klt成功追踪人眼区域之后,我们将抠出人眼并采取一系列图像处理:伽马增强+二值化反色处理+形态学闭运算(平滑连通部分)+提取最大连通域(虹膜区域)+计算虹膜均值中心。

    在提取虹膜的二值化过程中,为了适应不同光照下提取的鲁棒性,我们可以通过gamma和thresh滑动条设置gamma系数和二值化的阈值,来更好地提取可靠的虹膜区域。

该部分使用的函数主要有:特征点提取:detectMinEigenFeatures(其他特征点提取可参考图像特征点检测

                                            光流追踪:vision.PointTracker、initialize()、setPoints()

                                            几何运动估计:estimateGeometricTransform

                                           点的几何变换:transformPointsForward

                                          图像处理:imadjust(图像增强)、imbinarize(图像二值化)、imclose(形态学闭运算)、bwlabel(连通域提取)

%% preview回调函数同时也是main函数,故大部分操作仍在该回调中执行
%% KLT光流追踪
handles.win.String='The inital ROI to eyes has been located! Starting KLT-Track now .';
bboxPoints = bbox2points(eyesbbox(1, :));
points = detectMinEigenFeatures(rgb2gray(shot), 'ROI', eyesbbox(1,:));
imshow(detectedshot),hold on,plot(points),hold off;
% klt准备工作:提取眼部特征点并初始化
pointTracker = vision.PointTracker('MaxBidirectionalError', 2);
points = points.Location;
initialize(pointTracker, points, shot);
oldPoints = points;
       
EARdata=[];
numOfsamples=0;
recent=1;
RECORD=zeros(20,1);
se=strel('disk',1);

%网络摄像头运行正常(获取视频流,开始追踪)
while isempty(handles.cam)==0
   if handles.exit.Value==1
        break;
   end
    % 获取当前捕捉画面
    shot=snapshot(handles.cam);
    % 获取可疑点
    [points, isFound] = step(pointTracker, shot);
    % 获取当前可靠追踪点
     visiblePoints = points(isFound, :);
    % 获取可靠点上一帧位置 
     oldInliers = oldPoints(isFound, :);
     % need at least 2 points
      if size(visiblePoints, 1) >= 2
         % 获取新旧点之间的几何变换映射
         [xform, oldInliers, visiblePoints] = estimateGeometricTransform(oldInliers, visiblePoints, 'similarity', 'MaxDistance', 4);
         % 对包围盒进行同样映射,保持包围盒的追踪同步
         bboxPoints = transformPointsForward(xform, bboxPoints);      
         bboxPolygon = reshape(bboxPoints', 1, []);
         rectangeBbox = rectbbox(bboxPoints);
         % 获取校正后的bbox,便于裁取人眼
         a= rectangeBbox(1);
         b= rectangeBbox(2);
         deltaX= rectangeBbox(3);
         deltaY= rectangeBbox(4);
         rectangeBbox= round([a+0.1*deltaX,b+0.5*deltaY,0.7*deltaX,0.4*deltaY]);
         % 裁取人眼
         eyes=imresize(imcrop(shot,rectangeBbox),[35 35]);
         % 显示原始图像
         axes(handles.axes3);
          imshow(eyes);
          drawnow;
         % 伽马增强+反色处理+形态学闭运算平滑连通域
         axes(handles.axes4);
         eyes_gamma=imadjust(eyes,[],[],handles.gamma.Value);
         eyesBin=1-imbinarize(rgb2gray(eyes_gamma),handles.thresh.Value);
         eyesBin=imclose(eyesBin,se);
         imshow(eyesBin);
         drawnow;
         % 提取最大连通域即人眼的虹膜区域
         axes(handles.axes5);
            % 提取最大连通域
         [L num]=bwlabel(eyesBin,8);
         maxL=mode(L(L~=0));
         eyesBin(find(L~=maxL))=0;
            % 提取虹膜的矩形边界
         [r c]=find(eyesBin==1);
         minX=min(c);
         maxX=max(c);
         minY=min(r);
         maxY=max(r);
         if max(eyesBin)==0
              minX=16;
              maxX=16;
              minY=16;
              maxY=16;
         end
         % 计算虹膜区域的均值中心
         Xmean=mean([minX,maxX]);
         Ymean=mean([minY,maxY]);
         % 绘制定位经纬线
         target=eyesBin;
         target(:,minX)=1;
         target(:,maxX)=1;
         target(minY,:)=1;
         target(maxY,:)=1;
         imshow(target);
         hold on,plot(Xmean,Ymean,'Marker','*','MarkerSize',13,'MarkerEdgeColor','r','MarkerFaceColor','r'),hold off;
         % 显示klt追踪
         axes(handles.axes1);   
         shot = insertShape(shot, 'Rectangle', rectangeBbox,'LineWidth', 2,'Color','green');
         shot = insertShape(shot, 'Polygon', bboxPolygon,'LineWidth', 2);
         shot = insertMarker(shot, visiblePoints, '+', 'Color', 'white');
         imshow(shot);
          drawnow;
         % 新旧追踪点轮替
         oldPoints = visiblePoints;
         setPoints(pointTracker, oldPoints);
      end

 

 

⑧auto:该组件命令将自动检测眨眼并检测疲劳

眨眼检测:

基于眼部的landmarks分析眨眼信息(Real-Time Eye Blink Detection using Facial Landmarks)的论文中提出眼睛开闭系数EAR来判断眼睛的开闭程度。

                                                                    

作者通过分析眼部轮廓特征点的纵横比来分析睁眼情况,对于提取的虹膜区域,我们采用简单的高度信息来表示眼睛开闭特征。考虑到眼睛大小因人而异,因此不宜使用一个恒定阈值来判定眨眼情况。首先我们先采样100次虹膜高度信息(包含眨眼动作),我们采用kmeans方法对其分为两类并分别计算类别中心。然后在实时眨眼检测时,计算当前虹膜高度与类别中心的距离可以区分两种状态。

疲劳检测:

由于聚类算法未能给出睁眼和闭眼状态的标签。因此我们将两个类别中心的均值作为疲劳判别的阈值,分析当前帧是否为疲劳帧,采用时间窗分析的形式分析20帧内情况疲劳帧的个数,当fatigue_frames > fatigue_coefficient * 20 的时候即可判定疲劳状态,当然考虑到不同人眨眼持续时长不同,可以滑动条手动设置疲劳系数 fatigue_coefficient

该部分主要函数有:kmeans(聚类)

                               pdist(欧式距离计算)

%% preview回调函数同时也是main函数,故大部分操作仍在该回调中执行
%% 疲劳检测
              % 记录最近眨眼情况
              RECORD(recent)=EAR;
              if recent==20
                axes(handles.axes6);
                  axis([1 20 0 max(Center)+10]);
                  stem(RECORD);
                  hold on,plot([0 20],[Center(1,:),Center(1,:)],'LineWidth',2,'Color','r'),...
                  plot([0 20],[Center(2,:),Center(2,:)],'LineWidth',2,'Color','blue');
                  plot([0 20],[Average Average],'--','LineWidth',2,'Color','green');
                  text(20,Center(1,:),'open');
                  text(20,Center(2,:),'close');
                  text(20,Average,'thresh');
                  hold off;
                  recent=0;
                  weak=numel(RECORD(RECORD<Average));
                  if weak>=round(handles.fatigueCoef.Value*20)
                      handles.fatigue.String='疲劳';
                  else
                      handles.fatigue.String='正常';
                  end
              end
              recent=recent+1;
           end

收集样本:

聚类成功,眨眼分析:

疲劳检测:

おすすめ

転載: blog.csdn.net/Ephemeroptera/article/details/92149411