如何开发Java版人脸跟踪应用?本篇给出了设计大纲,并解释了相关的重要知识点
欢迎访问我的GitHub
这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
本篇概览
- 本篇是《Java版人脸跟踪三部曲》系列的第二篇,前文体验了人脸跟踪的效果,想要编码实现这样的效果,咱们需要做好设计工作,也就是本篇的任务
- 本篇主要包含以下内容:
- 核心逻辑
- 重要知识点:HSV、HUE
- 重要知识点:反向投影
- 重要知识点:CamShift
- 重要知识点:JavaCV的API支持
- 如何开局?
- 前文的完整功能分析
- 异常处理
- 期待下一篇的实战
核心逻辑
- 本篇没有编码和操作实战,会略显枯燥,所以提前小结人脸跟踪核心逻辑,如此就算您受不了欣宸的啰嗦提前关闭网页,好歹也能带走些干货:
- 如下图所示,人脸跟踪的核心逻辑,其实就是先拿人脸直方图hist,然后将每一帧都转为hist的概率分布图(也叫反向投影),再用MeanShift算法在图上做迭代计算,结果就是人脸位置:
- 拿到每一帧的人脸位置后,在人脸上添加一个矩形框,此时,在预览窗口看到的效果就是视频中人脸上始终有矩形框,实现了跟踪的效果
- 虽然尽可能简短的讲完了核心逻辑,但此时的您可能有一些疑问,例如:
- Hue分量是啥?
- 反向投影是啥?
- MeanShift又是啥?
- 前文提到过CamShift,这会儿咋又不提了?
- 没错,上面几个疑问就是人脸跟踪功能依赖的关键技术,接下来咱们都简单了解一下吧
重要知识点:HSV、HUE
- HSV:如下图,HSV是一种直观的颜色空间,把色调分布到一个圆盘上,Hue表示角度,所以Hue的值就代表一个具体的色调,然后,Saturation看做饱和度(我的感觉是添加黑色),把Value看做亮度(我的感觉是添加白色),刚才提到的Hue分量,其实就是指Hue的值,(Saturation和Value的值在后面的算法中不会用到)
- 再来仔细看看圆盘中Hue的值对应的色调:
重要知识点:反向投影
- 在使用JavaCV的CamShift算法API时,最重要的入参就是反向投影,每一帧最终都会被转成反向投影,也就是前面提到的用人脸Hue分量的直方图将第X帧转化成色彩概率分布图
- 反向投影图是用输入图像的某一位置上像素值(多维或灰度)对应在直方图的一个bin上的值来代替该像素值
- 反向投影在OpenCV中会经常见到,一般使用场景是在一个图像中查找特定图像的最匹配点或区域,或者说定位目标图像出现在指定图像的位置
- 来看看用一张图片制作反向投影的过程,如下所示,先根据人脸得到直方图,然后对每一张图片都用这个直方图去计算出反向投影图(也就是拿着人脸直方图,去每一帧图片中计算人脸在此图片中的色彩概率分布),JavaCV为我们准备好了API(Imgproc.calcBackProject),我们只需准备好API所需参数即可:
- 有了上面的流程,就能对每帧图片做反向投影,得到人脸在这张图片上的概率分布图,然后用MeanShitf算法对这个概率分布图做迭代计算,直到其收敛或者到达最大迭代次数,确定人脸在图片上的位置
重要知识点:CamShift算法
- 实现人脸跟踪的关键是CamShift,全称ContinuouslyAdaptive Mean Shift,即连续自适应的MeanShift算法
- Mean Shift算法是一种无参密度估计算法,不需要任何先验知识而完全依靠特征空间中样本点的计算其密度函数值,在很多领域都有成功应用,例如图像平滑、图像分割、物体跟踪等,本篇不会展开细说Mean Shift算法,就用下面这幅图简单说说,
- 上图每个圆心是一个质心,
- 以质心为原点画一个圆圈,圆圈内有很多红点
- 圆圈内每个点与圆心构成一个向量,把圆圈内向量相加,得到新的向量就是meanshift向量,即黄色箭头
- 以meanshift向量的重点为圆心,再画一个圆圈,在此圆圈内执行步骤3
- 不断重复上述过程,着该向量移动便能找到密度最大处,就是最终结果
- 向量-> 移动 -> 向量 -> 移动,这和梯度下降有些相似之处啊
- 以上就是meanshif算法,而将meanshift算法扩展到连续图像序列,就是camshift,它将视频的连续帧做meanshift
计算,用上一帧结果作为下一帧meanshift算法搜索窗的初始值,来调整下一帧的中心位置和窗体大小,如此迭代下去,就可以实现对目标的跟踪。 - 对应到OpenCV的实现中,就是输入一个图像(probImage),再输入一个开始迭代的窗口(window),以及迭代条件(criteria),而输出,就是迭代完成的位置(RotatedRect);
重要知识点:JavaCV对CamShift的支持
- 关于核心功能的理论已经聊得七七八八了,再来看看JavaCV对核心知识点提供了哪些具体的API支持,如下表所示,前面涉及到的关键技术都覆盖到了:
序号 | API | 作用 |
---|---|---|
1 | Imgproc.cvtColor | 从摄像头拿到的帧,其颜色空间是RGB格式的,需要转为HSV格式 |
2 | Core.mixChannels | 将HSV图片的Hue分量提取到另一个Mat中 |
3 | Imgproc.calcHists | 生成直方图 |
4 | Imgproc.calcBackProject | 生成反向投影 |
5 | Video.CamShift | 在反向投影图上执行CamShift计算 |
- 至此,核心技术算是分析完了,但仅有核心技术是不够的,需要有主程序、分支逻辑、异常处理等诸多努力,才能实现完整的功能,接下来就以开发者的视角,开始咱们的开发设计
- 首先要搞清楚的是:如何确定最初的那个人脸?
如何开局?
- 在设计过程中,咱们要面临的第一个问题就是如何开局?换句话说:从哪里拿到人脸,用于生成直方图,并找好位置作为下一帧做CamShift计算的起始位置
- 如果您之前在网上搜索过CamShift的文章,会发现大多都是用户用鼠标在预览窗口选定一个区域,然后程序取这个区域作为跟踪对象
- 但是,欣宸这里不会沿用上述手动选择的方式,如果您之前看过《JavaCV的摄像头实战》系列,会发现该系列经常用到JavaCV提供的人脸检测功能,因此,咱们继续使用这个人脸检测功能来开局
- 简单来说,当程序运行后,如果摄像头中出现了人脸,那么该人脸就被自动作为跟踪对象,会被计算Hue直方图,并且人脸位置也是下一帧做CamShift计算的起始位置
- 为了简单起见,假设摄像头中只会出现一个人脸,代码处理也只针对一个人脸的场景
- 如果您想了解人脸检测的更多细节,请参考《JavaCV的摄像头实战之八:人脸检测》