自从Kinect出现,用它实现多点触控的方案就百家争鸣,其中一种是使任意屏幕变成可触控的。前几年在ubi-interactive就已经做了这种应用(现在他们除了弄了个Ubi Camera还弄了一个Ubi Pen)。这位同学不但成功做出来可触控效果,并且分享了自己的实现思路,我认为很有必要转载到这里分享。
先看一个视频:
他分享的心得里其中最重要的就是这段话:
从Kinect获取的整个画面中识别出指尖
因为用手指来调用鼠标进行操作,而接触屏幕的地方又是手指的指尖,所以需要识别出指尖。
一开始我是基于Kinect中BodyIndex这个数据源来寻找指尖,首先定位出腕关节在哪,然后根据腕关节的位置向上寻找复合指尖特征的点,可以说效果很好,识别非常精确和稳定,而且能同时识别出5个指尖,这部分是我在假期里完成的,本来以为来到学校后将程序根据投影仪调整下就差不多可以用了,然而到校测试后才发现一个致命的问题,就是当手臂贴近墙壁时,整个手臂的BodyIndex数据都丢失了。因为微软似乎认为,如果某个点要是属于人体的话,那么它和背景的深度差至少要有二三十厘米左右(正好是人体的厚度)。这个问题让我失眠了几晚上…不过也是在失眠的时候想出了现在用的解决方法。ds现在用的方法是基于
Depth
数据来找指尖的,简单来说就是根据指尖的特点找出所有吻合的点,然后取 位置最高的那个(因为操作的时候用的基本都是一根手指),这样做可以减少很多工作量,因为很多非法点都直接被略去了。
他也说了算法还是有很多问题,还需要优化。我想可以再考虑下肤色识别,在深度数据不给力的时候尝试下彩色数据。
欢迎订阅我的微信公众号 nui-develop
下面是转载他的原文http://www.cnblogs.com/czaoth/p/6027449.html:
项目介绍
好了,现在正式开始介绍一下项目本身。
背景
这个项目原本是用来参加2016年的微软创新杯,但是在上一周,也就是3月20号的四川省区域赛中未获奖,只能直接参战中国区半决赛。这次区域赛失败的原因有很多,虽然作品已经完成得差不多了,但是没能优秀地将其展示出来,在现成演示的时候还遇到了一个巨大失误,中途才发现,所以没等通知结果就知道多半是悲剧了。第一次参加这类开发类的比赛,就当交学费好了,不过这次区域赛给我的感觉是,微软还是想找几个最有商业前途的作品,这个项目炫是很炫酷,但是实际意义不大的样子,所以感觉中国区半决赛希望也不大。不过无所谓啦,我自己玩得嗨就行了,微软欣不欣赏那是另外一回事,说不定哪天我就搭建出个钢铁侠那样的实验室不是?啊哈哈,最近也顺便把这个项目报成了大学生创新创业训练计划,成功申请到国家级,算是可以安慰一下。
目标
简单来说,此项目就是要把投影仪投出的投影变得可以直接用裸手操控,就好像投影变成了一块大型的平板电脑,投影可以是在投影幕上、墙上甚至桌子上,任何光滑且不是反射材质的平面都行,至于为什么不能是反射材质,等下会有介绍。刚开始是计划达到能用手指直接在投影上写字的精度,后来发现很难做到,瓶颈在于指尖的识别算法不够精确,这是我自己构思的一个简单算法,未来应该会用更高级更精确的算法来替代。
开发环境
Kinect for Windows V2
+ Kinect SDK 2.0
+ OpenCV 3.0
+ Visual Studio Community 2015
Kinect
项目里利用到的一个非常重要的东西就是Kinect for Windows V2
,一款微软的动作感应器,可以算成是一类现实增强设备,发布时主要是搭配XBox
来玩体感游戏,但是这么厉害的一个东西只能用来玩游戏实在太可惜,所以微软在前几天发布了它的Windows
版本,让它能够在PC上进行开发。就是下面这么个东西:
原理
原理其实并不算难,主要可以参考下面这张图。
- 从Kinect获取的整个画面中识别出投影
- 因为操作要在投影上进行,所以需要先识别出投影是画面中的哪一块,这里用的算法比较简单,先投一副纯色图像出来,然后利用投影区RGB值近似的原理,找出投影的左下角和右上角之后就确定了投影的区域,这样做的缺点是投影区只能是矩形而且不能太歪。其实有时间的话,可以试一下利用9*9的矩阵来找出所有属于边缘的点,然后渲染所有边缘点,也就找出了整个边缘,这样可以适应任意形状。
- 从Kinect获取的整个画面中识别出指尖
- 因为用手指来调用鼠标进行操作,而接触屏幕的地方又是手指的指尖,所以需要识别出指尖。
一开始我是基于
Kinect
中BodyIndex
这个数据源来寻找指尖,首先定位出腕关节在哪,然后根据腕关节的位置向上寻找复合指尖特征的点,可以说效果很好,识别非常精确和稳定,而且能同时识别出5个指尖,这部分是我在假期里完成的,本来以为来到学校后将程序根据投影仪调整下就差不多可以用了,然而到校测试后才发现一个致命的问题,就是当手臂贴近墙壁时,整个手臂的BodyIndex
数据都丢失了。因为微软似乎认为,如果某个点要是属于人体的话,那么它和背景的深度差至少要有二三十厘米左右(正好是人体的厚度)。这个问题让我失眠了几晚上…不过也是在失眠的时候想出了现在用的解决方法。ds1现在用的方法是基于<span class="hljs-string">`Depth`数据来找指尖的,简单来说就是根据指尖的特点找出所有吻合的点,然后取 位置最高的那个(因为操作的时候用的基本都是一根手指),这样做可以减少很多工作量,因为很多非法点都直接被略去了。</span> - 将手指的位置映射成鼠标的位置
- 因为想达到手指指哪,鼠标就点击哪的效果,所以必须把手指在投影上的位置,映射成鼠标在电脑里相应的位置,这个其实简单推导一下就可以得出。
黑色框为投影屏幕,大写的X和Y代表的是屏幕的宽和高,红色框为电脑屏幕,假设人的手指在的位置,如果想将鼠标也映射到同样的位置,那么就有 的等比关系成立。这里投影屏幕的宽和高在上面第一步中获取,而电脑屏幕的宽和高,实际上是不需要考虑分辨率的,因为在鼠标的坐标系下,电脑的宽和高都被分成了65535个单位,所以宽和高可以视为65535。根据这些,就可以算出的值来。
- 因为想达到手指指哪,鼠标就点击哪的效果,所以必须把手指在投影上的位置,映射成鼠标在电脑里相应的位置,这个其实简单推导一下就可以得出。
- 根据手指到屏幕的距离,判断点击和非点击两种状态
Kinect
是带有深度摄像头的,也就是说它能够知道画面中的每一点到它的距离。似乎是利用三组红外发射器来实现,所以也就要求物体不能是反射材质,不然会获取不到距离。因为能够知道屏幕的距离,也能知道手指的距离,所以如果手指距离屏幕足够近,那么就可以判断为点击。但是,屏幕有可能不是绝对垂直的,Kinect
也有摆歪的可能性,同时深度数据也不是100%精确,所以在计算屏幕距离时,需要考虑一个容错值,在这个范围内都被视为屏幕,在这里我设置的值是10cm,虽然看上去很多,但是实际效果还不错。但是,这也带来一个很严重的问题,就是手指在离屏幕的位置小于10cm的时候,也被视为了屏幕,这时候指尖就丢失了,手指变成了手指中部(因为手指不是完全平行于墙面的,而是有一定角度,所以指根的地方距离屏幕更远),这就会产生很不稳定的现象,至今没有解决。
上面就是核心的功能,除此之外,还要加入一些鼠标的抖动消除、误差消除的处理,同时我还调用了Kinect
的手势识别功能,直接用手势来完成撤销的操作。这段时间忙着找实习,以后有时间的话,应该会优化指尖识别的算法,同时加入更多的手势来调用操作。
效果展示
(博客园的MarkDown
居然不可以插视频,差评)
演示视频在这里
直接在墙上玩割绳子:
用手在墙上书写(外加用手势来调用撤销):
直接裸手操控PPT:
END
这个项目差不多就这么多啦,剩下的只是优化下各个功能,或者加点新东西进去。从假期里就构思了一个比较有意思的小程序,等这段时间忙结束,应该就会把它敲出来。真是越来越好玩了!