前言 最近长沙疫情不能出门,就只能在家里玩玩,刚好把项目组的leapmotion带回来了,就把互联网+省赛还未完全实现的代码,给实现下,一周前立下个flag要把手势识别系统做出来,结果几天都在玩游戏,想着不行啊,要动手做了,白天干了2天,熬了两天的夜,困难很多还是给肝出来了
leapmotion手势识别的思路及代码展示
间短介绍:leapmotion是我觉得是一款很不错的“玩具”,3D手模型,三维坐标,优秀的精度,200fps的刷新率,其实从项目组买leapmotion,到我真正上手它,到做完这个小项目,其实不到一个月,所以个人感觉上手不难。强烈建议要看文章末尾的BUG总结开发语言:C,C++
开发者:杨富超
编译器工具:vs2019
leapmotion版本:4.1.0
opencv版本:4.1.0
CSDN源码下载:项目源码
动图展示
- 了解leapmotion的重要函数:hand.grab_angle,这个函数是项目的关键函数之一,以hand.grab_angle为主要判断依据,大于2.2的为拳头,小于0.65的为布,剩下的为剪刀。
原理:hand.grabAngle()返回的是除却大拇指四根手指的平均弯曲程度,所以紧握拳头的时候数值为3.14(反复测试过了),出布的时候基本也为0,出剪刀的时候一般在1.8左右.
2.为了减少误差,我们可以取多帧的均值,思路是:设置全局变量
int sum = 0;
//计算平均值用
long long int num = 0;
//ave_number帧一算
int ave_number = 5;
//多少帧一算
然后再调用的函数中用个小算法,num每一帧加一,sum加上每次的hand.grabAngle(),num是五的倍数时,再算平均值,最后把sum归零
leapmotion猜拳游戏代码展示
static void OnFrame(const LEAP_TRACKING_EVENT* frame) {printf("Frame %lli with %i hands.\n", (long long int)frame->info.frame_id, frame->nHands);
//几只手
num++;
for (uint32_t h = 0;
h < frame->nHands;
h++) {//几个手
LEAP_HAND* hand = &frame->pHands[h];
printf("Hand id %i is a %s hand with position (%f, %f, %f).\nthumb:(%f,%f,%f)\n",
hand->id,
(hand->type == eLeapHandType_Left ? "left" : "right"),
hand->palm.position.x,
hand->palm.position.y,
hand->palm.position.z,
hand->thumb.bones->next_joint.x,
hand->thumb.bones->next_joint.y,
hand->thumb.bones->next_joint.z
);
sum += hand->grab_angle;
//五次的总值
if (num % ave_number == 0)
{
int average = sum / ave_number;
//五次的平均值
if (average >= 2.2)
{printf("石头");
CAdd(3);
}
else if (average < 0.6)
{printf("布");
CAdd(1);
}
else
{printf("剪刀");
CAdd(2);
}sum = 0;
}
}
}
- 网上找几张石头剪刀布的图片,方便我们演示,至于演示什么图片,由上面代码中的CAdd函数传入的参数决定,当然这是配置了opencv的
//1:布 2:剪刀 3:拳头4:数字一5:数字二6:数字三 7:数字四 8:数字五 9:点赞 10:ok
void CAdd(int num)
{
std::cout << "Hello OpenCV4.10!\n";
Mat img;
if (num == 1)
{
img = imread("image/a1.png", IMREAD_ANYCOLOR);
}
else if (num==2)
{
img = imread("image/a2.png", IMREAD_ANYCOLOR);
}
else if (num == 3)
{
img = imread("image/a3.png", IMREAD_ANYCOLOR);
}
else if (num == 4)
{
img = imread("image/a4.png", IMREAD_ANYCOLOR);
}
else if (num == 5)
{
img = imread("image/a5.png", IMREAD_ANYCOLOR);
}
else if (num == 6)
{
img = imread("image/a6.png", IMREAD_ANYCOLOR);
}
else if (num == 7)
{
img = imread("image/a7.png", IMREAD_ANYCOLOR);
}
else if (num == 8)
{
img = imread("image/a8.png", IMREAD_ANYCOLOR);
}
else if (num == 9)
{
img = imread("image/good.png", IMREAD_ANYCOLOR);
}
else if (num == 10)
{
img = imread("image/ok.png", IMREAD_ANYCOLOR);
}
if (!img.data)
{
printf("error,no image!\n");
}
imshow("图片显示", img);
waitKey(1);
}
4.对于数字识别,要先了解leapmotion的另外一个函数:hand->digits[0].is_extended,digits[5]是个包含五个手指的数组,digits[0]是大拇指,is_extended函数是bool类型的,如果这个手指伸直,那么就返回true,否则返回flase,有了这个函数,我们可以识别很多的手势,例如:数字一,食指伸直且其他手指均弯曲。介绍下leapmotion另外一个函数hand.pinch_distance,这个函数是返回大拇指与食指间的距离,多用于捏取手势识别,其实is_extended也能用上面介绍的函数hand.grab_angle来做,只是我还没摸清他们的大致判断范围,之后会把范围发表出来
leapmotion数字识别代码展示
/** Callback for when a frame of tracking data is available. */
static void OnFrame(const LEAP_TRACKING_EVENT* frame) {printf("Frame %lli with %i hands.\n", (long long int)frame->info.frame_id, frame->nHands);
//几只手
num++;
for (uint32_t h = 0;
h < frame->nHands;
h++) {//几个手
LEAP_HAND* hand = &frame->pHands[h];
printf("Hand id %i is a %s hand with position (%f, %f, %f).\nthumb:(%f,%f,%f)\n",
hand->id,
(hand->type == eLeapHandType_Left ? "left" : "right"),
hand->palm.position.x,
hand->palm.position.y,
hand->palm.position.z,
hand->thumb.bones->next_joint.x,
hand->thumb.bones->next_joint.y,
hand->thumb.bones->next_joint.z
);
sum += hand->grab_angle;
//五次的总值
//1:布 2:剪刀 3:拳头4:数字一5:数字二6:数字三 7:数字四 8:数字五 9:点赞 10:ok
if (hand->digits[0].is_extended && !hand->digits[1].is_extended && !hand->digits[2].is_extended && !hand->digits[3].is_extended && !hand->digits[4].is_extended)
{
printf("点赞\n");
CAdd(9);
}
else if (hand->pinch_distance < 3.0 && hand->digits[2].is_extended && hand->digits[3].is_extended && hand->digits[4].is_extended)
{
printf("欧克");
CAdd(10);
}
else if (!hand->digits[0].is_extended && hand->digits[1].is_extended && !hand->digits[2].is_extended && !hand->digits[3].is_extended && !hand->digits[4].is_extended)
{
printf("数字一\n");
CAdd(4);
}
else if (!hand->digits[0].is_extended && hand->digits[1].is_extended && hand->digits[2].is_extended && !hand->digits[3].is_extended && !hand->digits[4].is_extended)
{
printf("数字二");
CAdd(5);
}
else if (!hand->digits[0].is_extended && !hand->digits[1].is_extended && hand->digits[2].is_extended && hand->digits[3].is_extended && hand->digits[4].is_extended)
{
printf("数字三");
CAdd(6);
}
else if (!hand->digits[0].is_extended && hand->digits[1].is_extended && hand->digits[2].is_extended && hand->digits[3].is_extended && hand->digits[4].is_extended)
{
printf("数字四");
CAdd(7);
}
else if (hand->digits[0].is_extended && hand->digits[1].is_extended && hand->digits[2].is_extended && hand->digits[3].is_extended && hand->digits[4].is_extended)
{
printf("数字五");
CAdd(8);
}
}
}
心路历程:开始–咋弄了?–有思路了!–人麻了。。–彻底麻了。。–好起来了–完成!
看到一篇博客,很有启发
文章图片
晚上和hxd开黑,遇到了玩的菜的喷子,我当时就想,长了张嘴可惜了,喷子不如日常用手语交流。突然想到leapmotion可以做一个手语交流模型,就有了下面这个备忘录,嗯哼,大二的大创课题有了。
文章图片
文章图片
开干,发现问题了,因为下载的官方的SDK,直接打开的文件夹,没有配置,也就是根本不算一个真正的项目,后期配置不了opencv与数据库,所以决定配置完整的项目。
文章图片
文章图片
网上找博客,配置vs如何配置leapmotion,链接我放在后面的leapmotion的BUG模块了。过程当然是不出意外的话,要出意外了,一个LeapC.lib一直找不到,exe还打不开了,重新新建了项目,重新配置,删除官方文档多余的.c文件,.h文件,又遇到main函数重复,人麻了从凌晨弄到2点,心态崩了,去刷了会儿抖音,然后接着干,因为OpenCV只能配置到.cpp的文件,期间又遇到了.cpp与官方文档的sample里面的.c文件不兼容的问题,LeapC.lib还是没找到,exe在电脑重启后解决了。项目前后看了不下150篇各种网站的博客,帖子,leapmotion在国内的问题解决方案与案例真的还是太少了。最后的BUG介绍值得一看
文章图片
文章图片
凌晨到早上八点一直调bug,熬到了天亮早上8点,去睡觉了,困。
文章图片
下午两点接着干,在LeapC.lib以几乎碰运气得方式解决后,四点成功配置完成leapmotion
文章图片
后来继续睡了会儿,起来配置完所有的opencv与.c与.cpp之间函数的互相调用,所有项目配置完成。然后写算法,找图片,写代码框架,以后再添手势,就是参数问题了,简便很多。毕竟项目组今年获得了互联网+的省金,暑假继续出力,希望明年进国赛【Leapmotion|LeapMotion项目实践(一)-- 手势识别_猜拳+数字(经验满满+各种BUG经验总结+有运行动图)】
文章图片
各种离谱!离谱!搞心态的BUG合集 问题:无法找到LeapC.h文件 解决方案: 1.项目属性的包含目录大概率路径不正确
文章图片
2.直接把官方文档的LeapC.h复制到项目头文件中
文章图片
问题:XXXXXXXXX.exe打不开
解决方案,1.重启VS,2.重启电脑,是因为.exe已经打开过了,不能再次打开,但是它在哪个端口你并不知道,所以重启关闭端口,解决问题。问题:无法找到LeapC.lib文件
解决方案,1,检查目录,标红地方是否正确
文章图片
2.检查附加依赖项,注意是LeapC.lib,之前很多博主都是Leapd.lib,或是Leap.lib,都是错的
文章图片
3.用这个代码来找,加上还是错的话,把LeapC.lib直接复制进来,这个方法一样适合于opencv的lib找不到
#pragma comment(lib,"LeapC.lib")
文章图片
.c文件与.cpp文件互相调用
解决方案:vs怎么同时运行多个main函数
C与C++互相调用
解决方案:解决方案——属性–多个启动项目–选择项目启动
文章图片
opencv不能直接用在官方文档的.c中
解决方案:新建.cpp文件写入opencv的函数,上面用.c与.cpp互相调用的方法void cedl_XXXXXXXXXXX main函数找不到
原理:.c与.cpp(C++)的接口不相容
解决方法:1 .c与.cpp不兼容,更改后缀名leapmotion的学习途径分享
2. lib配置不正确,看上面的lib配置方法
- Leapmotion官网
- vs2019配置opencv
- vs2019配置Leapmotion
- Leapmotion中文文档百度网盘 提取码:0zx3
- B站Leapmotion学习视频(Unity)
- 项目所有代码-百度网盘 提取码:8888
- 记得配置好环境
遇到的问题可以看看本文BUG介绍,也许就有你的BUG
推荐阅读
- C语言|C语言指针进阶(更加深入地了解指针)
- c语言学习记录|C语言指针进阶学习
- 自学教程|<数据结构>链式二叉树的基本操作
- 刷题|<数据结构>来,一起刷题吧——二叉树(单值二叉树、相同的树、对称二叉树、另一棵树的子树、前序遍历)
- 数据结构(c语言实现)|<数据结构>还不会写单向链表(我手把手教你)
- 数据结构(c语言实现)|<数据结构>刷题笔记——链表篇(一)(有动图详解)
- 手撕常用排序算法|希尔排序——C语言实现
- 数据结构|C语言实现插入排序——希尔排序算法
- 数据结构|希尔排序——直插排序的更好优化