日萌社
人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新)
人脸识别功能实现的原理介绍与算法介绍
人脸识别:人脸数据集
Adaboost 人脸检测:Haar特征及积分图、分类器的级联
关键点提取:face_recognition、疲劳检测、人脸校准、人脸数据库
Haar-like 特征可以分为边缘特征、中心特征、对 角线特征以及线性特征。 (a)即为边缘特征,其用于检测目标图像在边缘上 的变化信息,如人脸边缘与背景的灰度变化,人 头发与人脸额头之间的灰度变化等; (b)即为线性特征,其用于检测目标图像在水平以 及垂直方向上的变化信息,如人的鼻梁两侧肤色 要比鼻梁上的颜色深等; (c)即为中心特征和对角线特征,其用于检测对角线上以及 矩形模板外围和中心之间的变化信息,如人的眼睛比人脸 的其他部分颜色要深,嘴巴要比其周围肤色颜色要深等。 并且在这些水平垂直的基础上,又添加了 45°方向的矩形 模板。通过采用这三种不同形式的矩形模板来表示人脸区 域的特征,达到区分人脸部分与背景部分并将两者分割的 目的。 在OpenCV接口中,实现了Haar/LBP/HOG等多种特征, 以Haar特征为例介绍,Haar特征最先由Paul Viola等人 提出,后经过Rainer Lienhart等扩展引入45°倾斜特征, 成为现在OpenCV所使用的的样子共计14种Haar特征, 包括5种Basic特征、3种Core特征和6种Titled(即45°旋 转)特征。 人脸检测分类器=haar-like (特征)+CART(弱)+ Adaboost(强) + Cascade(级联) Haar特征可以在检测窗口中由放大+平移产生一系列子 特征,但是白:黑区域面积比始终保持不变。 以x3特征为例,在放大+平移过程中白:黑:白面积比始终是1:1:1。 首先在红框所示的检测窗口中生成大小为3个像素的最小x3特征;之后分别沿着x和y平移产生 了在检测窗口中不同位置的大量最小3像素x3特征;然后把最小x3特征分别沿着x和y放大,再 平移,又产生了一系列大一点x3特征;然后继续放大+平移,重复此过程,直到放大后的x3和 检测窗口一样大。这样x3就产生了完整的x3系列特征。 将矩形作为人脸检测的特征向量,称为矩形特征。本算法选取了最简单的5个矩形特征模板进行训练,以得到 一套用于人脸检测的最适合的矩形特征,事实证明,虽然这种特征选取方法的训练速度时间较长,但是检测 效率很高。 矩形特征在人脸图像上的特征匹配: 上行是 24×24 像素窗口内选出的矩形特征, 下行是 24×24 像素分辨率的人脸图像与上行选出的矩形特征的匹配。 矩形特征对一些简单的图形结构,比如边 缘、线段,比较敏感,但是其只能描述特 定走向(水平、垂直、对角)的结构,因 此比较粗略。如图,脸部一些 特征能够 由矩形特征简单地描绘,例如,通常,眼 睛要比脸颊颜色更深;鼻梁两 侧要比鼻 梁颜色要深;嘴巴要比周围颜色更深。
Haar特征及积分图
对于一个24×24像素分辨率图像,其内的矩形特征数量160000多个,必须通过特定算法选择合适的矩形特 征,并将其组合成强分类器才能检测人脸。 满足(s, t)条件的矩形称为条件矩形: (1) x 方向边长必须能被自然数 s 整除(能均等分成 s 段) (2) y 方向边长必须能被自然数 t 整除(能均等分成 t 段) 直观列举了几种常用的不同尺寸窗口中包含的 Haar 矩形特征总量, 随着窗口边长的不断扩大, 特征总量呈现出近似指数增长趋势 。特征数量如 此庞大,势必会给后续的特征值计算过程带来困 难,甚至影响算法的时效性。 数量庞大的特征数量在作为人脸特征时还需完成 从形到值的转化 ,接下来将讨论特征值的求解过 程。 了解了大量的Haar特征用于训练和检测时所面临的计算量问题,接下来的问题是如何计算Haar特征值。 Haar特征值=整个Haar区域内像素和×权重 + 黑色区域内像素和×权重:对于x3和y3特征,weightall = 1,weightblack = -3; 对于point特征, weightall = 1,weightblack = -9; 其余11种特征均为 weightall=1,weightblack = -2。 例如: 对于x2特征:(黑 + 白) * 1+黑 * (-2) = 白 - 黑; 对于Point特征:(黑 + 白) * 1 + 黑 * (-9) = 白 - 8 * 黑。 Haar特征如何保存? 对应的,在OpenCV XML文件中,每一个Haar特征都被保存在2~3个形如: <x y width height weight>的标签中: 其中x和y代表Haar矩形左上角坐标(以检测窗口左上角为原点),width和height代表矩形的宽和高,而weight 则对应了上面说的权重值,例如图中的左边x2类型的Haar特征应该为<4 2 12 8 1.0>(整个Haar,权重1)和<4 2 12 4 -2.0>(黑色区域,权重-2)。 为什么要设置这种加权相减,而不是直接相减? 请仔细特征,不难发现x3、y3、point特征黑白面积不相等,而其他特征黑白面积相等。设置权值就是为了抵消面积 不等带来的影响,保证所有Haar特征的特征值在灰度分布绝对均匀的图中为0。 选取MIT人脸库中2706个大小为20*20的人脸正样本图像,计算左图位置的Haar特征值,结果如右图: 可知,2个不同Haar特征在同一组样本中具有不同的特征值分布,左边特征计算出的特征值基本都大于0,而右边特 征的特征值基本均匀分布于0两侧(分布越均匀对样本的区分度越小)。 所以,正是由于样本中Haar特征值分布不同,导致了不同Haar特征分类效果不同。 显而易见,对样本区分度越大的特征分类效果越好,即红色曲线对应的左边Haar特征分类效果好于右边Haar特征: 1,在检测窗口通过平移+放大可以产生一系列Haar特征,这些特征由于位置和大小不同,分类效果也各异; 2,通过计算Haar特征的特征值,可以有将图像矩阵映射为1维特征值,有效实现了降维。 Haar特征值标准化 从图中发现,仅仅一个12*8大小的Haar特征计算出的 特征值变化范围从-2000~+6000,跨度非常大。 这种跨度大的特性不利于量化评定特征值,所以需要进 行“标准化”,压缩特征值范围。 假设当前检测窗口中的图像为i(x,y),当前检测窗口为 w*h大小(例如图6中为20*20大小),OpenCV采用如 下方式“标准化”: Haar特征值标准化 如图中蓝色为检测窗口,红色为标准化过程中使用到的 像素。 其实如何标准化并不重要, 重要的是检测和训练时的方 法一定要一致 ,否则可能会由于标准化不同带来的误差 导致模型无法工作。 以OpenCV自带的人脸分类器haarcascade_frontalface_alt2.xml为例,其中存储了超过1000个大小和位置都 不相同的Haar特征。 在运算中,伴随着检测窗口的移动,如何快速计算Haar特征值就成了一个很重要的问题。在设计 Haar+AdaBoost算法时,Paul Viola等人就提出积分图。 对于灰度图像中任意一点image(x,y),OpenCV定义其积分图为sum(x,y)为: 其中(x1,y1)、(x2,y2)、(x3,y3)和(x4,y4)依次代表图1中image的1 2 3 4点的图像坐标。 需要说明,在计算D区域灰度和时sum(x1,y1)深蓝色区域被减去了2次,最后需要补上。 显然可以通过此方法快速计算图像中任意位置和大小区域的灰度和,即通过积分图只需要做有限次操作就能获 得任意位置的Haar特征值: 区域 D 的像素值,可以利用1、2、3、4点的积分图来计算。 区域 D 的像素和可以用积分图计算为: 区域 D 的像素值= (ii4 + ii1)− (ii2 + ii3) 因为: ii 1=区域 A 的像素值 ii2 =区域 A 的像素值+区域 B 的像素值 ii3 =区域 A 的像素值+区域 C 的像素值 ii4=区域 A 的像素值+区域 B 的像素值+区域 C 的像素值+区 域 D 的像素值 由上面方程得到: 区域 D 的像素值= (ii4 + ii1)− (ii2 + ii3) 以特征模板1为例,此特征模板的特征值为: 区域 A 的像素值-区域 B 的像素值 由刚才的证明可知: 区域 A 的像素值= ii1 + ii4 − (ii2 + ii3) 区域 B 的像素值= ii6 + ii3 − (ii4 + ii5) 所以此特征模板的特征值为: (ii4 −ii3 )−( ii2−ii1 )+( ii4 −ii3 )− (ii6 −ii5) 矩形特征的特征值计算,只与此特征端点的积分 图有关 。 1, 矩阵特征的特征值计算,只与此特征端点的 积分图有关,而与图像坐标图无关 。 2,不管此矩形的尺度如何,特征值的计算所耗 费的时间都是固定的,而且都只是简单的加减运 算。 正因如此,所以积分图的引入, 用积分图快速计 算矩形特征的特征值,减少了计算量,大大地提 高了检测的速度 。分类器的级联 依据 AdaBoost 算法的训练过程可以分为三步: 1, 初始化训练样本的权值分布。 2, 随后进行训练,在训练过程中,如果某一个样本点被正确的分类,那么在接下来的训练中其权重值会被降 低,否则其权重值被提高。 3, 用更新权值后的样本集构造下一个分类器,重复上述过程直到满足训练的目标需求,最后经过m 次训练后, 得到各个弱分类器h 1 ,h 2 ,h 3 ,...,h m ,增加分类误差小的并降低分类误差大的弱分类器的权重,得到相应的权重 α 1 ,α 2 ,α 3 ,...,α m 并将各个弱分类器组合成一个具有较强分类能力的分类器α 1 h 1 +α 2 h 2 +α 3 h 3 +...+α m h m 。 (2) 进行 T 轮训练; 1,首先将所有样本权重初始化为样本的概率分布,之后进行迭代,每一轮迭代都需要更新权重, 按照降低正确分类的样本权重,增加错误分类的样本权重,从而在接下来的一次迭代中更加重 视被分类器分错的样本,之后通过多次有针对性的迭代后,生成的每个新的分类器基本上都可 以进行效果较好的分类。 2,按照分类效果的好坏给每个分类器分配一个权重值,即效果较好的分类器权重较大。 3,将一系列经过加权的弱分类器进行组合便可得到一个强分类器。 OpenCV中的Adaboost级联分类是树状结构,如 图,其中每一个stage都代表一级强分类器。 当检测窗口通过所有的强分类器时才被认为是目标, 否则拒绝。 实际上,不仅强分类器是树状结构,强分类器中的 每一个弱分类器也是树状结构。 其中<features>标签存储了所有的Haar特性:弱分类器结构 Haar特征和弱分类器之间的关系很简单: 一个完整的弱分类器包括: 若干个Haar特征 + 和Haar特征数量相等的弱分类器阈值 若干个leftValue 若干个rightValue 这些元素共同构成了弱分类器,缺一不可。 haarcascade_frontalface_alt2.xml的弱分类器Depth=2,包含了2种形式,如图: 左边形式包含2个Haar特征、1个leftValue、2个rightValue和2个弱分类器阈(t1和t2) 右边形式包括2个Haar特征、2个leftValue、1个rightValue和2个弱分类器阈值弱分类器结构以图左边的形式为例: 计算第一个Haar特征的特征值haar1,与第一个弱分类器 阈值t1对比,当haar1<t1时,进入步骤2;当haar1>t1时, 该弱分类器输出rightValue2并结束。 计算第二个Haar特征值haar2,与第二个弱分类器阈值t2 对比,当haar2<t2时输出leftValue;当haar2>t2时输出 rightValue1。 即弱分类器的工作方式:通过计算出的Haar特征值与弱分 类器阈值对比,从而选择最终输出leftValue和rightValue 值中的哪一个。在OpenCV中,强分类器是由多个弱分类器“并列”构成,即强分类器中的弱分类器是两两相互独立的。 在检测目标时, 每个弱分类器独立运行并输出cascadeLeaves[leafOfs - idx]值,然后把当前强分类器中每一个弱 分类器的输出值相加 ,即: sum += cascadeLeaves[leafOfs - idx] 之后与本级强分类器的stageThreshold阈值对 比, 当且仅当结果sum>stageThreshold时,认 为当前检测窗口通过了该级强分类器 。当前检测 窗口通过所有强分类器时,才被认为是一个检测 目标。 可以看出, 强分类器与弱分类器结构不同,是一 种类似于“并联”的结构,称其为“并联组成的 强分类器”。 sum = cascadeLeaves[leafOfs0 - idx0]+cascadeLeaves[leafOfs1 - idx1]+cascadeLeaves[leafOfs2 - idx2]那么这些Haar特征、leftValue、rightValue和弱分类器阈值t都是如何存储在xml文件中的? 不妨来看haarcascade_frontalface_alt2.xml文件中的第一级的第三个弱分类器,如图中的弱分类器恰 好是图3中左边类型,包含了<internalNodes>和<leafValues>两个标签。 其中<leafValues>标签中的3个浮点数由左向右依次是rightValue2、leftValue和rightValue1;而 <internalNodes>中有6个整数和2个浮点数,其中2个浮点数依次分别是弱分类器阈值t1和t2; 首先来看两个浮点数前的整数,即0和1。 这两个整数用于标示所属本弱分类器Haar特征存储在<features>标签中的位置。比如数值0表示该弱分类器的 haar1特征存储在xml文件下面<features>标签中索引第0个的位置,而<internalNodes>的其他4个整数0、1和-1、 -2则用于控制弱分类器树的形状。在运行时,OpenCV会把1赋值给当前的node.left,并把0赋值给node.right例如<internalNodes>的其他4个 整数1、0和-1、-2则用于控制弱分 类器树的形状。 在运行时,OpenCV会把1赋值给 当前的node.left,并把0赋值给 node.right 为了找到图像中不同位置的目标,需要逐次移动检测窗口 (窗口中的Haar特征相应也随着移动),这样就可以遍历 到图像中的每一个位置; 而为了检测到不同大小的目标, 一般有两种做法 : 逐步缩小图像 or 逐步放大检测窗口 :缩小图像就是把图像 按照一定比例逐步缩小然后滑动窗口检测 如图: 放大检测窗口是把检测窗口长宽按照一定比例逐步放大, 这时位于检测窗口内的Haar特征也会对应放大,然后检测。 对检测结果NMS 考虑这样的情况:一个被检测为目标的窗口,其附近窗口也应该被检测到。 下图展示检测一副含有人脸图像的结果, 左边为合并检测结果窗口之前的结果,右边为合并之后的结果 。 所以有必要对重叠的检测结果窗口进行合并,同时剔除零散分布的错误检测窗口。 该功能其实就是 NMS(non-maximum suppression) 。 NMS(non-maximum suppression): 1,首先建立Rect对象的初始结构 2,然后遍历整个集合,判断每2个窗口相似性,若相似则将这2个窗口合并为一个; 3,运行完步骤2后应该出现几个相互间不相似的窗口,当合并后窗口的原未合并子窗口数量小于阈值 minNeighbors时,丢弃该合并后窗口(认为这是零散分布的误检); 4,之后剩下若干组由大量重叠窗口组成的大窗口,分别求每个“大窗口”中的所有窗口位置的平均值 作为最终检测结果 。 一般在实际应用中,希望precision和recall都很高。还是以人脸检测为例,不妨假设某张图中有10个 人脸。 若检测器只发现了1个人脸,此时precision=1 虽然很高,但是 recall=0.1 非常低。 若检测器发现了50个人脸(假设包含了10个真人脸),此时 recall=1 很高,但是 precision=10/50=0.2 很低。 所以只有precision和recall都比较高时,讨论检测器的参数才有意义。但是 现实情况中鱼和熊掌不可 兼得,很难做到precision和recall都很高,所以会绘制precision-recall曲线评估检测器 。 前面使用较少的类特征就可以拒绝大部分的非人脸 样本,而几乎所有的人脸样本都能获得通过。 并且,由于 前面的层使用的类特征个数较少,那么淘汰 非人脸样本的速度非常快,只有含有人脸的检测窗口 才能通过前面的强分类器 。 虽然后面的类特征增加了,但是输入图像中真正有人 脸的匹配窗口非常少,所以真正能通过所有层级级联 分类器的窗口数目非常少。这样就大大加速了人脸 检测的速度。每一个stage都要进行上述收集+分类过程,所以实际中每一个stage所使用的训练样本也都不一样! 首先计算所有Haar特征对这numPos+numNeg个样本的 特征值,排序后分别保存在的vector中: 然后遍历每个存储特征值的vector,至此,已经有很多弱 分类器了。 但是哪一个弱分类器最好呢?所以要挑选最优弱分类器放 进stage中。现在有人脸样本2000张,非人脸样本4000张,这些样本都经过了归一化,大小都是24X24的图像。 那么,对于160,000+中的任一特征fi,我们计算该特征在这2000人脸样本、4000非人脸样本上的值,这样就 得到6000个特征值。 将这些特征值排序,然后选取一个最佳的特征值,在该特征值下,对于特征来说,样本的加权错误率最低 。选 择160,000+个特征中,错误率最低的特征,用来判断人脸,这就是一个弱分类器,同时用此分类器对样本进行 分类,并更新样本的权重。 一个弱分类器,实际上就是在这160,000+的特征中选取一个特征,用这个特征能够区分出人脸or非人脸, 且错误率最低。 让所有弱分类器对搜索区域做表决,再对表决结果按照其错误率进行加权求和即为某个stage的输出结果 mo。 构成强分类器的弱分类器数量越少,搜索人脸区域的速度会越快但误检率也会随之增加 ;相反,弱分类器 数量越多,搜索人脸区域的精确度越高,但检测时间会随之增加。 更新当前训练的stage中每个样本的权重 对numPos+numNeg个权重按照如下公式更新权重(注意更新后需要对权重进行归一化)。计算当前的强分类器阈值stageThreshold sum = cascadeLeaves[leafOfs0 - idx0]+cascadeLeaves[leafOfs1 - idx1]+cascadeLeaves[leafOfs2 - idx2]1,使用当前的stage中已经训练好的弱分类器去检测样本中的每一TP,计算弱分类器输出值之和保存在eval中。 2,对eval升序排序 3,以eval[thresholdIdx]作为stage阈值stageThreshold,显然TP越多估计的stageThreshold越准确。 回顾一下,整个分类器的训练过程可以分为以下几个步骤: 1,寻找TP和FP作为训练样本 2,计算每个Haar特征在当前权重下的Best split threshold+leftvalue+rightvalue,组成了一个个弱分类器 3,通过WSE寻找最优的弱分类器 4,更新权重 5,按照minHitRate估计stageThreshold (-minhitrate每个阶段分类器需要的最小的命中率,总的命中率为min_hit_rate的number_of_stages次方) 6,重复上述1-5步骤,直到falseAlarmRate到达要求,或弱分类器数量足够。 停止循环,输出stage。 7,进入下一个stage训练遍历过程中,找到error最小的threshold作为当前vector的Best spilt,以及对应的leftvalue和 rightvalue保存下来。 可以看出这里的Best spilt threshold就是弱分类器阈值,与Haar特征+leftvalue+rightvalue 共同构成一个完成的弱分类器。 遍历过程中,找到error最小的threshold作为当前vector的 Best spilt,以及对应的leftvalue和rightvalue保存下来。 可以看出这里的Best spilt threshold就是弱分类器阈值,与 Haar特征+leftvalue+rightvalue共同构成一个完成的弱分类器。这里还需要注意,有关人脸检测领域研究者发表的论文中提到的级联指的是强分类器的级联。 如图 ,级联结构一般包括 8 个左右强分类器,每个强分类器又由 10-20个(可以自行选择)弱分类器 组合而成。 如图,级联分类器内部排在前面的强分类器由一些最能够代表人脸特征强的少量弱分类器组成,这样可快 速过滤掉大部分非面部区域且加快搜寻速度。 排在后面的强分类器构造越来越复杂,对检测子窗口要求越来越严格,用来判断较难识别的区域。Adaboost人脸检测: 1,积分图像,利用它可以快速计算特征。 2,利用机器学习算法将由特征生成的简 单分类器叠加成为强分类器。 3,将强分类器并联而成为级联分类器用 于检测人脸。 4,Haar-like 特征对一些简单的灰度变 化,如边缘和线段等的效果比较好,但 是如果图像存在复杂的纹理环境,则会 降低其检测效果,导致检测率下降。1,层级级联分类器的设计要综合考虑检测率、误检率和漏检率之 间的平衡。如果增加检测率,即让更多的窗口通过前期的强分类器, 那么就会导致很高的误检率。 2,一个简单的在增加检测率的同时降低误检率的方法是增加层级 级联分类器的级数,但是这会大大增加人脸检测的时间,同时由于层 级级联分类器越往后越复杂,包含越多的弱分类器,因此也就具有越 强的分辨人脸的能力,就可能在过滤掉所有的非人脸窗口的同时漏 掉部分人脸窗口,这样就导致在误检率下降的同时,也会增加漏检率 的上升。 3,总的来说增加层级的级数能够降低误检率,但同时会降低检测率 和增长检测的时间。基于的人脸检测算法的训练过程分为: 1,收集训练样本集,包括人脸样本和非人脸样本,并对 样本进行预处理,包括将彩色图转换为灰度图、图像缩 放成标准大小、归一化等; 2,利用积分图算法从训练样本集中提取所有的类特征; 3,把类特征作为训练过程的输入,训练过程按照学习 算法进行,训练的目标是最终生成一个层级级联分类器。 这个层级级联分类器是由若干个强分类器组成的, 每个 强分类器则由若干个若分类器组成,每个若分类器对应 一个按照一定规则选择出来的类特征 。