代码地址如下:
http://www.demodashi.com/demo/12011.html

之前公司项目需要,研究了一下人脸识别和活体识别,并运用免费的讯飞人脸识别,在其基础上做了二次开发,添加了活体识别。项目需要就开发了张嘴和摇头两个活体动作的识别。 这里简单介绍一下当时的开发思路和一些个人拙见,欢迎大神指点。

首先说一下讯飞第三方的人脸识别的几个缺点:1.识别不稳定,各点坐标跳动偏差比较大,不容易捕捉;2.CPU使用率比较高,连续识别一会儿手机会明显发烫,手机配置低的,就会反应很慢,本人使用的iPhone 6s,配置还可以,还算比较流畅,但也会发烫。3.屏幕小的手机识别率相对会低一点,当然这也和手机的配置脱不了干系。

下面开始我们的活体识别开发之路:

确定位置

讯飞的人脸识别坐标跳动比较大,如果全屏识别发现很容易出现错误的识别,导致识别错误的被通过,所以为了降低这个可能性,特意加了脸部位置的限制,把识别位置和范围大大缩小,大大提高了识别精度和成功率。

原版的Demo里给出了人脸框的坐标,也显示出了人脸的框,代码如下:

-(void)drawPointWithPoints:(NSArray *)arrPersons
{
if (context) {
CGContextClearRect(context, self.bounds) ;
}
context = UIGraphicsGetCurrentContext();
for (NSDictionary *dicPerson in arrPersons) {
if ([dicPerson objectForKey:POINTS_KEY]) {
for (NSString *strPoints in [dicPerson objectForKey:POINTS_KEY]) {
CGPoint p = CGPointFromString(strPoints);
CGContextAddEllipseInRect(context, CGRectMake(p.x - 1 , p.y - 1 , 2 , 2));
}
}
BOOL isOriRect=NO;
if ([dicPerson objectForKey:RECT_ORI]) {
isOriRect=[[dicPerson objectForKey:RECT_ORI] boolValue];
}
if ([dicPerson objectForKey:RECT_KEY]) {
CGRect rect=CGRectFromString([dicPerson objectForKey:RECT_KEY]);
if(isOriRect){//完整矩形
CGContextAddRect(context,rect) ;
}
else{ //只画四角
// 左上
CGContextMoveToPoint(context, rect.origin.x, rect.origin.y+rect.size.height/8);
CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y);
CGContextAddLineToPoint(context, rect.origin.x+rect.size.width/8, rect.origin.y);
//右上
CGContextMoveToPoint(context, rect.origin.x+rect.size.width*7/8, rect.origin.y);
CGContextAddLineToPoint(context, rect.origin.x+rect.size.width, rect.origin.y);
CGContextAddLineToPoint(context, rect.origin.x+rect.size.width, rect.origin.y+rect.size.height/8);
//左下
CGContextMoveToPoint(context, rect.origin.x, rect.origin.y+rect.size.height*7/8);
CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y+rect.size.height);
CGContextAddLineToPoint(context, rect.origin.x+rect.size.width/8, rect.origin.y+rect.size.height);
//右下
CGContextMoveToPoint(context, rect.origin.x+rect.size.width*7/8, rect.origin.y+rect.size.height);
CGContextAddLineToPoint(context, rect.origin.x+rect.size.width, rect.origin.y+rect.size.height);
CGContextAddLineToPoint(context, rect.origin.x+rect.size.width, rect.origin.y+rect.size.height*7/8);
}
}
}
[[UIColor greenColor] set];
CGContextSetLineWidth(context, 2);
CGContextStrokePath(context);
}

在这段代码的启发下,我对此作了改装,把动态的人脸框,改成了静态的框,这个静态框,就是指示和限定人脸位置的框,根据屏幕大小画出的,代码如下:

-(void)drawFixedPointWithPoints:(NSArray *)arrFixed
{
for (NSDictionary *dicPerson in arrFixed) {
if ([dicPerson objectForKey:POINTS_KEY]) {
for (NSString *strPoints in [dicPerson objectForKey:POINTS_KEY]) {
CGPoint p = CGPointFromString(strPoints);
CGContextAddEllipseInRect(context, CGRectMake(p.x - 1 , p.y - 1 , 2 , 2));
}
}
if ([dicPerson objectForKey:RECT_KEY]) {
CGRect rect=CGRectFromString([dicPerson objectForKey:RECT_KEY]);
// 左上
CGContextMoveToPoint(context, rect.origin.x, rect.origin.y+rect.size.height/8);
CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y);
CGContextAddLineToPoint(context, rect.origin.x+rect.size.width/8, rect.origin.y);
//右上
CGContextMoveToPoint(context, rect.origin.x+rect.size.width*7/8, rect.origin.y);
CGContextAddLineToPoint(context, rect.origin.x+rect.size.width, rect.origin.y);
CGContextAddLineToPoint(context, rect.origin.x+rect.size.width, rect.origin.y+rect.size.height/8);
//左下
CGContextMoveToPoint(context, rect.origin.x, rect.origin.y+rect.size.height*7/8);
CGContextAddLineToPoint(context, rect.origin.x, rect.origin.y+rect.size.height);
CGContextAddLineToPoint(context, rect.origin.x+rect.size.width/8, rect.origin.y+rect.size.height);
//右下
CGContextMoveToPoint(context, rect.origin.x+rect.size.width*7/8, rect.origin.y+rect.size.height);
CGContextAddLineToPoint(context, rect.origin.x+rect.size.width, rect.origin.y+rect.size.height);
CGContextAddLineToPoint(context, rect.origin.x+rect.size.width, rect.origin.y+rect.size.height*7/8);
}
}
[[UIColor blueColor] set];
CGContextSetLineWidth(context, 2);
CGContextStrokePath(context);
}

这里的框是限定脸部位置的,所以脸部位置超出设置的范围的时候,就需要停止人脸识别,停止动作识别,并给出用户提示,提示用户调整位置,或者明确告诉用户,脸部距离屏幕太近了,或者太远了。判定脸部位置的代码如下:

#pragma mark --- 判断位置
-(BOOL)identifyYourFaceLeft:(CGFloat)left right:(CGFloat)right top:(CGFloat)top bottom:(CGFloat)bottom
{
//判断位置
if (right - left < 230 || bottom - top < 250) {
self.textLabel.text = @"太远了...";
[self delateNumber];//清数据
isCrossBorder = YES;
return YES;
}else if (right - left > 320 || bottom - top > 320) {
self.textLabel.text = @"太近了...";
[self delateNumber];//清数据
isCrossBorder = YES;
return YES;
}else{
if (isJudgeMouth != YES) {
self.textLabel.text = @"请重复张嘴动作...";
[self tomAnimationWithName:@"openMouth" count:2];
#pragma mark --- 限定脸部位置为中间位置
if (left < 100 || top < 100 || right > 460 || bottom > 400) {
isCrossBorder = YES;
isJudgeMouth = NO;
self.textLabel.text = @"调整下位置先...";
[self delateNumber];//清数据
return YES;
}
}else if (isJudgeMouth == YES && isShakeHead != YES) {
self.textLabel.text = @"请重复摇头动作...";
[self tomAnimationWithName:@"shakeHead" count:4];
number = 0;
}else{
takePhotoNumber += 1;
if (takePhotoNumber == 2) {
[self timeBegin];
}
}
isCrossBorder = NO;
}
return NO;
}

这个方法基于Demo中第三方封装库中给的代理方法-(NSString)praseDetect:(NSDictionary )positionDic OrignImage:(IFlyFaceImage*)faceImg; 判断脸部并返回人脸的脸框的坐标,所以利用给的脸部框坐标做判断,超出设置的范围时停止识别。

其中,脸部框两边的坐标左边大于一定值且右边小于一定值的时候,判定为脸部位置“太远了”;同理,脸部框两边的坐标左边小于设定边框点且右边大于设定边框右边点的时候,判定为脸部位置“太近了”;如果位置正确,则脸部位置到达正确位置,这个时候显示脸部各点,并开始活体动作识别:张嘴和摇头。我这里先做张嘴,再做摇头。

张嘴识别

张嘴识别,这里的嘴部定点有五个:上、下、左、右、中。这里我取的是上下左右四个点,并判断上下点的距离变化和左右点的距离变化,一开始只判断了上下点距离变化超过设定值得时候就判断为张嘴,后来测试过程中,上下晃动屏幕,会判断失败,直接通过。所以为了解决这个bug,并判断更严谨,加上了左右点的判断,即上下点变化大于设定值并且左右点变化小于设定值的时候判定为张嘴动作识别通过。代码如下:

#pragma mark --- 判断是否张嘴
-(void)identifyYourFaceOpenMouth:(NSString *)key p:(CGPoint )p
{
if ([key isEqualToString:@"mouth_upper_lip_top"]) {
upperY = p.y;
}
if ([key isEqualToString:@"mouth_lower_lip_bottom"]) {
lowerY = p.y;
}
if ([key isEqualToString:@"mouth_left_corner"]) {
leftX = p.x;
}
if ([key isEqualToString:@"mouth_right_corner"]) {
rightX = p.x;
}
if (rightX && leftX && upperY && lowerY && isJudgeMouth != YES) {
number ++;
if (number == 1 || number == 300 || number == 600 || number ==900) {
mouthWidthF = rightX - leftX < 0 ? abs(rightX - leftX) : rightX - leftX;
mouthHeightF = lowerY - upperY < 0 ? abs(lowerY - upperY) : lowerY - upperY;
NSLog(@"%d,%d",mouthWidthF,mouthHeightF);
}else if (number > 1200) {
[self delateNumber];//时间过长时重新清除数据
[self tomAnimationWithName:@"openMouth" count:2];
}
mouthWidth = rightX - leftX < 0 ? abs(rightX - leftX) : rightX - leftX;
mouthHeight = lowerY - upperY < 0 ? abs(lowerY - upperY) : lowerY - upperY;
NSLog(@"%d,%d",mouthWidth,mouthHeight);
NSLog(@"张嘴前:width=%d,height=%d",mouthWidthF - mouthWidth,mouthHeight - mouthHeightF);
if (mouthWidth && mouthWidthF) {
//张嘴验证完毕
if (mouthHeight - mouthHeightF >= 20 && mouthWidthF - mouthWidth >= 15) {
isJudgeMouth = YES;
imgView.animationImages = nil;
}
}
}
}

张嘴动作识别通过后,开始判断摇头动作。

摇头识别

摇头识别,这里的摇头动作相比于张嘴动作,摇头动作我没有限制位置,张嘴识别必须在设置的框内完成动作,摇头动作不需要,因为摇头动作幅度大,需要的位置大,如果再限定位置的话,识别要求比较高,不容易识别通过,用户体验差。

摇头识别的思路比较简单,没有做细致的计算分析,仅仅是判断了鼻尖的点的坐标改变大于设定值,即判定为摇头动作通过。代码如下:

#pragma mark --- 判断是否摇头
-(void)identifyYourFaceShakeHead:(NSString *)key p:(CGPoint )p
{
if ([key isEqualToString:@"mouth_middle"] && isJudgeMouth == YES) {
if (bigNumber == 0 ) {
firstNumber = p.x;
bigNumber = p.x;
smallNumber = p.x;
}else if (p.x > bigNumber) {
bigNumber = p.x;
}else if (p.x < smallNumber) {
smallNumber = p.x;
}
//摇头验证完毕
if (bigNumber - smallNumber > 60) {
isShakeHead = YES;
[self delateNumber];//清数据
}
}
}

其实这样判断摇头是有bug的,左右晃动手机超过一定的距离,也会判定摇头通过,当时时间紧张,没做过多处理,所以就暂时这样判定了。

其他细节

判断比较数据,我用了计数法,取得是不同时间点的帧图片上的点的位置并记录下来,然后和初始值做比较,所以如果判断不符合要求,需要清除数据,并重新开始记录并判定。

另外Demo里给出了两种记录动作的方式,一种是有声音的拍照,一种是无声音的截图,可以为人脸的对比做铺垫。

文件目录截图

尾声

Demo的gitHub欢迎大家下载、参考、指正、交流,如果对您有帮助,感谢star,另外建立了一个活体识别交流群:498197808,欢迎同道中人加入,大家一起交流学习。

iOS活体人脸识别的Demo和一些思路

代码地址如下:
http://www.demodashi.com/demo/12011.html

注:本文著作权归作者,由demo大师代发,拒绝转载,转载需要作者授权

iOS活体人脸识别的Demo和一些思路的更多相关文章

  1. IOS CoreData 多表查询demo解析

    在IOS CoreData中,多表查询上相对来说,没有SQL直观,但CoreData的功能还是可以完成相关操作的. 下面使用CoreData进行关系数据库的表与表之间的关系演示.生成CoreData和 ...

  2. 常量,字段,构造方法 调试 ms 源代码 一个C#二维码图片识别的Demo 近期ASP.NET问题汇总及对应的解决办法 c# chart控件柱状图,改变柱子宽度 使用C#创建Windows服务 C#服务端判断客户端socket是否已断开的方法 线程 线程池 Task .NET 单元测试的利剑——模拟框架Moq

    常量,字段,构造方法   常量 1.什么是常量 ​ 常量是值从不变化的符号,在编译之前值就必须确定.编译后,常量值会保存到程序集元数据中.所以,常量必须是编译器识别的基元类型的常量,如:Boolean ...

  3. uniapp安卓ios百度人脸识别、活体检测、人脸采集APP原生插件

    插件亮点 1 支持安卓平板(横竖屏均可),苹果的iPad.2 颜色图片均可更换. 特别提醒 此插件包含 android 端和 iOS 端,考虑到有些同学只做其中一个端的 app,特意分为 2 个插件, ...

  4. Java版 人脸识别SDK demo

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

  5. iOS之一些实用的Demo

    图像浏览及处理 FLAnimatedImage - gif播放处理的工具. CLImageEditor - 超强的图片编辑库,快速帮你实现旋转,防缩,滤镜等等一系列麻烦的事情. ios-image-f ...

  6. [置顶] ios 时间定时器 NSTimer应用demo

    原创文章,转载请注明出处:http://blog.csdn.net/donny_zhang/article/details/9251917 demo功能:ios NSTimer应用demo .ipho ...

  7. 基于虹软sdk,java实现人脸识别(demo)

    ## 开发环境准备:###开发使用到的软件和工具:* Jdk8.mysql5.7.libarcsoft_face.dll(so).libarcsoft_face_engine.dll(so).liba ...

  8. java引用Arcface,实现人脸识别(demo)

    ## 开发环境准备: ###开发使用到的软件和工具: * Jdk8.mysql5.7.libarcsoft_face.dll(so).libarcsoft_face_engine.dll(so).li ...

  9. 人脸识别 ArcFace Demo [Windows]

    Arcsoft ArcfaceDemo for Windows, VS2013 C++   使用虹软技术开发完成 使用步骤: 1.下载SDK包,32位Windows平台将五个SDK包里lib中的文件到 ...

随机推荐

  1. deepin使用root身份运行

    https://blog.csdn.net/guanripeng/article/details/79630619

  2. POJ 2236 Wireless Network [并查集+几何坐标 ]

    An earthquake takes place in Southeast Asia. The ACM (Asia Cooperated Medical team) have set up a wi ...

  3. 免费Jdk选择:jdkadoptOpenJdk

    oracle公司的jdk即将要收费,不在给免费用户提供更新,点击查看. jira在7.13版本将jdk从oracle jdk变更为adoptOpenJdk.点击查看.

  4. 【BZOJ 2333 】[SCOI2011]棘手的操作(离线+线段树|可并堆-左偏树)

    2333: [SCOI2011]棘手的操作 Description 有N个节点,标号从1到N,这N个节点一开始相互不连通.第i个节点的初始权值为a[i],接下来有如下一些操作: U x y: 加一条边 ...

  5. 某考试 T2 orzcyr

    非常nice的一道行列式的题目. 考虑如果没有路径不相交这个限制的话,那么这个题就是一个行列式:设 a[i][j] 为从编号第i小的源点到编号第j小的汇点的路径条数,那么矩阵a[][]的行列式就是的答 ...

  6. 【R实践】时间序列分析之ARIMA模型预测___R篇

    时间序列分析之ARIMA模型预测__R篇 之前一直用SAS做ARIMA模型预测,今天尝试用了一下R,发现灵活度更高,结果输出也更直观.现在记录一下如何用R分析ARIMA模型. 1. 处理数据 1.1. ...

  7. 调用上一个页面的js方法

    点击商品分类,弹出下框 点击确定,将选中的类别的name和唯一的code返回到上个页面 function save(){ var ids = getIdSelections(); jp.get(&qu ...

  8. webstorm 2016

    2017.2.27更新 选择“license server” 输入:http://idea.imsxm.com/ 2016.2.2 版本的破解方式: 安装以后,打开软件会弹出一个对话框:选择“lice ...

  9. 解决Visual Studio 2013 XAML设计器异常

    今天使用Visual Studio 2013打开一个windows 应用商店程序的时候,发现出现如下异常:    at System.Windows.Input.Cursor.LoadFromFile ...

  10. 看懂ios命名规则

    http://liangrui.blog.51cto.com/1510945/509289/ http://daniellee520.blog.51cto.com/372529/229615