人脸识别之疲劳检测(二)阈值法、KNN分类和K-means聚类

Table of Contents

1、均值法

2、中值法

3、KNN

4、K-means


结合上一节在获得人眼特征点后需要对睁眼闭眼状态做出判断,方法的选择需要经验结合公平的评价方法,使用大量测试集得到不同方法下的精确度并做出比较:

1、均值法

50帧睁眼数据取均值,得到不同阈值下精确度。

2、中值法

50帧睁眼数据取中值,得到不同阈值下精确度。

3、KNN

KNN是一种ML常用分类算法,通过测量不同特征值之间的距离进行分类。它的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别,其中K通常是不大于20的整数。KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。

下面通过一个简单的例子说明一下:如下图,绿色圆要被决定赋予哪个类,是红色三角形还是蓝色四方形?如果K=3,由于红色三角形所占比例为2/3,绿色圆将被赋予红色三角形那个类,如果K=5,由于蓝色四方形比例为3/5,因此绿色圆被赋予蓝色四方形类。

KNN算法的思想:就是在训练集中数据和标签已知的情况下,输入测试数据,将测试数据的特征与训练集中对应的特征进行相互比较,找到训练集中与之最为相似的前K个数据,则该测试数据对应的类别就是K个数据中出现次数最多的那个分类,其算法的描述为:

1)计算测试数据与各个训练数据之间的距离;

2)按照距离的递增关系进行排序;

3)选取距离最小的K个点;

4)确定前K个点所在类别的出现频率;

5)返回前K个点中出现频率最高的类别作为测试数据的预测分类。


针对疲劳检测问题:将50帧睁眼、50闭眼数据进行训练,用1600张图像做测试,得到不同K值时的精确度。

KNN方法适用于训练集较小,因为每次predict都需要遍历训练集,当然训练集较大的话可以用KD-Tree KNN,减少搜索时间。

OpenCV提供了KNN算法接口,支持Mat数据作为训练集和测试集,要求训练集和测试集Mat形式相同,OpenCV设了很多assert,不支持直接给你抛出异常,需要训练前将特征数据标准化。不过网上只提供了标准输入参数,阅读完接口可以根据具体数据做一些转换,比如要训练的数据是一维的,把Vector直接放进Mat也可以,有了这接口可以少敲不少代码。下附KNN法主要代码:

//KNN clarify eye status
    cv::Mat train_data;
    cv::Mat train_labels;;   //特征
    int train_num = 50;

    for(int i = 0; i < train_num ; i++)
    {
        train_data.push_back(left_eye_ratio_[i]);  //序列化后放入data
        train_labels.push_back(1);  //对应的标注 睁眼1
    }
    for(int i = left_eye_ratio_.size()/2; i < (left_eye_ratio_.size()/2 + train_num); i++)
    {
        train_data.push_back(left_eye_ratio_[i]);  //序列化后放入data
        train_labels.push_back(0);  //对应的标注 闭眼0
    }
    //使用KNN算法    
    //cv::Ptr<cv::ml::TrainData> tData = cv::ml::TrainData::create(train_data, cv::ml::ROW_SAMPLE, train_labels);

    for(int K = 3;K <= 8;K += 1) {

        float count_left_eye = 0;
        float left_accuracy;
        float count_right_eye = 0;
        float right_accuracy;

        cv::Ptr<cv::ml::KNearest> model = cv::ml::KNearest::create();
        model->setDefaultK(K);
        model->setIsClassifier(true);
        //2nd KDTREE-KNN 训练集很大
        model->setAlgorithmType(cv::ml::KNearest::BRUTE_FORCE);

        model->train(train_data, cv::ml::ROW_SAMPLE, train_labels);
        
        cout<< "K ="<< K<<"  ";
        for(int i = 0; i< left_eye_ratio_.size()/2; i++) {
//            if(left_eye_ratio_[i] > FACTOR*eyeMouthStatus.GetLeftEyeThresh()) {
//                count_left_eye++;
//            }
            cv::Mat test_Mat;
            test_Mat.push_back(left_eye_ratio_[i]);
            float cnn_num = model->predict(test_Mat);
            //cout << i <<"CNN predict"<< cnn_num << "  ";
            if(cnn_num == 1)
            {
                count_left_eye++;
            }
        }
        for(int i = left_eye_ratio_.size()/2; i< left_eye_ratio_.size(); i++) {
            cv::Mat test_Mat;
            test_Mat.push_back(left_eye_ratio_[i]);
            float cnn_num = model->predict(test_Mat);
//            if(left_eye_ratio_[i] < FACTOR*eyeMouthStatus.GetLeftEyeThresh()) {
//                count_left_eye++;
//            }
            if(cnn_num == 0)
            {
                count_left_eye++;
            }
        }
        left_accuracy = count_left_eye/left_eye_ratio_.size();
        cout<<"left_accuracy"<<left_accuracy<<"  ";

        for(int i = 0; i< right_eye_ratio_.size()/2; i++) {
            cv::Mat test_Mat;
            test_Mat.push_back(right_eye_ratio_[i]);
            float cnn_num = model->predict(test_Mat);
            if(cnn_num == 1)
            {
                count_right_eye++;
            }
        }
        for(int i = right_eye_ratio_.size()/2; i< right_eye_ratio_.size(); i++) {
            cv::Mat test_Mat;
            test_Mat.push_back(right_eye_ratio_[i]);
            float cnn_num = model->predict(test_Mat);
            if(cnn_num == 0)
            {
                count_right_eye++;
            }
        }
        right_accuracy = count_right_eye/right_eye_ratio_.size();
        cout<<"right_accuracy"<<right_accuracy<<endl;
    }

三种方法对比效果如下图:其中K取值3-8效果相同,横轴为test1、2不同阈值,纵轴为对应精确度。

4、K-means

K-means顾名思义,K表示需要聚成的K个类,means指对象均值。

K-MEANS算法是输入聚类个数k,以及包含 n个数据对象的数据库,输出满足方差最小标准k个聚类的一种算法。k-means 算法接受输入量 k ;然后将n个数据对象划分为 k个聚类以便使得所获得的聚类满足:同一聚类中的对象相似度较高;而不同聚类中的对象相似度较小。

基本步骤
(1) 从 n个数据对象任意选择 k 个对象作为初始聚类中心;
(2) 根据每个聚类对象的均值(中心对象),计算每个对象与这些中心对象的距离;并根据最小距离重新对相应对象进行划分;
(3) 重新计算每个(有变化)聚类的均值(中心对象);
(4) 计算标准测度函数,当满足一定条件,如函数收敛时,则算法终止;如果条件不满足则回到步骤(2)。


用K-means方法处理眼睛的睁闭状态在实际应用中取得了非常好的效果,主要的优点有:校准时可以不用区分睁闭眼状态,使得算法兼容性好,而前三种方法在实际使用时往往会使用错误的数据进行训练;对个体自适应情况好,对不同个体能自适应较好的睁闭眼区间值。这部分代码就不贴了,主要还是用OpenCV自带的算法,步骤可以参考上一节KNN,参数调整下,OK了。

版权声明:本文为aaron19890330原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/aaron19890330/article/details/81328060

智能推荐

2018.8.27

2018.8.27...

HTML 表单元素的基本样式

HTML 表单元素的基本样式 原创 ixygj197875 发布于2018-02-22 17:48:53 阅读数 2296 收藏 更新于2018-05-20 15:35:58 分类专栏: 揭秘 CSS 揭秘 CSS 收起 表单元素主要包括 label、input、textarea、select、datalist、******、progress、meter、output等,以及对表单元素进行分组的 ...

php输出语句

php输出语句 常见的输出语句 echo(): 可以一次输出多个值,多个值之间用逗号分隔。echo是语言结构(language construct),而并不是真正的函数,因此不能作为表达式的一部分使用。 print(): 函数print()打印一个值(它的参数),如果字符串成功显示则返回true,否则返回false。 print_r(): 可以把字符串和数字简单地打印出来,而数组则以括起来的键和值...

工厂模式

简介 常见的实例化对象模式。 用工厂方法替代new操作的一种模式。 当我们使用new操作实例化对象时,调用构造函数完成初始化。若初始化仅是进行赋值等简单的操作,写入构造函数即可。但如果初始化时需要执行一长串复杂的代码,将多个工作装入一个方法,是不妥的。 创建实例与使用实例分离。将创建实例所需的大量初始化工作从基类的构造函数中分离出去。 简单工厂模式、工厂方法模式针对的是一个产品等级结构;而抽象工厂...

B1105 Spiral Matrix (画图)

B1105 Spiral Matrix (25分) //第一次只拿了21分 矩阵的长和宽,求最大因子,从sqrt(num)开始枚举. 每次循环一次,s++,t--,d--,r++ 测试点四运行超时,是因为输入一个数字的时候,需要直接输出这个数字。//1分 测试点二运行超时,最后一个数字不必再while循环一次,直接输出即可。//3分 最后一个测试点卡了好久/(ㄒoㄒ)/~~ 螺旋矩阵...

猜你喜欢

Java基础=>String,StringBuffer与StringBuilder的区别

字符串常量池 什么是字符串常量池? JVM为了减少字符串对象的重复创建,其维护了一块特殊的内存,这段内存被称为字符串常量池(存储在方法区中)。 具体实现 当代码中出现字符串时,JVM首先会对其进行检查。 如果字符串常量池中存在相同内容的字符串对象,如果有,则不再创建,直接返回这个对象的地址返回。 如果字符串常量池中不存在相同内容的字符串对象,则创建一个新的字符串对象并放入常量池,并返回新创建的字符...

java调用其他java项目的Https接口

项目中是这样的: 用户拿出二维码展示,让机器识别二维码, 机器调用开门的后台系统接口, 然后开门的后台系统接口需要调用管理系统的接口, 管理系统需要判断能不能开门.这两个系统是互相独立的.当时使用http调用是没有问题的.当时后来要求必须用https.废话不说,直接代码: 我的项目中调用的是 HttpsUtils.Get(utlStr) 这个接口 开门系统接口如下图:   管理系统的接口...

Hadoop1.2.1全分布式模式配置

一 集群规划 主机名            IP                               安装的软件 &nbs...

Go语言gin框架的安装

尝试安装了一下gin,把遇到的一些小问题来记录一下 安装步骤 首先来看看官方文档,链接点这里 可以看到安装步骤很简单,就一句话 在命令行中输入这句话运行等待就好。 问题来了,因为墙的问题,go get会很慢,所以命令行里面半天什么反应也没有,不要急,慢慢等着就会看到gin-gonic/gin这个目录出现 这个时候命令行还是没有结束,表示还在下一些东西。有的时候可能心急的人就停了(比如我),然后写个...

uni-app表单组件二

input(输入框) 属性名 类型 说明 平台差异 value String 输入框的初始内容 type String input 的类型 password Boolean(默认false) 是否是密码类型 placeholder String 输入框为空时占位符 placeholder-style String 指定 placeholder 的样式 placeholder-class Strin...