热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

计算机视觉基础(九)——Haar特征描述算子之人脸检测

Haar-like特征最早是由Papageorgiou等应用于人脸表示,在2001年,Viola和Jones两位大牛发表了经典的《RapidObject

Haar-like特征最早是由Papageorgiou等应用于人脸表示,在2001年,Viola和Jones两位大牛发表了经典的《Rapid Object Detection using a Boosted Cascade of Simple Features》和《Robust Real-Time Face Detection》,在AdaBoost算法的基础上,使用Haar-like小波特征和积分图方法进行人脸检测,他俩不是最早使用提出小波特征的,但是他们设计了针对人脸检测更有效的特征,并对AdaBoost训练出的强分类器进行级联。这可以说是人脸检测史上里程碑式的一笔了,也因此当时提出的这个算法被称为Viola-Jones检测器。又过了一段时间,Rainer Lienhart和Jochen Maydt两位大牛将这个检测器进行了扩展,最终形成了OpenCV现在的Haar分类器。


1 算法理论介绍


1.1 Haar-like 特征

Haar(哈尔)特征分为三类:边缘特征、线性特征、中心特征和对角线特征,组合成特征模板。特征模板内有白色和黑色两种矩形,并定义该模板的特征值为白色矩形像素和减去黑色矩形像素和。Haar特征值反映了图像的灰度变化情况。例如:脸部的一些特征能由矩形特征简单的描述,如:眼睛要比脸颊颜色要深,鼻梁两侧比鼻梁颜色要深,嘴巴比周围颜色要深等。但矩形特征只对一些简单的图形结构,如边缘、线段较敏感,所以只能描述特定走向(水平、垂直、对角)的结构。
在这里插入图片描述
对于图中的A, B和D这类特征,特征数值计算公式为:v=Σ白-Σ黑,而对于C来说,计算公式如下:v=Σ白-2Σ黑;之所以将黑色区域像素和乘以2,是为了使两种矩形区域中像素数目一致。我们希望当把矩形放到人脸区域计算出来的特征值和放到非人脸区域计算出来的特征值差别越大越好,这样就可以用来区分人脸和非人脸。
通过改变特征模板的大小和位置,可在图像子窗口中穷举出大量的特征。上图的特征模板称为“特征原型”;特征原型在图像子窗口中扩展(平移伸缩)得到的特征称为“矩形特征”;矩形特征的值称为“特征值”。
在这里插入图片描述
上图中两个矩形特征,表示出人脸的某些特征。比如中间一幅表示眼睛区域的颜色比脸颊区域的颜色深,右边一幅表示鼻梁两侧比鼻梁的颜色要深。同样,其他目标,如眼睛等,也可以用一些矩形特征来表示。使用特征比单纯地使用像素点具有很大的优越性,并且速度更快。
矩形特征可位于图像任意位置,大小也可以任意改变,所以矩形特征值是矩形模版类别、矩形位置和矩形大小这三个因素的函数。故类别、大小和位置的变化,使得很小的检测窗口含有非常多的矩形特征,如:在24
24像素大小的检测窗口内矩形特征数量可以达到16万个。这样就有两个问题需要解决了:
(1)如何快速计算那么多的特征?—积分图大显神通;
(2)哪些矩形特征才是对分类器分类最有效的?—如通过AdaBoost算法来训练。


1.2 Haar-like特征的计算—积分图

积分图就是只遍历一次图像就可以求出图像中所有区域像素和的快速算法,大大的提高了图像特征值计算的效率。
积分图主要的思想是将图像从起点开始到各个点所形成的矩形区域像素之和作为一个数组的元素保存在内存中,当要计算某个区域的像素和时可以直接索引数组的元素,不用重新计算这个区域的像素和,从而加快了计算(这有个相应的称呼,叫做动态规划算法)。积分图能够在多种尺度下,使用相同的时间(常数时间)来计算不同的特征,因此大大提高了检测速度。
积分图是一种能够描述全局信息的矩阵表示方法。积分图的构造方式是:位置(𝑖,𝑗)处的值𝑖𝑖(𝑖,𝑗)是原图像(𝑖,𝑗)左上角方向所有像素𝑓(𝑘,𝑙)的和:
ii(i,j)=∑k≤i,l≤jf(k,l)ii(i,j)=∑k≤i,l≤jf(k,l)i i(i, j)=\sum k \leq i, l \leq j f(k, l) i i(i, j)=\sum k \leq i, l \leq j f(k, l)ii(i,j)=ki,ljf(k,l)ii(i,j)=ki,ljf(k,l)

积分图构建算法:
1、用𝑠(𝑖,𝑗)表示行方向的累加和,初始化𝑠(𝑖,−1)=0;
2、使用𝑖𝑖(𝑖,𝑗)表示一个积分图像,初始化𝑖𝑖(−1,𝑖)=0;
3、逐行扫描图像,递归计算每个像素(𝑖,𝑗)行方向的累加和𝑠(𝑖,𝑗)和积分图像𝑖𝑖(𝑖,𝑗)的值:
s(i,j)=s(i,j−1)+f(i,j)ii(i,j)=ii(i−1,j)+s(i,j)\begin{array}{l} s(i, j)=s(i, j-1)+f(i, j) \\ i i(i, j)=i i(i-1, j)+s(i, j) \end{array}s(i,j)=s(i,j1)+f(i,j)ii(i,j)=ii(i1,j)+s(i,j)
4、扫描图像一遍,当到达图像右下角像素时,积分图像𝑖𝑖就构建好了。
积分图构造好之后,图像中任何矩阵区域像素累加和都可以通过简单运算得到如图所示:
在这里插入图片描述
设D的四个顶点分别为α,β,γ,δ则D的像素和可以表示为
Dsum=ii(α)+ii(β)−(ii(γ)+ii(δ))D s u m=i i(\alpha)+i i(\beta)-(i i(\gamma)+i i(\delta))Dsum=ii(α)+ii(β)(ii(γ)+ii(δ)) Haar-like特征值无非就是两个矩阵像素和的差,同样可以在常数时间内完成。


1.3 计算Haar特征值

上面已经知道,一个区域的像素值的和,可以由该区域的端点的积分图来计算。由前面特征模板的特征值的定义可以推出,矩形特征的特征值可以由特征端点的积分图计算出来。以A矩形特征为例,如下图,使用积分图计算其特征值:
在这里插入图片描述
该矩形特征的特征值,由定义,为区域A的像素值减去区域B的像素值。
区域A的像素值:
ii(5)+ii(1)−ii(2)−ii(4)ii(5)+ii(1)−ii(2)−ii(4) ii(5)+ii(1)ii(2)ii(4)
区域B的像素值:
ii(6)+ii(2)−ii(5)−ii(3)ii(6)+ii(2)−ii(5)−ii(3) ii(6)+ii(2)ii(5)ii(3)
所以:该矩形特征的特征值
ii(5)+ii(1)−ii(2)−ii(4)−[ii(6)+ii(2)−ii(5)−ii(3)]ii(5)+ii(1)−ii(2)−ii(4)−[ii(6)+ii(2)−ii(5)−ii(3)] ii(5)+ii(1)ii(2)ii(4)[ii(6)+ii(2)ii(5)ii(3)] =[ii(5)−ii(4)]+[ii(3)−ii(2)]−[ii(2)−ii(1)]−[ii(6)−ii(5)]=[ii(5)−ii(4)]+[ii(3)−ii(2)]−[ii(2)−ii(1)]−[ii(6)−ii(5)] =[ii(5)ii(4)]+[ii(3)ii(2)][ii(2)ii(1)][ii(6)ii(5)]
所以,矩形特征的特征值,只与特征矩形的端点的积分图有关,而与图像的坐标无关。通过计算特征矩形的端点的积分图,再进行简单的加减运算,就可以得到特征值,正因为如此,特征的计算速度大大提高,也提高了目标的检测速度。
了解了特征值的计算之后,我们来看看不同的特征值的含义是什么。我们选取MIT人脸库中2706个大小为20*20的人脸正样本图像,计算如下图所示的Haar特征:
在这里插入图片描述
左边对应的人眼区域,右边无具体意义。
在这里插入图片描述
可以看到,图中2个不同Haar特征在同一组样本中具有不同的特征值分布,左边特征计算出的特征值基本都大于0(对样本的区分度大),而右边特征的特征值基本均匀分布于0两侧(对样本的区分度小)。所以,正是由于样本中Haar特征值分布不均匀,导致了不同Haar特征分类效果不同。显而易见,对正负样本区分度越大的特征分类效果越好,即红色曲线对应图中的的左边Haar特征分类效果好于右边Haar特征。
那么看到这里,应该理解了下面2个问题:
(1)在检测窗口通过平移+缩放可以产生一系列Haar特征,这些特征由于位置和大小不同,分类效果也不同;
(2)通过计算Haar特征的特征值,可以有将图像矩阵映射为1维特征值,有效实现了降维。


1.4 Haar特征值归一化

从上图我们可以发现,仅仅一个128大小的Haar特征计算出的特征值变化范围从-2000/+6000,
跨度非常大。这种跨度大的特性不利于量化评定特征值,所以需要进行“归一化”,压缩特征值范围。假设当前检测窗口中的图像像素为𝑖(𝑥,𝑦)i(x,y),当前检测窗口为𝑤∗ℎw∗h大小(例如上图中为20
20大小),OpenCV采用如下方式“归一化”:
1、计算检测窗口中图像的灰度值和灰度值平方和:
sum=∑i(x,y)sum=\sum i(x,y) sum=i(x,y)
sqsum=∑i2(x,y)sq_{sum}=\sum i^2(x,y) sqsum=i2(x,y)
2、计算平均值:
mean=sumw∗hmean = \frac{sum}{w*h} mean=whsum
sqmean=sqsumw∗hsq_{mean}=\frac{sq_{sum}}{w*h} sqmean=whsqsum
3、计算归一化因子:
varNormFactor=sqmean−mean2varNormFactor=\sqrt{sq_{mean}-mean^2} varNormFactor=sqmeanmean2


4、归一化特征值:
normValue=featureValuevarNormFactornormValue=\frac{featureValue}{varNormFactor} normValue=varNormFactorfeatureValue
之后使用归一化的特征值𝑛𝑜𝑟𝑚𝑉𝑎𝑙𝑢𝑒与阈值对比。


总结

从上面所述内容我们可以总结Haar分类器训练的五大步骤:
1、准备人脸、非人脸样本集;
2、计算特征值和积分图;
3、筛选出T个优秀的特征值(即最优弱分类器);
4、把这个T个最优弱分类器传给AdaBoost进行训练。
5、级联,也就是强分类器的强强联手。


2 实战

我们首先把上述的data文件夹复制到当前项目路径下,然后创建.py文件,例程如下:

import cv2
import numpy as np
haar_front_face_xml = './data/haarcascade_frontalface_default.xml'
haar_eye_xml = './data/haarcascade_eye.xml'# 1.静态图像中的人脸检测
def StaticDetect(filename):# 创建一个级联分类器 加载一个 .xml 分类器文件. 它既可以是Haar特征也可以是LBP特征的分类器.face_cascade = cv2.CascadeClassifier(haar_front_face_xml)# 加载图像img = cv2.imread(filename)# 转换为灰度图gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 进行人脸检测,传入scaleFactor,minNegihbors,分别表示人脸检测过程中每次迭代时图像的压缩率以及# 每个人脸矩形保留近似数目的最小值# 返回人脸矩形数组faces = face_cascade.detectMultiScale(gray_img, 1.3, 5)for (x, y, w, h) in faces:# 在原图像上绘制矩形img = cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)cv2.namedWindow('Face Detected!')cv2.imshow('Face Detected!', img)cv2.waitKey(0)cv2.destroyAllWindows()# 2、视频中的人脸检测
def DynamicDetect():'''打开摄像头,读取帧,检测帧中的人脸,扫描检测到的人脸中的眼睛,对人脸绘制蓝色的矩形框,对人眼绘制绿色的矩形框'''# 创建一个级联分类器 加载一个 .xml 分类器文件. 它既可以是Haar特征也可以是LBP特征的分类器.face_cascade = cv2.CascadeClassifier(haar_front_face_xml)eye_cascade = cv2.CascadeClassifier(haar_eye_xml)# 打开摄像头camera = cv2.VideoCapture(0)cv2.namedWindow('Dynamic')while True:# 读取一帧图像ret, frame = camera.read()# 判断图片读取成功?if ret:gray_img = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)# 人脸检测faces = face_cascade.detectMultiScale(gray_img, 1.3, 5)for (x, y, w, h) in faces:# 在原图像上绘制矩形cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)roi_gray = gray_img[y:y + h, x:x + w]# 眼睛检测eyes = eye_cascade.detectMultiScale(roi_gray, 1.03, 5, 0, (40, 40))for (ex, ey, ew, eh) in eyes:cv2.rectangle(frame, (ex + x, ey + y), (x + ex + ew, y + ey + eh), (0, 255, 0), 2)cv2.imshow('Dynamic', frame)# 如果按下q键则退出if cv2.waitKey(100) & 0xff == ord('q'):breakcamera.release()cv2.destroyAllWindows()if __name__ == '__main__':filename = 'test1.jpg'StaticDetect(filename)# DynamicDetect()

关于Datawhale


Datawhale是一个专注于数据科学与AI领域的开源组织,汇集了众多领域院校和知名企业的优秀学习者,聚合了一群有开源精神和探索精神的团队成员。Datawhale以“for the learner,和学习者一起成长”为愿景,鼓励真实地展现自我、开放包容、互信互助、敢于试错和勇于担当。同时Datawhale 用开源的理念去探索开源内容、开源学习和开源方案,赋能人才培养,助力人才成长,建立起人与人,人与知识,人与企业和人与未来的联结。



推荐阅读
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
  • 如何去除Win7快捷方式的箭头
    本文介绍了如何去除Win7快捷方式的箭头的方法,通过生成一个透明的ico图标并将其命名为Empty.ico,将图标复制到windows目录下,并导入注册表,即可去除箭头。这样做可以改善默认快捷方式的外观,提升桌面整洁度。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
author-avatar
cl云中皓
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有