一种鼠标手势识别的方案

其实鼠标手势识别在实际中好像并没有太多应用场景,比较常见的就是浏览器的手势操作,不过感觉这个也比较鸡肋,如果和硬件结合起来会好一些。我是由于一个小项目的需要,所以动手做鼠标手势识别这个东西,本来想在网上找一下看有没有相关文章,奈何很多根本都看不懂。。所以我就自己构思要如何实现,后来我就想到了一种比较简单的方法,经过测试,识别率是挺高的,应该可以>90%。

先上个图:

从实际效果来看,识别率挺不错的。接下来说说我实现的思路。

一、要知道鼠标手势,那就要知道移动的方向,那么我们可以用一个数组来保存鼠标每次移动的方向,如前次坐标(3,5),此次坐标(5,4),则将结果(大于为1,小于为-1,等于与0)记为(1,-1),这时我们便知道鼠标是向右下方滑动的。

二、接着将这些方向与标准的手势进行比较,若方向与上次相同则忽略,比较得出匹配的轨迹种类之后,我们还不能确定就是此轨迹,因为我们只确定了方向,所以此时我们还要将绘制出的轨迹图像与标准轨迹图片进行对比,若相似度大于阈值,则匹配成功。

三、因为用户在移动鼠标的过程中,肯定不可能完全是标准轨迹的样子,所以要设置一个误差范围,在此范围内可认为前后坐标相同。这个误差范围是根据用户绘制的轨迹图像的大小来动态确定的,图像越大,误差范围越大,最高不超过20。然后用户在绘制的过程中,可能出现手抖,可能会导致出现一个与标准轨迹不同的方向,这时我们就要设置一个容错次数,在这个次数内的错误的方向均不计,若超过了,则跳到识别下一个轨迹。

明白了以上三点,那么来写代码也就不难了。那么要怎么做呢?请继续看:

一、保存标准轨迹的配置文件

{ "track":[
{"trackImage":"d:/0/0.png", 
"description":"下一张", 
"function":"NEXT",
"scene":0,
"direction":[[0,1],[1,0]]} ] }

  • trackImage : 鼠标轨迹图片
  • description : 鼠标轨迹描述,将在程序下方提示
  • function : 要执行的功能编号
  • scene : 使用场景(暂时不用,比如说在不同的窗口时执行同样的功能需要按下不同的快捷键)
  • direction : 鼠标轨迹的移动方向,方向越多的话判断起来越准确

二、图像相似度比较

我在网上找了几个算法,直方图比较,均值哈希,汉明距离,发现均值哈希比较好,它的比较过程大体如下:

第一步,缩小尺寸。

将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。

第二步,简化色彩。

将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。

第三步,计算平均值。

计算所有64个像素的灰度平均值。

第四步,比较像素的灰度。

将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。

第五步,计算哈希值。

将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。
得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。

三、鼠标坐标记录

每隔几次移动后将鼠标坐标保存到一个动态数组中,并且刚开始移动时的坐标不要计入,因为此时的误差范围很小,所以可能会识别出某个轨迹

四、开始识别

int offset=(int)(cut.getWidth()/10.0+2);//鼠标移动时的误差范围
		offset=offset>20?20:offset;
		System.out.println("offset: "+offset+"  "+cut.getWidth());
		double max=0;
		int max_index=-1;//图片相似度最大的轨迹索引
		int interval=MouseTrack.INTERVAL;
		for(int i=0;i<tracks.length();i++) {
			JSONObject obj=tracks.getJSONObject(i);
			JSONArray ts=obj.getJSONArray("direction");
			int ts_i=0;
			String[] last=null;
			String last_dir=null,//前一次的坐标
			last_res="";//前两个坐标比较后的结果
			int mistake_times=0;//容错率
			String last_mistake="";
			for(String dir:dis) {//遍历鼠标运动方向
			String[] ds=dir.split(",");//此次的坐标
			if(last_dir!=null) {
				last=last_dir.split(",");//上次的坐标
				int lx=Integer.parseInt(last[0]),ly=Integer.parseInt(last[1]),
					nx=Integer.parseInt(ds[0]),ny=Integer.parseInt(ds[1]);
				int ox=nx-lx>offset?1:nx-lx<=-offset?-1:0
						,oy=ny-ly>offset?1:ny-ly<=-offset?-1:0;
					String dir_res=ox+","+oy;//得到方向
						
				if(ts_i>=ts.length()) {//超出标准轨迹的方向数目了,允许小范围内的超出
							if(interval--==0) {//每移动10次记录一次,不然太密集的话总是在误差范围内
							last_res=dir_res;
							interval=MouseTrack.INTERVAL;
							}
							if(!last_res.equals(dir_res)) {//瞎画的时候退出
							ts_i=0;
							break;
							}
							continue;
						}
						
			if((!dir_res.equals(last_res))) {//新的运动方向
						
						if((dir_res).equals(ts.getString(ts_i))) {//运动方向与轨迹吻合
						ts_i++;
						last_res=dir_res;
						}else if(!last_mistake.equals(dir_res)&&mistake_times++>MAX_MISTAKE_TIMES) {//同个错误不重复计入
							last_mistake=dir_res;
							break;
						}
					}
					last_dir=dir;
				}else {
					last_dir=dir;
				}
			}
				System.out.println(i+" ) Mistake: "+mistake_times);
		if(mistake_times>MAX_MISTAKE_TIMES)continue;
			if(ts_i==ts.length()) {//手势运动方向吻合,判断图片相似度
				double tmp=new FingerPrint(cut).compare(trackImages[i]);
					if(tmp>max) {
						max=tmp;
						max_index=i;
					}
					
				}
				
		}
		if(max_index>=0&&max>=0.8) {//有匹配的轨迹
			tip.setVisible(true);
			JSONObject obj=tracks.getJSONObject(max_index);
			tip.setIcon(new ImageIcon(drawTip(obj.getString("description"))));
			func=obj.getString("function");
			if(!sameFlag) {
				
				sameFlag=true;
			}
		}else {
			func="NONE";
			tip.setIcon(new ImageIcon(invalid));
		}
		

大致的过程就是如此,github项目地址:https://github.com/starkZH/MouseTrack-Recognition

 

 

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码一个多月没看,昨晚终于把错误找着了。这个演示程序能识别上下左右、西南、东南、正方形等,识别率很高。在线演示地址是ai-demo.appspot.com。 这个算法原理很好理解,好比老师让学生背诵课文,老师要达到的目的是“学生背过课文”,相对应的,软件要达到的目的是“我们把鼠标向左移动”显示“左”。老师第一次对学生说,你把课文背一遍,学生磕磕绊绊,老师说不通过接着告诉同学背课文的技巧等等,然后学生回到作为继续背课文。下节课,老师又对这个学生说,你把课文背一遍,学生背还是不很流畅,老师又说不通过,然后学生再回去背……,直到最后一次背课文,老师说OK,这就算达到目的了。 同样,软件识别鼠标手势也是一样,“鼠标向左移动手势”输入到算法中,程序检查算法计算结果,如算法输出果结果距离“左”差很远,那么不通过,程序就告诉算法,你要继续改进。如此循环,直到算法输出结果跟输入手势非常接近了,程序说通过了,那么就达到目的了,也就意味着这个算法就可以识别出“左”了!同样,也可以把“右”识别出来。 这个算法在神经网络中叫“有监督的训练方法”也叫“反向传播”。目前我对这个算法就这么些理解了。里边还有很多东西有待研究。但是,能把书中的例子改成 JavaScript版本,我就非常高兴了!有兴趣的也可以看看这本《游戏编程中的人工智能技术》,我自古讨厌数学,而且讨厌公式,而人工只能就需要数学,但这本书讲的确实非常好! 这个程序里边并没有训练方法,我只是把书中训练好的数据提取出来,直接输入到JavaScript写的神经网络里,这样一打开程序就能直接识别,否则还得训练,就现在javascript的执行效率,这不得到猴年马月去了! 程序用了一个js游戏库,名字叫jsgamesoup,里边的画图是HTML5语法的,在IE6下支持不太好,在IE8下运行也很顺畅。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值