人脸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 条评论
登录 后参与评论

相关文章

来自专栏新智元

TensorFlow 自动句子语义编码,谷歌开源机器学习模型 Skip-Thoughts

【新智元导读】谷歌今天开源一个句子编码器模型 Skip-Thoughts,在 TensorFlow 上实现,学习将输入的句子编码成固定维度的向量表示,可以用于语...

3273
来自专栏机器之心

教程 | 一文简述多种无监督聚类算法的Python实现

1854
来自专栏ATYUN订阅号

使用Python实现无监督学习

人工智能研究的负责人Yan Lecun说,非监督式的学习——教机器自己学习,而不用被明确告知他们做的每一件事是对还是错——是实现“真”AI的关键。

715
来自专栏计算机视觉

天空是无限制的:基于语义的天空替换Sky is not limit:semantic aware sky replacement

第一个图片是输入图片,后面三个是不同风格的天空替换后的结果 这篇文章是给出一张输入的图片,论文中提出的方法自动的生成一组风格化天空图,我们首先使用了FCN全卷积...

3267
来自专栏企鹅号快讯

机器学习优化算法——梯度下降

在机器学习算法中,优化算法有很多,其中梯度下降法是个重头戏,如果说理解不到梯度下降法的原理,那很多算法的核心都难以掌握,今天我们就来谈谈何为“梯度下降法”。 我...

1989
来自专栏机器学习算法全栈工程师

听说比K-means厉害多了:谱聚类

地址:https://www.cnblogs.com/pinard/p/6221564.html

1472
来自专栏机器学习算法原理与实践

谱聚类(spectral clustering)原理总结

    谱聚类(spectral clustering)是广泛使用的聚类算法,比起传统的K-Means算法,谱聚类对数据分布的适应性更强,聚类效果也很优秀,同时...

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

R分类器性能评价:图形方法

几个基本概念 对于二元分类器,我们可以把分类样本的真实值记为1(positive,正例/阳性),-1(或0,negative,负例/阴性)分类结果记作1(s...

27210
来自专栏一心无二用,本人只专注于基础图像算法的实现与优化。

图像纹理合成及纹理传输算法学习(附源码)。

    有2到3年没有逛CodeProject了,上班一时无聊,就翻翻这个比较有名的国外网站,在其Articles » Multimedia » General...

2388
来自专栏https://www.cnblogs.com/L

【机器学习】--谱聚类从初始到应用

    谱聚类(spectral clustering)是一种基于图论的聚类方法,主要思想是把所有的数据看做空间中的点,这些点之间可以用边连接起来。距离较远(或...

573

扫码关注云+社区