#基于VR环境下的手指识别键盘输入# 基于轮廓质心的手部识别优化(二)

在上一篇博客中,我们提到了对手部进行识别的方法,接下来,我又选择了三种不同模式的,基于肤色进行识别的方法,这三种算法各有不同,都进行了实现。并且通过实际操作时不断地调整比较,最终将选择效果较好的识别手段。

(1)重心距离法:

主要思路:找到手掌的重心,并找出识别出的手的边缘点,对每一个点进行遍历,依次找出五个距离重心最远的点,可以把他们看作是手指点。

1、对图像做高斯模糊;

2、肤色分割(背景不要有类肤色,如果有,就需要加其他信息来排除干扰);

3、找到手轮廓;

4、计算矩,即重心;

5、寻找指尖。

			/*
			 * 方法一
			 */
//			Point temp;
//			double max = 0;
//			int count = 0;
//			int notice = 0;
//			for (int i = 0; i < conPoint.Count; i++) {
//				temp = conPoint [i];
//				double dist = (temp.x - center.x) * (temp.x - center.x) + (temp.y - center.y) * (temp.y - center.y);
//				if (dist > max) {
//					max = dist;
//					notice = i;
//				}
//				if (dist != max) {
//					count++;
//					if (count > 40) {
//						count = 0;
//						max = 0;
//						bool flag = false;
//						if (center.y < conPoint [notice].y)
//							continue;
//						for (int j = 0; j < fingerTips.Count; j++) {
//							if (Mathf.Abs ((float)conPoint [notice].x - (float)fingerTips [j].x) < 20) {
//								flag = true;
//								break;
//							}
//						}
//						if (flag)
//							continue;
//						fingerTips.Add (conPoint [notice]);
//						Imgproc.circle (rgbaMat, conPoint [notice], 6, new Scalar (0, 0, 255, 255), -1);
//						Imgproc.line (rgbaMat, center, conPoint [notice], new Scalar (0, 0, 255, 255), 2);
//					}
//
//				}
//			}
(2)方法二:曲率分析法

       在手上,指尖和两指之间的凹槽的曲率是最大的。假设手的轮廓是通过一系列点{Pi}来保存的,那么我们可以通过 [Pi, Pi-k] 和 [Pi, Pi+k] 两个向量的内积(点乘)来寻找高曲率的点,如下图的Φ和β的计算结果,因为他们的两个向量的夹角趋向于90度,也就是它们的内积趋向于0,所以内积结果越小,曲率越大,我们只需要设置一个阈值,就可以把手的指尖和两指之间的凹槽都可以找到。
       那么如何分辨指尖和两指之间的凹槽呢?我们可以通过两个向量的叉乘来计算,我们想象下手处于3D空间中,那么就还有一个z方向,β中两个向量的叉乘是大于0的(叉乘方向垂直纸面朝向你,z轴正方向),而Φ处的叉乘是小于0的(叉乘方向垂直纸面远离你,z轴负方向)。

        综上,通过在每个点上构造两个向量,比较他们的点乘和叉乘就可以确定指尖了。

1、对图像做高斯模糊;
2、肤色分割(背景不要有类肤色,如果有,就需要加其他信息来排除干扰);
3、找到手轮廓;

4、寻找指尖。

			/*
			 * 方法三
			 */
//			int max = 0;
//			int count = 0;
//			int notice = 0;
//			Point p1, q, r;
//			for (int i = 5; (i < (conPoint.Count - 5)); i++) {
//				q = conPoint [i - 5];
//				p1 = conPoint [i];
//				r = conPoint [i + 5];
//				double dot = (q.x - p1.x) * (q.y - p1.y) + (r.y - p1.y) * (r.x - p1.x);
//				if (dot < 20 && dot > -20) 
//				{
//					double crossnum = (q.x - p1.x) * (r.y - p1.y) - (r.x - p1.x)*(q.y - p1.y);
//					if (crossnum > 0) {
//						fingerTips.Add (p1);
//						Imgproc.circle (rgbaMat, p1, 6, new Scalar (0, 0, 255, 255), -1);
//						Imgproc.line (rgbaMat, center, p1, new Scalar (0, 0, 255, 255), 2);
//					}
//				}
//			}

(3)基于手掌平均位置的算法:同样也是计算出质心之后,对手掌轮廓上的各个点进行遍历。首先计算出轮廓上各个点到手掌质心位置的平均距离,然后根据调试经验,在这个平均距离的基础上乘上一个系数,假设该系数为2,也就是标记出所有手掌上到质心位置大于平均距离两倍的点。这里需要对手指附近进行处理,把位置相近的点合并。考虑到最终进行传输的是点的数据,所以传输的时候只需要筛选掉位置相近的点即可。

/*
			 * 方法二
			 */
			Point temp;
			double max = 0;
			int count = 0;
			int notice = 0;
			double sum = 0;
			for (int i = 0; i < conPoint.Count; i++) {
				temp = conPoint [i];
				double dist = (temp.x - center.x) * (temp.x - center.x) + (temp.y - center.y) * (temp.y - center.y);
				sum += dist;
			}
				//平均距离
				double average = sum / conPoint.Count;
			for (int i = 0; i < conPoint.Count; i++) {
				temp = conPoint [i];
				double dist = (temp.x - center.x) * (temp.x - center.x) + (temp.y - center.y) * (temp.y - center.y);
				if (dist > average * 1.6f) {
					fingerTips.Add (temp);
					Imgproc.circle (rgbaMat, temp, 6, new Scalar (0, 0, 255, 255), -1);
					Imgproc.line (rgbaMat, center, temp, new Scalar (0, 0, 255, 255), 2);
				}
			}

最终通过比较之后发现,加入了质心之后的手掌识别,在识别单个或者两指伸出时效果比较好。从理论上也可以理解,在如果单纯利用凸包来进行寻找的话,只伸出两指的情况,手掌小指出的转角很容易也被识别为一个凸点。而考虑到距离,则不会有这种情况。

但是在五指识别的时候,明显是加入了凸包元素的识别结果更好。

见下图,玫红色点为凸包识别,蓝色点为距离识别。



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值