opencv之HAAR与LBP级联分类器,实现人脸识别人眼追踪

haar、LBP检测器原理

本人不擅长,请自行百度


级联器相关介绍

  • Viola和Jones在2001在CVPR提出
  • 一种实时对象(人脸)检测框架
  • 训练速度非常慢,检测速度非常快
  • 5000个正向人脸样本与3000万个非人脸负样本数据

级联分类器原理-AdaBoost

  • 弱分类器-Weak classifier = Feature
  • 强分类器-多个弱分类器的线性组合
  • 级联分类器-多个强分类器的组合

在这里插入图片描述


opencv中级联检测器使用

  • CascadeClassifier
  • 加载特征数据
  • 检测特征对象
  • 代码演示

HAAR与LBP区别

  • HAAR特征是浮点数运算
  • LBP特征是整数计算
  • LBP训练需要的样本数量要比HAAR大
  • 同样的样本训练空间,HAAR训练出来的数据检测结果要比LBP准确
  • 扩大LBP的样本数据,训练结果可以跟HAAR一样
  • LBP的速度一般比HAAR快几倍

相关API

函数api

它可以检测出图片中所有的人脸,并将人脸用vector保存各个人脸的坐标、大小(用矩形表示),函数由分类器对象调用

void cv::CascadeClassifier::detectMultiScale 	( 	InputArray  	image,
		std::vector< Rect > &  	objects,
		double  	scaleFactor = 1.1,
		int  	minNeighbors = 3,
		int  	flags = 0,
		Size  	minSize = Size(),
		Size  	maxSize = Size() 
	) 	

参数说明

  • image 表示的是要检测的输入图像
  • objects 表示检测到的人脸目标序列
  • scaleFactor 表示每次图像尺寸减小的比例
  • minNeighbors 表示每一个目标至少要被检测到3次才算是真的目标(因为周围的像素和不同的窗口大小都可以检测到人脸),
  • minSize 为目标的最小尺寸
  • maxSize 为目标的最大尺寸

函数api

void cv::matchTemplate 	( 	InputArray  	image,
		InputArray  	templ,
		OutputArray  	result,
		int  	method,
		InputArray  	mask = noArray() 
	) 	

参数介绍

  • image:待匹配的源图像
  • templ:模板图像
  • result:保存结果的矩阵,我们可以通过minMaxLoc()确定结果矩阵的最大值和最小值的位置.模板搜索结果输出图像,必须为单通道32-bit位浮点型图像,如果图像尺寸是WxH而template尺寸是wxh,则此参数result一定是**(W-w+1)x(H-h+1)**
  • method :模板匹配的算法有以下六种:
    • 不同的方法会得到差异很大的结果,可以通过测试选择最合适的方法。
      • (1).平方差匹配法TM_SQDIFF
      • (2)归一化平方差匹配法TM_SQDIFF_NORMED
      • (3)相关匹配法TM_CCORR
      • (4)归一化相关匹配法TM_CCORR_NORMED
      • (5)系数匹配法TM_CCOEFF
      • (6)化相关系数匹配法TMCCOEFF_NORMED
    • 通常来讲,随着从简单测量方法(平方差)到更复杂的测量方法(相关系数法),我们可以获得越来越准确的匹配。然而这同时也会以越来越大的计算量为代价。对于选取何种方法,针对不同的匹配情况进行对此分析比较,选取更适合自己应用场景同时兼顾速度和精度的最佳方案。
      注意
      值得注意的是对于方法SQDIFFSQDIFF_NORMED两种方法来讲,越小的值就有着更高的匹配结果,而其余的方法则是数值越大匹配效果越好。

函数api

void minMaxLoc(const SparseMat& a,
                double* minVal,
                double* maxVal, 
                int* minIdx=0,
                int* maxIdx=0);	

参数说明

  • a: 匹配结果矩阵
  • &minVal 和 &maxVal: 在矩阵 result 中存储的最小值和最大值
  • &minLoc 和 &maxLoc: 在结果矩阵中最小值和最大值的坐标.

小案例—视频中的人脸检测与眼睛跟踪

人脸检测与人脸的生物学特征

  • HAAR级联检测器实现人脸检测
  • 人脸的生物学特征
    • 两个眼睛之间的距离大致等于一个眼睛的距离
    • 左右对称
    • 眼睛到嘴巴的距离大致在两个眼睛的宽度大小左右
    • 鼻子到嘴唇的距离,大致等于两个嘴唇的厚度

检测方法

首先利用级联检测器检测人脸与眼睛,眼睛追踪不必一直检测,浪费算力,眼睛区域在脸部区域上半区域,眼睛区域在上半部分区域左右两侧,上班部分区域的上部1/4到1/3区域是眉毛、额头部分,不含有眼睛。左眼左边八分之一的宽度也不含有眼睛,右眼右边同样是这样。

在这里插入图片描述

为提高效率,减小检测面积是一方面,检查出眼睛位置后,获取眼睛图片,在相应的眼睛区域做模板匹配,之后就可以不用级联检测器检测眼睛,只需要检测面部即可。


代码 (缩小检测区域)

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace std;
using namespace cv;

int main(void)
{
    string face_data      = "/work/opencv_data/haarcascade_frontalface_alt.xml";
    string lefteyes_data  = "/work/opencv_data/haarcascade_lefteye_2splits.xml";
    string righteyes_data = "/work/opencv_data/haarcascade_righteye_2splits.xml";

    CascadeClassifier face_classifier;
    CascadeClassifier lefteyes_classifier;
    CascadeClassifier righteyes_classifier;


    if( !face_classifier.load(face_data) ||
             !lefteyes_classifier.load(lefteyes_data) ||
            !righteyes_classifier.load(righteyes_data)  )
    {
        cout << "data is not exist" << endl;
        return -1;
    }

    VideoCapture cap(0);
    if(!cap.isOpened())    // 判断是否打开成功
    {
        cout << "open camera failed. " << endl;
        return -1;
    }
    Mat frame,gray_src;
    while(true)
    {
        cap >> frame;
        if(frame.empty())
        {
            cout << "frame is empty" << endl;
            return -1;
        }

        flip(frame,frame,1);  //画面水平反转
        cvtColor(frame,gray_src,COLOR_BGR2GRAY);
        equalizeHist(gray_src,gray_src);
        vector<Rect> faces;
        Mat faceimg = frame.clone();
        //识别人脸
        face_classifier.detectMultiScale(gray_src,faces,1.1,3,0,Size(30,30));
        for(size_t i=0;i<faces.size();i++)
        {
            rectangle(faceimg,faces[i],Scalar(0,255,0),2);

            //缩小眼睛的ROI区域
            int offsety = faces[i].height/4;
            int offsetx = faces[i].width/8;

            int eyeheight = faces[i].height/2 - offsety;
            int eyewidth = faces[i].width/2 - offsetx;

            //截取左眼区域
            Rect left_rect;
            left_rect.x = faces[i].x+offsetx;
            left_rect.y = faces[i].y+offsety;
            left_rect.width = eyewidth;
            left_rect.height = eyeheight;

            Mat left_roi = gray_src(left_rect);

            //识别左眼
            vector<Rect> lefteye;
            //小范围识别 识别次数选择一次即可
            lefteyes_classifier.detectMultiScale(left_roi,lefteye,1.1,1,0,Size(20,20));
            if(lefteye.size())
            {
                left_rect = lefteye[0] + Point(left_rect.x,left_rect.y);
                rectangle(faceimg,left_rect,Scalar(255,0,0),2);
            }

            //截取右眼区域
            Rect right_rect;
            right_rect.x = faces[i].x+faces[i].width/2;
            right_rect.y = faces[i].y+offsety;
            right_rect.width = eyewidth;
            right_rect.height = eyeheight;

            Mat right_roi = gray_src(right_rect);

            //识别右眼
            vector<Rect> righteye;
            //小范围识别 识别次数选择一次即可
            righteyes_classifier.detectMultiScale(left_roi,righteye,1.1,1,0,Size(20,20));
            if(righteye.size())
            {
                right_rect = righteye[0] + Point(right_rect.x,right_rect.y);
                rectangle(faceimg,right_rect,Scalar(255,0,0),2);
            }
        }
        imshow("检测画面",faceimg);

        if(waitKey(30)>0)
        {
            break;
        }
    }
    destroyAllWindows();
    return 0;
}

眼睛识别效果(缩小识别区域)

在这里插入图片描述


代码(模板实现眼睛追踪)

#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace std;
using namespace cv;

Mat lefttpl,righttpl;   //待匹配的源图像
Rect leftEye,rightEye;  //匹配后获得的矩形区域

//模板匹配函数
void track_eye(Mat &im,Mat &tpl,Rect &rect)
{
    Mat result;
    int result_cols = im.cols - tpl.cols +1;
    int result_rows = im.rows - tpl.rows +1;

    result.create(result_rows,result_cols,CV_32FC1);
    matchTemplate(im,tpl,result,TM_CCORR_NORMED);

    //寻找位置
    double minval,maxval;
    Point minloc,maxloc;
    minMaxLoc(result,&minval,&maxval,&minloc,&maxloc);
    if(maxval > 0.75)
    {
        rect.x = rect.x + maxloc.x;
        rect.y = rect.y + maxloc.y;
    }else
    {
        rect.x = rect.y = rect.width = rect.height = 0;
    }

}
int main(void)
{
Mat result;
    int result_cols = im.cols - tpl.cols +1;
    int result_rows = im.rows - tpl.rows +1;

    result.create(result_rows,result_cols,CV_32FC1);
    matchTemplate(im,tpl,result,TM_CCORR_NORMED);

    //寻找位置
    double minval,maxval;
    Point minloc,maxloc;
    minMaxLoc(result,&minval,&maxval,&minloc,&maxloc);
    if(maxval > 0.75)
    {
        rect.x = rect.x + maxloc.x;
        rect.y = rect.y + maxloc.y;
    }else
    {
        rect.x = rect.y = rect.width = rect.height = 0;
    }

}

int main(void)
{
    string face_data      = "/work/opencv_data/haarcascade_frontalface_alt.xml";
    string lefteyes_data  = "/work/opencv_data/haarcascade_lefteye_2splits.xml";
    string righteyes_data = "/work/opencv_data/haarcascade_righteye_2splits.xml";

    CascadeClassifier face_classifier;
    CascadeClassifier lefteyes_classifier;
    CascadeClassifier righteyes_classifier;


    if( !face_classifier.load(face_data) ||
             !lefteyes_classifier.load(lefteyes_data) ||
            !righteyes_classifier.load(righteyes_data)  )
    {
        cout << "data is not exist" << endl;
        return -1;
    }

    VideoCapture cap(0);
    if(!cap.isOpened())    // 判断是否打开成功
    {
        cout << "open camera failed. " << endl;
        return -1;
    }
    Mat frame,gray_src;
    while(true)
    {
        cap >> frame;
        if(frame.empty())
        {
            cout << "frame is empty" << endl;
            return -1;
        }

        flip(frame,frame,1);  //画面水平反转
        cvtColor(frame,gray_src,COLOR_BGR2GRAY);
        equalizeHist(gray_src,gray_src);
        vector<Rect> faces;
        Mat faceimg = frame.clone();
        //识别人脸
        face_classifier.detectMultiScale(gray_src,faces,1.1,3,0,Size(30,30));
        for(size_t i=0;i<faces.size();i++)
        {
            rectangle(faceimg,faces[i],Scalar(0,255,0),2);

            //缩小眼睛的ROI区域
            int offsety = faces[i].height/4;
            int offsetx = faces[i].width/8;

            int eyeheight = faces[i].height/2 - offsety;
            int eyewidth = faces[i].width/2 - offsetx;

            //截取左眼区域
            Rect left_rect;
            left_rect.x = faces[i].x+offsetx;
            left_rect.y = faces[i].y+offsety;
            left_rect.width = eyewidth;
            left_rect.height = eyeheight;

            Mat left_roi = gray_src(left_rect);

            //识别左眼
            vector<Rect> lefteye;
            //小范围识别 识别次数选择一次即可

            lefteyes_classifier.detectMultiScale(left_roi,lefteye,1.1,1,0,Size(20,20));
            if(lefttpl.empty())
            {
                if(lefteye.size())
                {
                    left_rect = lefteye[0] + Point(left_rect.x,left_rect.y);
                    lefttpl = gray_src(left_rect);
                    rectangle(faceimg,left_rect,Scalar(255,0,0),2);
                }
            }else
            {
                leftEye.x = left_rect.x;
                leftEye.y = left_rect.y;
                track_eye(left_roi,lefttpl,leftEye);
                if(leftEye.x > 0 && leftEye.y >0)
                {
                    leftEye.width = lefttpl.cols;
                    leftEye.height = lefttpl.rows;
                    rectangle(faceimg,leftEye,Scalar(255,0,0),2);
                }
            }



            //截取右眼区域
            Rect right_rect;
            right_rect.x = faces[i].x+faces[i].width/2;
            right_rect.y = faces[i].y+offsety;
            right_rect.width = eyewidth;
            right_rect.height = eyeheight;

            Mat right_roi = gray_src(right_rect);

            //识别右眼
            vector<Rect> righteye;
            //小范围识别 识别次数选择一次即可
            righteyes_classifier.detectMultiScale(left_roi,righteye,1.1,1,0,Size(20,20));

            if(righttpl.empty())
            {
                if(righteye.size())
                {
                    right_rect = righteye[0] + Point(right_rect.x,right_rect.y);
                    righttpl = gray_src(right_rect);
                    rectangle(faceimg,right_rect,Scalar(255,0,0),2);
                }
            }else
            {
                rightEye.x = right_rect.x;
                rightEye.y = right_rect.y;
                track_eye(right_roi,righttpl,rightEye);
                if(rightEye.x > 0 && rightEye.y >0)
                {
                    rightEye.width = righttpl.cols;
                    rightEye.height = righttpl.rows;
                    rectangle(faceimg,rightEye,Scalar(255,0,0),2);
                }
            }

        }
        imshow("检测画面",faceimg);

        if(waitKey(30)>0)
        {
            break;
        }
    }
    destroyAllWindows();
    return 0;
}

眼睛识别效果(模板识别)

在这里插入图片描述


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值