效果图
闲扯:公司鼓励创新,做了个皮肤检测器,本来的想法是用手机外接wifi摄像头,用摄像头来拍摄皮肤图像的,后来找了几个wifi摄像头的厂家,因价格没谈拢,就不用wifi摄像头了,只用手机自带的相机来获取图像。
下面我写了个 demo 测试,如下图
目前实现了皱纹,油份,水份的检测算法,以后还想实现更多功能,比如痘痘数量,色斑检测,年龄预测等等。
人脸检测原理
人脸检测就是要判断一张图像是不是人脸,以及人脸的位置。
一张大图像由他的一张很小的子图像窗口,以一定的倍数扩大(比如每次扩大 1.2 倍)暴力搜索,最终能确定位置。
判断一张图像是不是人脸,先提取图像的 haar-like 特征值,交给 adaboost 算法,它能判断是不是人脸。
提取图片的 haar 特征
为什么要提取 haar 特征?
因为要用 adaboost 做分类,整张图塞进去计算量太大,提取一些主要的特征,能区分人脸非人脸就行。怎么提取 haar 特征?
有大牛已经给出方法了,看下面的模板,把模板放在图像上,用 白色区域的灰度和 减去 黑色区域的灰度和,得到的数值就是其中一个 haar 特征,把模板在图像上滑动,能计算出一组数值,例如(123, 234, -134, ...),这就是图像的 haar 特征了。
- 拓展:
图像区域快速求和,用积分图,对原图像进行一次积分,就能在O(1)时间算出区域像素和,大概写一下。
假设积分图是 F(x, y),求 Rect(x, y, w, h) 区域和。
sum = F(x+w, y+h) - F(x+w, y) - F(x, y+h) + F(x, y)
// F(x, y) 表示 Rect(0, 0, x, y) 的区域和,f(x, y) 表示 (x, y) 灰度级
// 后面加上 + F(x, y) 是因为发现 - F(x+w, y) - F(x, y+h) 减多了重叠的区域,所以就加回来。
// 生成积分图的状态转移方程:
F(x, y) = f(x, y) + F(x-1, y) + F(x, y-1) - F(x-1, y-1)
AdaBoost 算法
先来一个感性的认识。
每来一张图片,提取其特征值 X,用 AdaBoost 算法能判断出 X 是不是人脸。
AdaBoost 里面有很多个分类器对特征值 X 进行打分,分数有正有负,每个分类器还有个权值,最终把多个分类器给出的分数乘上他们自身的权值,全部累加起来,得到一个数值,如果是正数,表示属于 1 类,负数表示属于 -1 类。
假设 1 类是人脸类,-1 类是非人脸类,就能分出一张图是不是人脸了。
分类器可以理解为一个函数 G(x),输入特征值 x,返回数值 G(x)
AdaBoost 里面有多个弱分类器 (假设3个),组合成一个强分类器,像这样
每个弱分类器有权重
再来个sign(n)函数,sign 返回 n 前面的正负号,0和正数返回 1,负数返回 -1
拓展到 n,最终的判断公式为
输入一个图像的特征值 x,看看返回是 1 还是 -1,就可以判断出是人脸还是非人脸。
其他:w1, w2, w3 一开始不知道是多少,需要训练数据进行多轮的迭代后算出,训练过程就是调整权值的过程。训练完了后,就用这些权值组成的模型,对未知的图像进行分类。
具体的例子
有8组包含各种情况的特征值,作为训练数据。
特征值 x | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
类别 | 1 | 1 | -1 | -1 | -1 | 1 | 1 | 1 |
1.初始化 n 组训练数据的权值为 D(i) = 1 / n
所以 1 到 8 的权值为 1 / 8 = 0.125
D = (0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125)
2.多轮迭代
看训练数据
1, 2 属于 1 类
3, 4, 5 属于 -1 类
6, 7, 8 属于 1 类
2 到 3 的时候,由 1 变为 -1,是一个变化的地方,设定 x = 2.5,意思是在 2.5 的地方切一刀。
5 到 6 由 -1 变为 1,又是一个变化点,设定 x = 5.5
下面对 2.5,5.5 进行迭代
规则:有一个常数 c,先认为 x <= c 的元素为 1 类,x > c 的为 -1 类,然后计算有多少个数据被分错的。
误差率 e = 分错的元素的权值和。
如果误差率 e > 0.5,则反过来,认为 x <= c 的为 -1,x > c 的为 1。
误差率写成公式有点晦涩了,用文字描述比较直观,就是把被分类器分错的元素的和权值全部加起来,就是误差率了。
- 第一轮迭代
x = 2.5 时,先认为 x <= 2.5 为 1 类,x > 2.5 的为 -1 类
那么 6, 7, 8 三个元素被分错了,每个元素的权值为 0.125。
误差率为 6, 7, 8 的权值和:0.125 + 0,125 + 0.125 = 0.375。
x = 5.5 时,先认为 x <= 5.5 为 1,x > 5.5 为 -1
那么 3, 4, 5, 6, 7, 8 六个元素被分错。
误差率为 6 乘 0.125 = 0.75 大于 0.5 了。
那么反过来 x <= 5.5 为 -1,x > 5.5 为 1,此时只有 1, 2 被分错。
误差率为 1, 2 的权值和:0.125 + 0.125 = 0.25。
选个误差小的 0.25,得出第一个分类器 G1
计算 G1 权重公式:(e 是误差率)
该函数图像:
由图像可知误差率 e 越小,w 权值越大。
G1 的误差率 e1 = 2 / 8 = 0.25
G1 权重 w1 = 1 / 2 * log (0.75 / 0.25) = 0.23856
w1 就描述了 G1 的重要程度,或者说对一个特征值被分成哪一类的话语权的大小。
接下来要更新以下元素权重:
第一轮:D = (0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125, 0.125)
假设第一轮的 D = (d1, d2, d3, ..., dn),是已知的
下一轮的 D = (k1, k2, k3, ..., kn),是要计算的
权重更新公式:
其中常数 Z 是
ki 本轮要计算的元素的权重
di 上一轮已经计算出的权重
w 上一轮分类器的权重
G(xi) 上一轮分类器对第 i 个特征的分类结果
yi 第 i 个元素的类别,1 或者 -1
e 自然常数,约 2.71828
Z 归一化用的常数
按照上面方式,算出新的权重为
D = (0.174721, 0.174721, 0.108426, 0.108426, 0.108426, 0.108426, 0.108426, 0.108426)
可以看出 1, 2 被分错后,它的权值增大了,那么如果后面的分类器再把 1, 2 分错,产生的误差率会更大,因为误差率根据权值和算出的,所以权值大的,会被重点考虑。
- 第二轮迭代
x = 2.5 时,误差率为 6, 7, 8 权值和:0.325278
x = 5.5 时,误差率为 1, 2 的权值和:0.349442
选个误差率小的,即 0.325278,x = 2.5 的,得出第二个分类器 G2
G2 的误差率 e2 = 0.325278
G2 权重 w2 = 0.15844
此时,已经得到两个弱分类器,代入 adaboost 算法的公式中
得到模型:
- 测试该模型
特征值 x | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
类别 | 1 | 1 | -1 | -1 | -1 | 1 | 1 | 1 |
G(x) | -1 | -1 | -1 | -1 | -1 | 1 | 1 | 1 |
分析 G(x) 得出的结果,发现 1,2 被分错了,还是有误差,说明本例子迭代两次是不够的,还需要更多轮的迭代,减小误差,直到模型 G(x) 在训练数据上的误差为0,再继续迭代下去误差会收敛于一个常数。(详见最后的参考文献)
训练好了一个误差很小的模型 G(x),就可以用该模型去对未知的图像进行分类,分为人脸类和非人脸类,即可检测出一张图像是不是人脸。