OpenCV拾趣(六)——视频流人脸跟踪

本篇简介

从本小节开始,我们将尝试使用基础框架搭建篇完成的各类工具,实现一些实战向的应用场景。
本篇作为实战篇的第一个案例,首先来看看如何实现视频流人脸跟踪这个较为常见的功能。

原理简介

实现对人脸时别/跟踪,需要解决的主要问题就是将人脸的部分与场景中其他的部分区分开来。这就涉及到两方面的问题:

  1. 如何提取人脸的特征
  2. 如何使用提取的特征对场景中的对象进行分类

对此,一种常见的实现方式为Haar特征检测提取特征 + 级联分类器(Cascading Classifier)进行分类的组合。而OpenCV中也提供了相应的分类器训练及分类工具。关于Haar特征提取和级联分类器的原理,在这里就不详细说明了,有兴趣的话可以参考文末参考连接中的相关资料[1][2]

此外,简明起见,接下来的实现将只关注如何使用已训练好的分类器进行人脸检测,如何对分类器进行训练不在本文的讨论范围内。

那么接下来我们就来看一看如何使用我们实现好的QCvCamView控件和OpenCV提供的分类器工具实现视频流人脸跟踪。

实现人脸检测滤波器

首先,秉承QCvCamView中通过滤波链来实现视频流处理的思路(参考:扩展视频流控件),我们需要实现一个人脸检测滤波器,声明如下:

class QCvFaceDetectFilter : public QCvMatFilter
{
  public:
    QCvFaceDetectFilter(QString name) : QCvMatFilter(name) {}

  public:
    bool load(QString fileName);
    bool isClassifierValid();

  protected:
    virtual void execFilter(const cv::Mat& inMat, cv::Mat& outMat);

  protected:
    cv::CascadeClassifier m_classifier;
};

这个滤波器除了实现了滤波器通用的execFilter方法外,还提供了读取分类器配置和判断分类器是否有效的公共方法。OpenCV提供的级联分类器作为组件以成员变量的形式在这里声明。
注:要使用这个分类器,需要引用opencv2/objdetect.hpp头文件

读取分类器配置的具体实现如下:

bool QCvFaceDetectFilter::load(QString fileName)
{
    return m_classifier.load(fileName.toStdString().c_str());
}

也就是直接使用了OpenCV提供的读取方法。

滤波处理的具体实现如下:

void QCvFaceDetectFilter::execFilter(const cv::Mat& inMat, cv::Mat &outMat)
{
    outMat = inMat.clone();
    if (m_classifier.empty())
    {
        return;
    }

    std::vector<cv::Rect> faces;
    cv::Mat frameGray;
    cv::cvtColor(outMat, frameGray, cv::COLOR_BGR2GRAY);
    cv::equalizeHist(frameGray, frameGray);
    //-- Detect faces
    m_classifier.detectMultiScale(frameGray, faces, 1.1, 2, 0 | cv::CASCADE_SCALE_IMAGE,
                                  cv::Size(30, 30));
    for (size_t i = 0; i < faces.size(); i++)
    {
        cv::Point center(faces[i].x + faces[i].width / 2, faces[i].y + faces[i].height / 2);
        cv::ellipse(outMat, center,
                    cv::Size(faces[i].width / 2, faces[i].height / 2), 0, 0, 360,
                    cv::Scalar(255, 0, 255), 4, 8, 0);
    }
}

上面的实现中,首先借助cvtColor方法将RGB图像转化为灰度图像,并通过直方图匹配(eualizeHist)对灰度图像进行了预处理,以提升光线较暗时识别的准确率。
之后,将预处理后的灰度图像传入级联分类器的多级检测方法(detectMultiScale)进行分类,并将结果矩形保存到faces数据中。
最后,在RGB图像上将检测到的人脸结果绘制为椭圆形。这样基于级联分类器的人脸检测滤波器就实现完成了。

注:这里的实现参考了OpenCV官方级联分类器教程中的实现,并进行了一定简化。

界面实现

整体界面设计如下:


face_detect_gui.png

灰色部分即为嵌入的QCvCamView控件,下方两个按钮分别为加载分类器的按钮和开始人脸检测的按钮。其中开始检测的按钮在没有完成分类器加载前处于不可用状态。

基于上述界面设计,我们初始化QCvCamView控件,并将上面实现好的人脸检测滤波器添加到滤波链中:

    m_camView = new QCvCamView(ui->frame);
    m_faceFilter = new QCvFaceDetectFilter("face");
    m_camView->appendFilter(m_faceFilter);

然后将配置好的视频流控件添加到布局中即可。这里限于篇幅就不贴布局相关的代码了。
接下来我们来实现两个功能按钮。首先是加载分类器的按钮,比较关键的实现步骤如下:

void FaceVideoDlg::onBtnLoad()
{
    // 省略界面布局及控件相关的操作
    ...

    QString dataDir = QDir::currentPath() + "/../../../../sdk/opencv_release/share/OpenCV/haarcascades";
    if (!QDir(dataDir).exists())
    {
        dataDir = ".";
    }
    QString classifierName = QFileDialog::getOpenFileName(this,
                                                          "Please Select a Cascade Classifier",
                                                          dataDir,
                                                          "XML Files (*.xml)");
    if (!classifierName.isEmpty())
    {
        bool isValid = m_faceFilter->load(classifierName);
        // 省略界面布局及控件相关的操作
        ...
    }
    else
    {
        // 省略界面布局及控件相关的操作
        ...
    }
}

上面的实现分为三个步骤:

  • 读取分类器。在上面的实现中,默认路径指向了OpenCV官方提供的训练结果样例路径(参考第一节有关SDK目录的说明),方便后续测试。
  • 加载分类器。这个步骤委托给了人脸检测滤波器处理。
  • 根据加载结果,执行各控件的启用/禁用操作。

另一方面,开始人脸检测的按钮实现就相对比较简单了,只需要启动视频流控件即可:

void FaceVideoDlg::onBtnDetect(bool clicked)
{
    m_camView->onStreamSwitch(clicked);
}

测试检测效果

整个界面实现完成后,我们来测试下检测的效果。首先点击加载分类器按钮,选择官方样例中的haarcascade_frontalface_alt2.xml训练器,然后点击开始检测按钮,效果如下:


face_detect2.png

有关视频流人脸检测的实现就说明到这里。
>>本篇参考代码
>>返回系列索引

参考链接

[1] Haar-like Feature Wikipedia
[2] Cascading Classfier Wikipedia
[3] OpenCV官方级联分类器教程

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,902评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,037评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,978评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,867评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,763评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,104评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,565评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,236评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,379评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,313评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,363评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,034评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,637评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,719评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,952评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,371评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,948评论 2 341

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,982评论 4 60
  • 一点背景知识 OpenCV 是一个开源的计算机视觉和机器学习库。它包含成千上万优化过的算法,为各种计算机视觉应用提...
    沬日十七阅读 998评论 0 4
  • 1 实验目的 目前计算机视觉技术已经比较成熟,相关的开源项目与算法很多,可以将这些开源算法进行整合,进而做成一个小...
    YOUNG_FAN阅读 6,663评论 0 50
  • ②尼玛剧情也太快了吧 想着想着,突然眼前一片漆黑,我拿出手机来照亮,可是我一点也动不了,一道光闪过,我到了一片平原...
    视姬哥阅读 243评论 0 0
  • 白驹过隙,时光荏苒,一转眼,整整两个月过去了,两个月的时间,快的让我猝不及防,快的让我心中感慨,我依稀记得3月5号...
    麦浪下的花香阅读 731评论 0 1