简介

苹果官方为我们提供了简单手势的识别器,但对于图形手势,例如五角星、三角形等的识别,就需要自己实现了。通过识别这些手势,可以去执行特定的操作,或是输入公式、释放魔法等,可以为App增光添彩。

下载与使用

该框架已经上传到github,点击这里前去下载,欢迎Star!

有关该框架的使用在github上已经写明,这里不再赘述,本文主要介绍的是图形手势识别的实现原理与框架的结构。

框架的结构

一个图形手势是一条曲线,可以用采样点来描述,存储采样点的类为SGGesturePoint,使用它替代CGPoint,更符合面向对象的操作规范。

采样点的集合为SGGestureSet,它用于记录所有采样点以及手势的名称,以及标准化以后的手势向量。

手势向量使用SGGestureVector存储,向量通过将所有采样点的(x,y)坐标依次插入并标准化得来,手势向量用于进行余弦相似度计算。

用于标准化集合、生成向量、保存、加载与识别手势的类为SGGestureManager,它是一个单例对象,正常使用中只需要关心manager和set两个对象,其他对象由manager负责管理。

手势采样的过程

1.采样

Demo中给出的是使用UIPanGestureRecognizer完成的采样,每个采样点都是一个CGPoint,将其包装秤NSValue并且存储在数组中。

2.生成集合

使用SGGestureSet的gestureSetWithName:points:方法,传入手势的名称与采样点(NSValue数组),即可使用采样点初始化一个集合。

3.重新采样

将集合中的所有采样点构成的图形看作折线,根据所需要的采样密度确定采样间距interval,并在原曲线上生成均匀分布的采样点,生成采样点的代码如下,代码后将结合图例进行讲解。

// to resample the curve, calculate the length of the curve
SGGestureSet *tempSet = *set;
double sumLength = 0;
for (int i = 1; i < tempSet.countPoints; i++) {
SGGesturePoint *pt1 = [tempSet pointAtIndex:i];
SGGesturePoint *pt2 = [tempSet pointAtIndex:i - 1];
sumLength += [pt1 distanceTo:pt2];
}
// resample with sample uniform distributed points
SGGestureSet *resampleSet = [SGGestureSet gestureSetWithName:tempSet.name];
double Interval = sumLength / self.samplePointCount;
double D = 0;
SGGesturePoint *p1 = [tempSet pointAtIndex:0];
[resampleSet addGesturePoint:p1];
for (int i = 1; i < tempSet.countPoints;) {
SGGesturePoint *p2 = [tempSet pointAtIndex:i];
double d = [p1 distanceTo:p2];
if ((D + d) >= Interval) {
double k = (Interval - D ) / d;
double x = p1.x + k * (p2.x - p1.x);
double y = p1.y + k * (p2.y - p1.y);
SGGesturePoint *p = [SGGesturePoint gesturePointWithCGPoint:CGPointMake(x, y)];
[resampleSet addGesturePoint:p];
D = 0;
p1 = p;
}else{
D += d;
p1 = p2;
i++;
}
}

其中D用于折现拐点后确定下一采样点的距离,d为原集合中的相邻采样点间距,下图是某图形手势的局部折线图。

第一次进入循环时,P1为原集合的第一个采样点(也是重新采样集合的第一个点),P2为原集合的第二个采样点,他们之间的距离P大于重新采样的采样点间距interval,这时D=0,D+d=d>interval,因此进入if分支。



接下来根据interval与d的比例关系求出x、y的步进值,从而得到下一个重新采样点的坐标点,并且这个点作为新的P1。



以此类推,由于折线段的长度比interval大的多,因此能够分布许多新采样点,直到P1足够接近P2,使得interval>d,如下图所示。



这时下一个采样点应该落在下一个折线段上,并且为了保证均匀分布,下一个采样点距离折线段拐点的距离应该减去当前P1到P2的距离,这就是D的作用了。下图说明了这个计算的目的。



这时候会进入else分支,将P1更新为P2坐标,并且将d累加到D中,需要注意的是,下一个P2的坐标并不是原集合中的点,而是根据D计算出的点,因此应该跳过原集合中的下一个点,这就是i++的作用。如果曲线有足够多的短折线段,则会不断的进入else分支,一直累加d,直到满足新采样点间距,也就是D+d>=interval,才生成一个新的采样点,本图中的情况仅仅计算了一次D就进入了下一条比较长的折线段,这时候清空D,开始在折线段上分布采样点,如下图所示。



经过多次这样的运算,就可以完成均匀分布的重新采样了,之后使用的是重新采样点的集合。

4.曲线位置的标准化

将集合中的x、y分别求平均值,得到曲线的重心,根据重心坐标将曲线移动到坐标原点,得到标准位置的曲线。

5.曲线尺寸的标准化

根据曲线的外接矩形与标准尺寸将曲线上的每个点进行比例运算,即可得到缩放到标准尺寸的曲线。

6.曲线转角的标准化

根据曲线上的第一个采样点与中心的连线的角度对曲线进行标准化,设当前角度为iAngle,目标角度为r,则如下图所示经过坐标变换将曲线旋转到红色位置。

7.生成向量

为了进行后续运算,需要将二元采样点集化为一元集合,也可以看做多维向量,方法是依次将采样点的x、y坐标插入一元集合,并对向量进行标准化,每个向量代表一个手势,可用于后续的比较运算。

手势识别的过程

对于需要识别的手势,先经过上面的运算得到手势向量,然后将这个手势向量与手势库中的向量逐一进行余弦相似度的运算,余弦相似度比较的是向量的夹角,夹角越小则越相似,根据一定的阈值来筛选出符合条件的所有手势,并在遍历结束后取最优(运算结果最小)的作为匹配结果。

余弦相似度的计算代码如下:

- (double)cosDistanceWithVector1:(SGGestureVector *)vec1 vector2:(SGGestureVector *)vec2 {
double a = 0;
double b = 0;
for (int i = 0; i <= vec1.length - 1 && i <= vec2.length - 1; i+=2) {
a += [vec1 doubleAtIndex:i] * [vec2 doubleAtIndex:i] + [vec1 doubleAtIndex:i + 1] * [vec2 doubleAtIndex:i + 1];
b += [vec1 doubleAtIndex:i] * [vec2 doubleAtIndex:i + 1] - [vec1 doubleAtIndex:i + 1] * [vec2 doubleAtIndex:i];
}
double angle = atan(b / a);
return acos(a * cos(angle) + b * sin(angle));
}

识别一个手势的代码如下,先标准化手势集合并得到向量,然后在手势库中筛选,最后选择最优结果。

- (NSString *)recognizeGestureSet:(SGGestureSet *)set {
[self standardizeSet:&set];
SGGestureVector *vec1 = [set getVector];
SGGestureSet *bestSet = nil;
double minD = CGFLOAT_MAX;
for (int i = 0; i < self.gestureSets.count; i++) {
SGGestureSet *libSet = self.gestureSets[i];
SGGestureVector *vec2 = [libSet getVector];
double D = [self cosDistanceWithVector1:vec1 vector2:vec2];
if(D <= self.threshold && D < minD){
minD = D;
bestSet = libSet;
}
}
return bestSet.name;
}

手势的存取

以上介绍的每一个与存储有关的类都遵循NSCoding协议,将每一个标准化的SGGestureSet存入到数组中,并将数组利用NSKeyedArchiver归档存储到磁盘,需要读取时再通过NSKeyedUnarchiver反归档即可。

iOS图形手势识别框架SGGestureRecognizer的更多相关文章

  1. iOS 的基本框架

    在iOS中框架是一个目录,包含了共享资源库,用于访问该资源库中储存的代码的头文件,以及图像.声音文件等其他资源.共享资源库定义应用程序可以调用的函数和方法.    iOS为应用程序开发提供了许多可使用 ...

  2. ios开发——实用技术篇OC篇&iOS的主要框架

    iOS的主要框架         阅读目录 Foundation框架为所有的应用程序提供基本系统服务 UIKit框架提供创建基于触摸用户界面的类 Core Data框架管着理应用程序数据模型 Core ...

  3. 【iOS】系统框架学习

    iOS的系统架构分为四个层次:核心操作系统层(Core OS layer).核心服务层(Core Services layer).媒体层(Media layer)和可触摸层(Cocoa Touch l ...

  4. Sprint 2 : ios图形界面设计与代码整合

    这周我们主要focus在personal photo experience 项目的ios图形界面设计与代码整合工作上. 工作进度: 1. 图形界面设计方面:兆阳和敏龙基本已经将ios手机客户端的雏形界 ...

  5. [转载]iOS 10 UserNotifications 框架解析

    活久见的重构 - iOS 10 UserNotifications 框架解析 TL;DR iOS 10 中以前杂乱的和通知相关的 API 都被统一了,现在开发者可以使用独立的 UserNotifica ...

  6. Qt图形视图框架公开课资料

    接受CSDN学院的邀请,讲一次公开课,主题是Qt图形视图框架,报名链接在这里:http://edu.csdn.net/huiyiCourse/detail/228. 内容有两部分:自定义Item和拖放 ...

  7. 关于第三方IOS的checkBox框架的使用

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  8. Qt之图形视图框架

    简述 图形视图(Graphics View)提供了一个平台,用于大量自定义2D图元的管理与交互,并提供了一个视图部件(view widget)来显示可以缩放和旋转的图元. 框架包括一个事件传播架构,支 ...

  9. 漫谈iOS Crash收集框架

    漫谈iOS Crash收集框架   Crash日志收集 为了能够第一时间发现程序问题,应用程序需要实现自己的崩溃日志收集服务,成熟的开源项目很多,如 KSCrash,plcrashreporter,C ...

随机推荐

  1. MongoDB 更新文档

    MongoDB 使用 update() 和 save() 方法来更新集合中的文档.接下来让我们详细来看下两个函数的应用及其区别. update() 方法 update() 方法用于更新已存在的文档.语 ...

  2. arc的安装

    安装:   # sudo apt-get install php5 php5-curl   # ubuntu 系统 # sudo yum install php5 # centos 系统   # cd ...

  3. Java网络编程基础(Netty预备知识)

    今天在家休息,闲来无事,写篇博客,陶冶下情操~~~ =================我是分割线================ 最近在重新学习Java网络编程基础,以便后续进行Netty的学习. 整 ...

  4. [LeetCode] Bold Words in String 字符串中的加粗单词

    Given a set of keywords words and a string S, make all appearances of all keywords in S bold. Any le ...

  5. Java 中的时间日期 API

    自从 14 年发布 Java 8 以后,我们古老 java.util.Date 终于不再是我们 Java 里操作日期时间的唯一的选择. 其实 Java 里的日期时间的相关 API 一直为世猿诟病,不仅 ...

  6. [HAOI 2011]Problem c

    Description 给n个人安排座位,先给每个人一个1~n的编号,设第i个人的编号为ai(不同人的编号可以相同),接着从第一个人开始,大家依次入座,第i个人来了以后尝试坐到ai,如果ai被占据了, ...

  7. ●POJ 3378 Crazy Thairs

    题链: http://poj.org/problem?id=3378 题解: 树状数组维护,高精度. 依次考虑以每个位置结尾可以造成的贡献. 假设当前位置为i,为了达到5个元素的要求,我们需要求出,在 ...

  8. ●BZOJ 2442 [Usaco2011 Open]修剪草坪

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=2442 题解: 单调队列优化DP 把问题转化为:从序列里选出一些相邻之间间隔不超过K的数,使得 ...

  9. 习题9-3 UVA1629(dp)

    Cake Slicing 题意:有一个n行m列的网格上有一些黑点,要求进行切割,使最后每块上只有一个黑点,求最少的刀数 思路:记忆化搜索,枚举每一条边来切,每一次搜索自己所能切割的所有情况取最小值 但 ...

  10. [Codeforces]605E Intergalaxy Trips

    小C比较棘手的概率期望题,感觉以后这样的题还会贴几道出来. Description 给定一个n*n的邻接矩阵,邻接矩阵中元素pi,j表示的是从 i 到 j 这条单向道路在这一秒出现的概率百分比,走一条 ...