iOS图形手势识别框架SGGestureRecognizer
简介
苹果官方为我们提供了简单手势的识别器,但对于图形手势,例如五角星、三角形等的识别,就需要自己实现了。通过识别这些手势,可以去执行特定的操作,或是输入公式、释放魔法等,可以为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的更多相关文章
- iOS 的基本框架
在iOS中框架是一个目录,包含了共享资源库,用于访问该资源库中储存的代码的头文件,以及图像.声音文件等其他资源.共享资源库定义应用程序可以调用的函数和方法. iOS为应用程序开发提供了许多可使用 ...
- ios开发——实用技术篇OC篇&iOS的主要框架
iOS的主要框架 阅读目录 Foundation框架为所有的应用程序提供基本系统服务 UIKit框架提供创建基于触摸用户界面的类 Core Data框架管着理应用程序数据模型 Core ...
- 【iOS】系统框架学习
iOS的系统架构分为四个层次:核心操作系统层(Core OS layer).核心服务层(Core Services layer).媒体层(Media layer)和可触摸层(Cocoa Touch l ...
- Sprint 2 : ios图形界面设计与代码整合
这周我们主要focus在personal photo experience 项目的ios图形界面设计与代码整合工作上. 工作进度: 1. 图形界面设计方面:兆阳和敏龙基本已经将ios手机客户端的雏形界 ...
- [转载]iOS 10 UserNotifications 框架解析
活久见的重构 - iOS 10 UserNotifications 框架解析 TL;DR iOS 10 中以前杂乱的和通知相关的 API 都被统一了,现在开发者可以使用独立的 UserNotifica ...
- Qt图形视图框架公开课资料
接受CSDN学院的邀请,讲一次公开课,主题是Qt图形视图框架,报名链接在这里:http://edu.csdn.net/huiyiCourse/detail/228. 内容有两部分:自定义Item和拖放 ...
- 关于第三方IOS的checkBox框架的使用
*:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...
- Qt之图形视图框架
简述 图形视图(Graphics View)提供了一个平台,用于大量自定义2D图元的管理与交互,并提供了一个视图部件(view widget)来显示可以缩放和旋转的图元. 框架包括一个事件传播架构,支 ...
- 漫谈iOS Crash收集框架
漫谈iOS Crash收集框架 Crash日志收集 为了能够第一时间发现程序问题,应用程序需要实现自己的崩溃日志收集服务,成熟的开源项目很多,如 KSCrash,plcrashreporter,C ...
随机推荐
- tkinter的冷却技能
validatecommand=(f,s1,s2,s3) f就是冷却后的验证函数名,s1,s2,s3这些时额外的选项,这些选项会作为参数依次传给f函数. register()冷却作用:register ...
- 类似吸顶功能解决ios不能实时监听onscroll的触发问题
问题:近期项目需要一个类似西东功能,当页面向上滚动160px后div固定在顶部 解决方法:首先,想到的是window.onscroll方法 .fixed{position:fixed;-webkit- ...
- PAT 1082. 射击比赛 (20)
本题目给出的射击比赛的规则非常简单,谁打的弹洞距离靶心最近,谁就是冠军:谁差得最远,谁就是菜鸟.本题给出一系列弹洞的平面坐标(x,y),请你编写程序找出冠军和菜鸟.我们假设靶心在原点(0,0). 输入 ...
- 浅析java的深拷贝与浅拷贝
(转自:http://www.cnblogs.com/chenssy/p/3308489.html) 首先来看看浅拷贝和深拷贝的定义: 浅拷贝:使用一个已知实例对新创建实例的成员变量逐个赋值,这个方式 ...
- [LeetCode] Reverse String II 翻转字符串之二
Given a string and an integer k, you need to reverse the first k characters for every 2k characters ...
- TOMCAT下的JNDI的配置
一.第一种配置局部JNDI 1.在tomcat的conf目录下的server.xml的<host>标签内,添加: <Context path="/TestMvcMode&q ...
- codevs 2621 土地侵蚀
提交地址:http://codevs.cn/problem/2621/ 2621 土地侵蚀 时间限制: 1 s 空间限制: 32000 KB 题目等级 : 黄金 Gold 题目描述 De ...
- operator[] 和 insert
operator[] 和 insert: map的[]操作和其他容器和内置[]没有关系 如果我们通过[]向map中插入or更新值,需要考虑一些东西 1.键已经存在,那么直接进行修改即可 2.键不存在, ...
- 【bzoj4443 scoi2015】小凸玩矩阵
题目描述 小凸和小方是好朋友,小方给了小凸一个 nn × mm (n \leq m)(n≤m) 的矩阵 AA ,并且要求小凸从矩阵中选出 nn 个数,其中任意两个数都不能在同一行或者同一列.现在小凸想 ...
- 关于快速沃尔什变换(FWT)的一点学习和思考
最近在学FWT,抽点时间出来把这个算法总结一下. 快速沃尔什变换(Fast Walsh-Hadamard Transform),简称FWT.是快速完成集合卷积运算的一种算法. 主要功能是求:,其中为集 ...