opencv&qt学习之四——opencv实现人脸检测与相关知识整理(代码片段)

author author     2022-11-14     351

关键词:

开发配置


OpenCV的例程中已经带有了人脸检测的例程,位置在:OpenCV\\samples\\facedetect.cpp文件,OpenCV的安装与这个例子的测试可以参考我之前的博文Linux 下编译安装OpenCV

网上能够找到关于OpenCV人脸检测的例子也比较多,大多也都是基于这个例程来更改,只是多数使用的是OpenCV 1.0的版本,而OpenCV2.0以后由于模块结构的更改,很多人并没有将例程运行起来。如果是新版的OpenCV跑旧的例程,编译运行出错的话,需要确保:

  1. #include "opencv2/objdetect/objdetect.hpp" 头文件被引用,老的头文件包含可能会提示找不到定义
  2. libopencv_objdetect243.dll.a 库需要加入链接

之前找了几个例程,不尽如人意,于是决定还是改自带的例程更靠谱,更多的信息,已经在程序中添加注释,参见程序吧。

pro文件的工程配置,具体路径按照安装路径更改,Linux下也一样。

人脸检测基础知识整理


下面整理下人脸检测的相关知识。

人脸检测从整体来看分为四个部分:

1、Face detection 人脸识别,即识别出这是人的脸,而不管他是谁的。

2、Face preprocessing 面部预处理,即提取出脸部图像。

3、Collect and learn faces 脸部的特征采集和学习

4、Face recognition 脸部识别,找出最相近的相近脸部图像。

“基于知识的方法主要利用先验知识将人脸看作器官特征的组合,根据眼睛、眉毛、嘴巴、鼻子等器官的特征以及相互之间的几何位置关系来检测人脸。基于统计的方法则将人脸看作一个整体的模式——二维像素矩阵,从统计的观点通过大量人脸图像样本构造人脸模式空间,根据相似度量来判断人脸是否存在。在这两种框架之下,发展了许多方法。目前随着各种方法的不断提出和应用条件的变化,将知识模型与统计模型相结合的综合系统将成为未来的研究趋势。”(来自论文《基于Adaboost的人脸检测方法及眼睛定位算法研究》)

人脸检测算法的可靠性很大程度上依赖于分类器的设计,在2001年,Viola和Jones两位大牛发表了经典的《Rapid Object Detection using a Boosted Cascade of Simple Features》【1】和《Robust Real-Time Face Detection》【2】,在AdaBoost算法的基础上,使用Haar-like小波特征和积分图方法进行人脸检测,他俩不是最早使用提出小波特征的,但是他们设计了针对人脸检测更有效的特征,并对AdaBoost训练出的强分类器进行级联。这可以说是人脸检测史上里程碑式的一笔了,也因此当时提出的这个算法被称为Viola-Jones检测器。又过了一段时间,Rainer Lienhart和Jochen Maydt两位大牛将这个检测器进行了扩展【3】,最终形成了OpenCV现在的Haar分类器。在OpenCV2.0中又扩充了基于LBP特征的人脸检测器,某些情况下LBP特征比Haar来的更为快速。

 

在进行识别时首先通过大量的具有比较明显的haar特征(矩形)的物体图像用模式识别的方法训练出分类器,分类器是个级联的,每级都以大概相同的识别率保留进入下一级的具有物体特征的候选物体,而每一级的子分类器则由许多haar特征构成(由积分图像计算得到,并保存下位置),有水平的、竖直的、倾斜的,并且每个特征带一个阈值和两个分支值,每级子分类器带一个总的阈值。识别物体的时候,同样计算积分图像为后面计算haar特征做准备,然后采用与训练的时候有物体的窗口同样大小的窗口遍历整幅图像,以后逐渐放大窗口,同样做遍历搜索物体;每当窗口移动到一个位置,即计算该窗口内的haar特征,加权后与分类器中haar特征的阈值比较从而选择左或者右分支值,累加一个级的分支值与相应级的阈值比较,大于该阈值才可以通过进入下一轮筛选。当通过分类器所有级的时候说明这个物体以大概率被识别。

程序设计


如果单纯是对功能进行实现,有了官方自带的例程做参考,移植实现并不是很难,几乎不用费太大的功夫,自带例程对照着OpenCV参考手册还是比较好理解,这部分例程已经成功在Linux(Ubuntu和嵌入式Linux)以及Windows下实现,后面实验室基于Qt设计的实验软件,也整合了进去。

程序参考本文后面给出的参考程序,当然最权威的还是软件自带例程,实现人脸检测的另外一个关键就是训练文件,基于Haar和LBP特征的人脸检测可以自动的对大量数据图片进行训练,训练结果存储在XML文件中以供使用,这些级联分类器一般需要训练上千幅人脸图片和上万幅非人脸图片,这些训练过程往往需要很长的时间(LBP特征需要几个小时,Harr特征可能甚至需要一个星期)不过OpenCV已经提供了不同种类的训练好的文件,因此我们可以方便的通过载入这些训练好的级联分类器XML文件来实现人脸、眼睛、鼻子等检测。

OpenCV的训练文件在源码目录的data文件夹下,里面包含haarcascades、hogcascades、lbpcascades,在haarcascades文件下包含大量的针对不同目标的训练文件,如下图所示:

image

文件名已经体现了文件的功能,因此只需要载入对应的文件即可。

到这里还只是进行了一个非常初步的研究,下一步的学习和识别还需要多多积累。

实验


根据自带例程,将人脸检测算法加入我所做的实验软件中,分别选择不同的分类器进行实验,下图为实验结果。

image

 

人脸检测实验

 

 

 

imageimage

实现特定的器官检测右眼、鼻子等检测

测试程序

QT       += core

QT       -= gui

TARGET = cvcap
CONFIG   += console
CONFIG   -= app_bundle

TEMPLATE = app

SOURCES += main.cpp
INCLUDEPATH+=D:\\OpenCV\\build\\include
INCLUDEPATH+=D:\\OpenCV\\build\\include\\opencv

LIBS+=D:\\OpenCV\\build\\x86\\mingw\\lib\\libopencv_core243.dll.a
LIBS+=D:\\OpenCV\\build\\x86\\mingw\\lib\\libopencv_highgui243.dll.a
LIBS+=D:\\OpenCV\\build\\x86\\mingw\\lib\\libopencv_imgproc243.dll.a
LIBS+=D:\\OpenCV\\build\\x86\\mingw\\lib\\libopencv_video243.dll.a
LIBS+=D:\\OpenCV\\build\\x86\\mingw\\lib\\libopencv_objdetect243.dll.a

主程序,具体地方都已经注释。这里是打开摄像头读取数据,同样可以自己打开图片。

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <QDebug>

using namespace cv;

void detectAndDraw( Mat& img, CascadeClassifier& cascade,
                    CascadeClassifier& nestedCascade,
                    double scale, bool tryflip );

int main()

    VideoCapture cap(0);    //打开默认摄像头
    if(!cap.isOpened())
    
        return -1;
    
    Mat frame;
    Mat edges;

    CascadeClassifier cascade, nestedCascade;
    bool stop = false;
    //训练好的文件名称,放置在可执行文件同目录下
    cascade.load("haarcascade_frontalface_alt.xml");
    nestedCascade.load("haarcascade_eye_tree_eyeglasses.xml");
    while(!stop)
    
        cap>>frame;
        detectAndDraw( frame, cascade, nestedCascade,2,0 );
        if(waitKey(30) >=0)
            stop = true;
    
    return 0;

void detectAndDraw( Mat& img, CascadeClassifier& cascade,
                    CascadeClassifier& nestedCascade,
                    double scale, bool tryflip )

    int i = 0;
    double t = 0;
    //建立用于存放人脸的向量容器
    vector<Rect> faces, faces2;
    //定义一些颜色,用来标示不同的人脸
    const static Scalar colors[] =   CV_RGB(0,0,255),
        CV_RGB(0,128,255),
        CV_RGB(0,255,255),
        CV_RGB(0,255,0),
        CV_RGB(255,128,0),
        CV_RGB(255,255,0),
        CV_RGB(255,0,0),
        CV_RGB(255,0,255) ;
    //建立缩小的图片,加快检测速度
    //nt cvRound (double value) 对一个double型的数进行四舍五入,并返回一个整型数!
    Mat gray, smallImg( cvRound (img.rows/scale), cvRound(img.cols/scale), CV_8UC1 );
    //转成灰度图像,Harr特征基于灰度图
    cvtColor( img, gray, CV_BGR2GRAY );
    //改变图像大小,使用双线性差值
    resize( gray, smallImg, smallImg.size(), 0, 0, INTER_LINEAR );
    //变换后的图像进行直方图均值化处理
    equalizeHist( smallImg, smallImg );

    //程序开始和结束插入此函数获取时间,经过计算求得算法执行时间
    t = (double)cvGetTickCount();
    //检测人脸
    //detectMultiScale函数中smallImg表示的是要检测的输入图像为smallImg,faces表示检测到的人脸目标序列,1.1表示
    //每次图像尺寸减小的比例为1.1,2表示每一个目标至少要被检测到3次才算是真的目标(因为周围的像素和不同的窗口大
    //小都可以检测到人脸),CV_HAAR_SCALE_IMAGE表示不是缩放分类器来检测,而是缩放图像,Size(30, 30)为目标的
    //最小最大尺寸
    cascade.detectMultiScale( smallImg, faces,
        1.1, 2, 0
        //|CV_HAAR_FIND_BIGGEST_OBJECT
        //|CV_HAAR_DO_ROUGH_SEARCH
        |CV_HAAR_SCALE_IMAGE
        ,
        Size(30, 30));
    //如果使能,翻转图像继续检测
    if( tryflip )
    
        flip(smallImg, smallImg, 1);
        cascade.detectMultiScale( smallImg, faces2,
                                 1.1, 2, 0
                                 //|CV_HAAR_FIND_BIGGEST_OBJECT
                                 //|CV_HAAR_DO_ROUGH_SEARCH
                                 |CV_HAAR_SCALE_IMAGE
                                 ,
                                 Size(30, 30) );
        for( vector<Rect>::const_iterator r = faces2.begin(); r != faces2.end(); r++ )
        
            faces.push_back(Rect(smallImg.cols - r->x - r->width, r->y, r->width, r->height));
        
    
    t = (double)cvGetTickCount() - t;
 //   qDebug( "detection time = %g ms\\n", t/((double)cvGetTickFrequency()*1000.) );
    for( vector<Rect>::const_iterator r = faces.begin(); r != faces.end(); r++, i++ )
    
        Mat smallImgROI;
        vector<Rect> nestedObjects;
        Point center;
        Scalar color = colors[i%8];
        int radius;

        double aspect_ratio = (double)r->width/r->height;
        if( 0.75 < aspect_ratio && aspect_ratio < 1.3 )
        
            //标示人脸时在缩小之前的图像上标示,所以这里根据缩放比例换算回去
            center.x = cvRound((r->x + r->width*0.5)*scale);
            center.y = cvRound((r->y + r->height*0.5)*scale);
            radius = cvRound((r->width + r->height)*0.25*scale);
            circle( img, center, radius, color, 3, 8, 0 );
        
        else
            rectangle( img, cvPoint(cvRound(r->x*scale), cvRound(r->y*scale)),
                       cvPoint(cvRound((r->x + r->width-1)*scale), cvRound((r->y + r->height-1)*scale)),
                       color, 3, 8, 0);
        if( nestedCascade.empty() )
            continue;
        smallImgROI = smallImg(*r);
        //同样方法检测人眼
        nestedCascade.detectMultiScale( smallImgROI, nestedObjects,
            1.1, 2, 0
            //|CV_HAAR_FIND_BIGGEST_OBJECT
            //|CV_HAAR_DO_ROUGH_SEARCH
            //|CV_HAAR_DO_CANNY_PRUNING
            |CV_HAAR_SCALE_IMAGE
            ,
            Size(30, 30) );
        for( vector<Rect>::const_iterator nr = nestedObjects.begin(); nr != nestedObjects.end(); nr++ )
        
            center.x = cvRound((r->x + nr->x + nr->width*0.5)*scale);
            center.y = cvRound((r->y + nr->y + nr->height*0.5)*scale);
            radius = cvRound((nr->width + nr->height)*0.25*scale);
            circle( img, center, radius, color, 3, 8, 0 );
        
    
    cv::imshow( "result", img );

 

 

深度学习之python,opencv中的卷积(代码片段)

这篇博客将介绍图像内核和卷积。如果将图像视为一个大矩阵,那么图像内核只是一个位于图像顶部的微小矩阵。从左到右和从上到下滑动内核,计算输入图像和内核之间的元素乘法总和——称这个值为内核输出。内核... 查看详情

6.paddlegraphlearning(pgl)图学习之图游走类模型[系列四](代码片段)

PaddleGraphLearning(PGL)图学习之图游走类模型[系列四]更多详情参考:PaddleGraphLearning图学习之图游走类模型[系列四]https://aistudio.baidu.com/aistudio/projectdetail/5002782?contributionType=1相关项目参考:关于图计算&图学习的基础知... 查看详情

elasticsearch聚合学习之四:结果排序(代码片段)

...给这些数据进行排序;系列文章列表《Elasticsearch聚合学习之一:基本操作》;《Elasticsearch聚合学习之二:区间聚合》;《Elasticsearch聚合学习之三:范围限定》;《 查看详情

人脸检测 OpenCV + Qt + cvMat

】人脸检测OpenCV+Qt+cvMat【英文标题】:facedetectOpenCV+Qt+cvMat【发布时间】:2015-10-0620:05:07【问题描述】:我正在尝试将旧的opencv面部检测代码从使用IplImage结构迁移到使用来自opencv的Mat类。问题是当没有Qt代码时代码正在运行。这... 查看详情

qt学习之qt下载安装

登录QT的官网www.qt.io发现界面是相当不友好,更新过后更不知道在哪了,要下载QT5.6的版本,开源源码包相当不好找。l另外Qt官网有一个专门的资源下载网站,Qt官方所有的开发环境和相关工具都可以从这个资源下载站找到(但是... 查看详情

javascript实例学习之四——javascript分页

话不多少,直接上代码html代码:<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>javascript分页效果</title><style>a{margin-right:5px;}</style></head>&l 查看详情

qt学习之qdatastream

QDataStream简介QDataStream类为QIODevice提供序列化的二进制数据。一个datastream是一个编码后的二进制流,它与操作系统等无关。你可以使用一个datastream去读写原始未编码的二进制数据。如果你想得到一个“parsing”的输入流,请查阅QT... 查看详情

halcon学习之四:有关图像生成的函数

1、copy_image ( Image : DupImage : : )复制image图像2、region_to_bin ( Region : BinImage : ForegroundGray, BackgroundGray,Width, Hei 查看详情

qt学习之qlistwidget删除item

将QListWidgetItem从QListWidget列表中删除有两种方法能够做到。但也要依据自己的须要进行选择。第一种是QListWidgetItem*takeItem(introw);使用此方法须要知道删除的是第几个Item。而且返回删除的Item指针。另外一种是inlinevoidremoveItemWidget(QL... 查看详情

qt学习之多线程程序设计

QT通过三种形式提供了对线程的支持。它们各自是,一、平台无关的线程类二、线程安全的事件投递三、跨线程的信号-槽连接。这使得开发轻巧的多线程Qt程序更为easy,并能充分利用多处理器机器的优势。多线程编程也是一个实... 查看详情

tensorflow深度学习之十二:基础图像处理之二

...lowastfimportcv2#这里定义一个tensorflow读取的图片格式转换为opencv读取的图片格式的函数#请注意:#在tensorflow中,一 查看详情

qt学习之qmainwindow详解(代码片段)

文章目录1、菜单栏2、工具栏3、状态栏4、铆接部件5、核心部件(中心部件)6、资源文件有关QT的学习我们会采取连载更新,传送门:有C++基础如何直接上手QT?最适合新手的第一个Qt小程序今天更新内容... 查看详情

elasticsearch学习之深入聚合分析四---案例实战

1.需求:比如有一个网站,记录下了每次请求的访问的耗时,需要统计tp50,tp90,tp99tp50:50%的请求的耗时最长在多长时间tp90:90%的请求的耗时最长在多长时间tp99:99%的请求的耗时最长在多长时间PUT/website{"mappings":{"logs":{"properties... 查看详情

go学习之channel&&同步

packagemainimport( "fmt" "sync")funcfibonacci(nint,cchanint,waitGroup*sync.WaitGroup) deferwaitGroup.Done() x,y:=0,1 fori:=0;i<n;i++ c<-x x,y=y,x+y clo 查看详情

api源码学习之集合--linkedlist

继续集合源码学习--LinkedList 1、该类主要成员变量及构造方法有如下几个:1transientintsize=0;23/**4*Pointertofirstnode.5*Invariant:(first==null&&last==null)||6*(first.prev==null&&first.item!=null)7*/8transientN 查看详情

qt-opencv在项目中集成使用(待完成)(代码片段)

概要说明:1、OpenCV头文件/库文件  opencv2/core.hpp    //Mat核心库  opencv2/imgcodecs.hpp //读图片  opencv2/highgui.hpp   //显示界面 使用CMake编译OpenCV源码:1、使用mingw编译OpenCV 编译中遇到问题&解决办法:1、... 查看详情

Qt + OpenCV 使用 std::thread 播放视频

】Qt+OpenCV使用std::thread播放视频【英文标题】:Qt+OpenCVplayvideoswithstd::thread【发布时间】:2015-03-3004:07:51【问题描述】:这是我的图形用户界面,我想要在这里做的是一起显示四个不同的视频。用户输入特定视频文件的路径并点击... 查看详情

manuals学习之第四课时

  3.python简介 下面的例子中,输入和输出分别由大于号和句号提示符(>>>和...)标注:如果想重现这些例子,就要在解释器的提示符后输入(提示符后面的)那些不包含提示符的代码行。需要注意的是在练习中遇到的... 查看详情