如何开发Java版人脸跟踪应用?本篇给出了设计大纲,并解释了相关的重要知识点

欢迎访问我的GitHub

这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos

本篇概览

  • 本篇是《Java版人脸跟踪三部曲》系列的第二篇,前文体验了人脸跟踪的效果,想要编码实现这样的效果,咱们需要做好设计工作,也就是本篇的任务
  • 本篇主要包含以下内容:
  1. 核心逻辑
  2. 重要知识点:HSV、HUE
  3. 重要知识点:反向投影
  4. 重要知识点:CamShift
  5. 重要知识点:JavaCV的API支持
  6. 如何开局?
  7. 前文的完整功能分析
  8. 异常处理
  9. 期待下一篇的实战

核心逻辑

  • 本篇没有编码和操作实战,会略显枯燥,所以提前小结人脸跟踪核心逻辑,如此就算您受不了欣宸的啰嗦提前关闭网页,好歹也能带走些干货:
  • 如下图所示,人脸跟踪的核心逻辑,其实就是先拿人脸直方图hist,然后将每一帧都转为hist的概率分布图(也叫反向投影),再用MeanShift算法在图上做迭代计算,结果就是人脸位置:

  • 拿到每一帧的人脸位置后,在人脸上添加一个矩形框,此时,在预览窗口看到的效果就是视频中人脸上始终有矩形框,实现了跟踪的效果
  • 虽然尽可能简短的讲完了核心逻辑,但此时的您可能有一些疑问,例如:
  1. Hue分量是啥?
  2. 反向投影是啥?
  3. MeanShift又是啥?
  4. 前文提到过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算法,就用下面这幅图简单说说,

  1. 上图每个圆心是一个质心,
  2. 以质心为原点画一个圆圈,圆圈内有很多红点
  3. 圆圈内每个点与圆心构成一个向量,把圆圈内向量相加,得到新的向量就是meanshift向量,即黄色箭头
  4. 以meanshift向量的重点为圆心,再画一个圆圈,在此圆圈内执行步骤3
  5. 不断重复上述过程,着该向量移动便能找到密度最大处,就是最终结果
  • 向量-> 移动 -> 向量 -> 移动,这和梯度下降有些相似之处啊
  • 以上就是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的摄像头实战之八:人脸检测》

前文的完整功能分析(重要)

  • 咱们在前文体验的是一个功能完整的java应用,为了编码实现这个应用,自然是要先分析一下这个应用的主要流程
  • 来看看完整的应用主流程,如下图,检测到人脸后,就用此人脸生成直方图,对之后的每一帧都用反向投影+CamShift计算人脸位置,如果位置有效就表示跟踪成功,在图上添加矩形框,如果位置无效,表示跟踪失败(例如人已经离开摄像头),此时再不断的检测每一帧有没有人脸,一旦检测到,就重复前面的直方图和CamShift计算逻辑:

  • 以上就是主流程了,也就是大部分时间中应用的运行状态,相信此刻的您已经受够了这些文字和图表,迫不及待的想要敲打键盘,写出自己心目中的人脸跟踪应用,但我还是要强行劝您一句:咱们把异常流程也梳理和罗列一下,否则程序运行的时候会出现各种灵异现象,十分钟写代码,一小时查问题...

异常处理

  • 在实际运行过程中,可能会遇到以下六个问题:
  1. 提前准备必要文件之一,opencv在windows环境的动态链接库,下载地址(不用积分):

  1. 提前准备必要文件之二,人脸检测的模型文件,下载地址:https://raw.github.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_alt.xml
  2. native方法异常:BGR实例转为javacv的RGBA时,opencv_imgproc.cvtColor可能抛出异常,所以要注意捕获,避免程序退出
  3. JavaCV中,最常用的类来自org.bytedeco.opencv.opencv_core这个包,然而,在计算直方图、反向投影、CamShift的时候,大部分参数又来自org.opencv.core这个包,因此从摄像头取得的帧相关的数据对象,都要转换成另一个包下面的同名对象,才能顺利的执行人脸跟踪操作
  4. 人脸跟踪的时候,如何判断跟丢了?正常情况下,CamShift返回的是一个有效的矩形,人不再出现的帧,CamShift计算其反向投影的时候,返回的矩形的长和宽都小于等于零,但实际测试的时候,发现人脸消失后,CamShift还可能返回一个很小的矩形,这显然是必须要丢弃的,因此,判断是否跟丢的逻辑,我这里就改成:长或者宽比上一次的变化率是否超过百分八十,实测效果尚可,您也可以自行调整这个参数
  5. 假设人脸检测的结果是50*60的矩形,能将整个人脸包括在此矩形中,但CamShift计算得到的矩形就未必是50*60了,一般高度会更大,导致将人脸之下的脖子也包括进来,而且头发上面会包括进来,此刻,您可以按照自己的业务需求来调整这个矩形,我这里是将位置向下移动(不把头发包括进来),再把宽度的值设置成高度,这样看起来与人脸检测的结果比较接近,调整前后的效果如下图所示:

  • 以上就是之前的开发过程中遇到的典型问题,可见如果没有事先准备,怕是每个问题都能将爱学习的您折磨得痛苦不堪...

期待下一篇的实战

  • 至此,图文并茂的设计篇已全部完成,和愉快的编码相比,这种设计和准备工作既枯燥且辛苦,但却是一个功能健全的应用不可或缺的一部分,只希望本篇能为您提供足够的理论知识信息,让咱们在下一篇的编码实战中做到胸有成竹,下笔如有神助
  • 下一篇:《Java版人脸跟踪三部曲之三:编码实战》,敬请期待~

欢迎关注博客园:程序员欣宸

学习路上,你不孤单,欣宸原创一路相伴...

Java版人脸跟踪三部曲之二:开发设计的更多相关文章

  1. 三分钟极速体验:Java版人脸检测

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  2. Java版人脸检测详解上篇:运行环境的Docker镜像(CentOS+JDK+OpenCV)

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  3. Java版人脸检测详解下篇:编码

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  4. Java版 人脸识别SDK demo

    虹软人脸识别SDK之Java版,支持SDK 1.1+,以及当前最新版本2.0,滴滴,抓紧上车! 前言 由于业务需求,最近跟人脸识别杠上了,本以为虹软提供的SDK是那种面向开发语言的,结果是一堆dll· ...

  5. Java版 人脸识别SDK dem

    虹软人脸识别SDK之Java版,支持SDK 1.1+,以及2.0版本,滴滴,抓紧上车! 前言由于业务需求,最近跟人脸识别杠上了,本以为虹软提供的SDK是那种面向开发语言的,结果是一堆dll······ ...

  6. 数据结构(java版)学习笔记(二)——线性表之顺序表

    顺序表的优点: 随机存取元素方便,根据定位公式容易确定表中每个元素的存储位置,所以要指定第i个结点很方便 简单,直观 顺序表的缺点: 插入和删除结点困难 扩展不灵活,难以确定分配的空间 容易造成浪费 ...

  7. 数据结构Java版之排序算法(二)

    排序按时间复杂度和空间复杂度可分为 低级排序 和 高级排序 算法两种.下面将对排序算法进行讲解,以及样例的展示. 低级排序:冒泡排序.选择排序.插入排序. 冒泡排序: 核心思想,小的数往前移.假设最小 ...

  8. 微信公众平台Java版极速SDK

    JEEWX-API 是第一个微信公众平台Java版极速SDK,基于 jeewx-api 开发可以立即拥有简单易用的API,让开发更加轻松自如,节省更多时间 http://www.jeewx.com/

  9. 阿里云短信验证解决方案(java版)(redis存储)

    最近搞了一个互联网项目的注册,需要写一个手机号验证(由于之前没有轮子,只能自己摸索了); 1:基本思路: 1>购买了阿里云短信服务->下载阿里云短信发送demo(java版); 2> ...

  10. 第六篇 :微信公众平台开发实战Java版之如何自定义微信公众号菜单

    我们来了解一下 自定义菜单创建接口: http请求方式:POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/menu/create?access_to ...

随机推荐

  1. day72:drf:反序列化功能&模型类序列化器Modelserializer&drf视图APIView

    目录 1.续:反序列化功能(5-8) 1.用户post类型提交数据,反序列化功能的步骤 2.反序列化功能的局部钩子和全局钩子 局部钩子和全局钩子在序列化器中的使用 反序列化相关校验的执行顺序 3.反序 ...

  2. 【Mybatis】(一)

    Mybatis简介 提供持久层框架包括SQL Maps和Data Access Objects(DAO). SQL Maps提供数据库数据和java数据的映射关系,换句话说即是封装JDBC的过程. D ...

  3. 深入理解 python 虚拟机:字节码教程(3)——深入剖析循环实现原理

    深入理解 python 虚拟机:字节码教程(3)--深入剖析循环实现原理 在本篇文章当中主要给大家介绍 cpython 当中跟循环相关的字节码,这部分字节码相比起其他字节码来说相对复杂一点,通过分析这 ...

  4. C++核心知识回顾(函数&参数、异常、动态分配)

    复习C++的核心知识 函数与参数 传值参数.模板函数.引用参数.常量引用参数 传值参数 int abc(int a,int b,int c) { return a + b * c; } a.b.c是函 ...

  5. JavaScript 发布-订阅设计模式实现 React EventBus(相当于vue的$Bus)非父子之间通信

    提前声明: 我没有对传入的参数进行及时判断而规避错误,仅仅对核心方法进行了实现: 解决了react的非父子间的通信: 参考文档:https://github1s.com/browserify/even ...

  6. Python 组织列表

    组织列表 在创建的列表中,元素的排列顺序是无法预测的,不能总控制用户提供数据的顺序,通过组织列表的方式,来控制列表的排序 使用方法sort()对列表进行永久性排序 sort()方法:列表中值时小写时默 ...

  7. [Pytorch框架] 4.3 fastai

    文章目录 4.3 fastai 4.3.1 fastai介绍 fastai库 fast.ai课程 Github 4.3.2 fastai实践 MNIST 4.3.3 fastai文档翻译 import ...

  8. 沁恒 CH32V208(四): CH32V208 网络DHCP示例代码分析

    目录 沁恒 CH32V208(一): CH32V208WBU6 评估板上手报告和Win10环境配置 沁恒 CH32V208(二): CH32V208的储存结构, 启动模式和时钟 沁恒 CH32V208 ...

  9. 人手一个 Midjourney,StableStudio 重磅开源!

    人手一个 Midjourney,StableStudio 重磅开源! Stability AI 公司在上个月 19 号推出了 Alpha 版本 StableLM 大语言模型,包含了 30 亿和 70 ...

  10. Java 世界的法外狂徒:反射

    概述 反射(Reflection)机制是指在运行时动态地获取类的信息以及操作类的成员(字段.方法.构造函数等)的能力.通过反射,我们可以在编译时期未知具体类型的情况下,通过运行时的动态查找和调用. 虽 ...