人脸Haar特征与快速计算神器:积分图

背景介绍

iPhone十周年纪念之作iPhoneX刚刚发布,其搭载的“刷脸解锁”功能再次将“人脸识别”技术带入大众视野。

借iPhoneX的东风,今天给大家介绍一下人脸检测的关键特征:Haar特征,并讲解如何快速计算待检测图像对应的积分图。

iPhoneX

Haar特征

想象一下现在你手上有一张图像需要用来做人脸检测,在人脸检测时有一个子窗口在待检测的图片中不断地移动,计算出对应位置的特征。将计算出的特征送到人脸分类器(本文主要讲解Haar特征及其计算,分类器的训练不涉及)中进行判断,通过筛选的区域则判断为人脸,反之则不是人脸。

那么,这个特征如何表示呢?

Viola、Lienhart等大牛提出了Haar特征。

其实,Haar特征本身并不复杂,就是用图中黑色矩形区域内所有像素值的和减去白色矩形区域内所有像素值的和,得到的值称为人脸特征值,如果Haar矩形放到非人脸区域,那么计算出的值和人脸特征值是不一样的。

但是,在实际使用Haar特征的过程中我们发现,Haar矩形特征是与矩形模版类别、矩形位置和矩形大小这三个变量的函数。随着矩形模板类别、大小和位置的变化,使得在检测的过程中会产生大量的特征值,如:在24*24像素大小的检测窗口内产生的矩形特征数量就超过10万个了。那么,如何可以快速计算出大量的Haar特征值呢?

下面,就需要介绍人脸检测中的神器——积分图!

积分图

首先给出积分图的定义:对于一张积分图ii(i,j),其位置(i,j)处的值ii(i,j)是是原图像i(i,j)左上角方向所有像素的和。

积分图的构建算法:

1. 用s(i,j)表示行方向的累加和,初始化s(i,-1)=0;

2. 用ii(i,j)表示一个积分图像,初始化ii(-1,j)=0;

3. 逐行扫描图像,计算每个像素(i,j)行方向的累加和s(i,j)和积分图像ii(i,j)的值:

s(i,j)=s(i,j-1)+i(i,j)

ii(i,j)=ii(i-1,j)+s(i,j)

4. 扫描图像一遍,当到达图像右下角像素时,积分图像ii就构造好了。

构造好积分图后,记图像中某一矩形区域A的四个顶点为a,b,c,d(左上角标为a,其余顶点按照顺时针次序标记),矩形区域A的像素和S(比如用某个Haar特征模板扫描图像时,需要计算白色区域或黑色区域的像素和)就可以这样计算:S=ii(a)+ii(c)-ii(b)-ii(d)(建议读者自行画图体会这个公式的妙处~)

这样,对于任意大小的矩形模板,只需查找积分图像4次就可以求得任意矩形的像素值的和,大大减少了计算量!

同时,只需要遍历一边图像,就可以计算得到所有子窗口的的Haar特征值,真可谓神器也!

OpenCV实现人脸检测

#include <iostream>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "opencv2/objdetect/objdetect.hpp"
using namespace cv;
using namespace std;
string face_cascade_name = "haarcascade_frontalface_alt2.xml";
CascadeClassifier face_cascade;
string window_name = "人脸实时检测程序";
void detectAndDisplay(Mat frame){
   std::vector<Rect> faces;
   Mat frame_gray;
   cvtColor(frame, frame_gray, CV_BGR2GRAY);
   equalizeHist(frame_gray, frame_gray);
   face_cascade.detectMultiScale(frame_gray, 
        faces, 1.1, 2, 0 | CV_HAAR_SCALE_IMAGE, Size(30, 30));
   for (int i = 0; i < faces.size(); i++){
      Point center(faces[i].x + faces[i].width*0.5, 
         faces[i].y + faces[i].height*0.5);
      ellipse(frame, center, Size(faces[i].width*0.5, 
   faces[i].height*0.5), 0, 0, 360, Scalar(255, 0, 255), 4, 8, 0);
   }
   imshow(window_name, frame);
}

int main(int argc, char* argv[])
{
   VideoCapture cap(0);
   if (!cap.isOpened())
      return -1;
   Mat edges;
   //namedWindow("edges", 1);
   if (!face_cascade.load(face_cascade_name)){
      printf("[error] 无法加载级联分类器文件!\n");
      return -1;
   }
   int nTick = 0;
   for (;;)
   {
      if (!cap.isOpened())
      {//等等摄像头打开
         continue;
      }
      Mat frame;
      nTick = getTickCount();
      cap >> frame; // get a new frame from camera
      if (frame.data == NULL)
      {//等到捕获到数据
         continue;
      }
      cvtColor(frame, edges, CV_BGR2BGRA);
      detectAndDisplay(edges);
      if (waitKey(30) >= 0) break;
   }
   return 0;
}

运行效果

(PS.保护舍友安全,舍友太帅,已经有女朋友了......)

原文发布于微信公众号 - 机器学习算法全栈工程师(Jeemy110)

原文发表时间:2017-09-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏深度学习计算机视觉

【人脸检测】Compact Cascade CNN和MTCNN算法

【文章导读】目前人脸识别技术已经遍地开花,火车站、机场、会议签到等等领域都有应用,人脸识别的过程中有个重要的环节叫做人脸检测,顾名思义就是在一张图片中找出所有的...

311
来自专栏数据科学与人工智能

【陆勤笔记】《深入浅出统计学》2集中趋势的度量:中庸之道

有时候,把握问题的核心是当务之急。你的核心竞争力是什么?认识事物,要抓重点,抓事物的本质。这个方法论,也是一个很好的学习之道。 从一大堆数字中看出模式和趋势可能...

1996
来自专栏绿巨人专栏

强化学习读书笔记 - 13 - 策略梯度方法(Policy Gradient Methods)

3548
来自专栏一个会写诗的程序员的博客

计算机中的数学【集合论】现代数学的共同基础

现代数学有数不清的分支,但是,它们都有一个共同的基础——集合论——因为 它,数学这个庞大的家族有个共同的语言。集合论中有一些最基本的概念:集合(set),关系(...

593
来自专栏贾志刚-OpenCV学堂

图像处理开发者必读

小编作为一个图像与计算机视觉的开发者,总结了一下作为图像处理开发工程师应该知道或者掌握的图像处理知识点。跟大家分享一下,以备大家学习方便。 图像像素操作 -...

2544
来自专栏人工智能头条

推荐收藏 | AI术语中英文对照

761
来自专栏大数据挖掘DT机器学习

一元线性回归的细节

文/程sir(简书作者) 原文:http://www.jianshu.com/p/fcd220697182 一元线性回归可以说是数据分析中非常简单的一个知识点,...

2744
来自专栏人工智能

以撩妹为例,5分钟让你秒懂深度学习!

深度学习是一个不断磨合的过程 就像谈恋爱一样 如果在这个人工智能的时代,作为一个有理想抱负的程序员,或者学生、爱好者,不懂深度学习这个超热的话题,似乎已经跟时代...

1777
来自专栏测试开发架构之路

大话移动通信(一)

数字系统由于模拟系统:长距离传输没有噪声叠加;便于保密。 贝尔电话之  接收信号 何为信号?信号的时域概念 周期信号:正弦波(单一频率) 信号的频域概念:电磁信...

3176
来自专栏AI2ML人工智能to机器学习

基于深度学习的文本分类?

曾几何时, SVM一统江湖, Lecun见证的Vapnik和Larry Jackel的世纪之赌, 从95年坚持到2000年依然岿然不动。 但是再过10年, 到2...

632

扫描关注云+社区