手势识别(二) - 静态手势动作识别

这篇具有很好参考价值的文章主要介绍了手势识别(二) - 静态手势动作识别。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

我公司的科室开始在公众号上规划一些对外的技术文章了,包括实战项目、模型优化、端侧部署和一些深度学习任务基础知识,而我负责人体图象相关技术这一系列文章,偶尔也会出一些应用/代码解读等相关的文章。
文章在同步发布至公众号和博客,顺带做一波宣传。有兴趣的还可以扫码加入我们的群。
(文章有写的不好的地方请见谅,另外有啥错误的地方也请大家帮忙指出。)
(另外,文章引用的图片or代码如有侵权,请联系我删除。)

微信公众号:AI炼丹术


更新一下 2022.4.30:由于近期很多人加我,而且发现原作者EricLee的代码加了很多组件模块(本身作者不是单做一个手势识别的),然后我这阵子也比较忙,没办法一个一个粉丝去梳理代码。所以我这边把之前梳理的以及加入我做的静态手势识别那部分代码单独上传到github上了,大家自取即可,https://github.com/ooooxianyu/simple-handpose-recognition。参照readme应该可以直接运行。(另外方便的话大家可以给我github点个星😀)

手势识别(二) - 静态手势动作识别

一、项目概述

​ 上一节我们已经可以实现从手部检测到手部关键点检测这两个简单的功能呢。但是实际上,计算机获取到的信息只是图像上手部的位置和关键点的位置信息。而这些信息代表的意思是什么,我们并不知道。所以本节要对上一节我们所能获取到的这一些信息,做进一步的分析,对一些简单的静态手势动作做识别。

​ 静态手势动作包括数数字,握拳,拉钩等……不难想到,当我们竖起一根食指代表数字一、竖起食指和无名指代表数字二或耶,五根手指合在一起代表握拳,竖起尾指代表拉钩。这些我们日常常用的手势动作,我们可以直接从关节点上对应的位置信息就可以分析得到对应的手势。

​ 本文将利用手部关节点的位置信息,通过制定相应规则和神经网络两种方式,对这些简单的静态手势动作进行识别。

二、技术简述 & 代码实现

1. 基于规则简单实现手势动作识别

​ 这里直接引用EricLee作者再源码中提供的部分简单手势识别规则代码。

​ 作者通过计算手指头关节上节和下节的夹角,设定相关阈值来判断手指是否弯曲或伸直。接着通过五个手指头的弯曲数量来识别手势。如,食指伸直,其他弯曲判定为one;食指和无名指伸直,其他弯曲判定为yearh…以此类推。

目前该示例由于静态手势数据集的限制,目前用手骨骼的二维角度约束定义静态手势,原理如下图,计算向量AC和DE的角度,它们之间的角度大于某一个角度阈值(经验值)定义为弯曲,小于摸一个阈值(经验值)为伸直。
注:这种静态手势识别的方法具有局限性,有条件还是通过模型训练的方法进行静态手势识别。
手势识别(二) - 静态手势动作识别

​ 作者只实现了握拳 fist、五 five、枪 gun、爱你 love、 一 one、 六 six、 三 three、点赞 thumbUp、比耶 yeah,九个动作。根据作者的意思,二维约束的方法定义手势,由于受限于没有大量的静态手势数据集原因,只实现了部分手势动作。

​ 弊端也很明显,由于规则简单,而且是人工设定阈值。容易因为检测出现偏差,阈值设定不合适,导致误检的状态。

完整代码来源:https://codechina.csdn.net/EricLee/dpcas/-/blob/e14f78e482d887f8c440a5027d82be11e571d264/lib/hand_lib/cores/handpose_fuction.py

'''
    求解二维向量的角度
'''
def vector_2d_angle(v1,v2):
    v1_x=v1[0]
    v1_y=v1[1]
    v2_x=v2[0]
    v2_y=v2[1]
    try:
        angle_=math.degrees(math.acos((v1_x*v2_x+v1_y*v2_y)/(((v1_x**2+v1_y**2)**0.5)*((v2_x**2+v2_y**2)**0.5))))
    except:
        angle_ =65535.
    if angle_ > 180.:
        angle_ = 65535.
    return angle_
'''
    获取对应手相关向量的二维角度
'''
def hand_angle(hand_,x=0,y=0):
    angle_list = []
    #---------------------------- thumb 大拇指角度
    angle_ = vector_2d_angle(
        ((int(hand_['0']['x']+x)- int(hand_['2']['x']+x)),(int(hand_['0']['y']+y)-int(hand_['2']['y']+y))),
        ((int(hand_['3']['x']+x)- int(hand_['4']['x']+x)),(int(hand_['3']['y']+y)- int(hand_['4']['y']+y)))
        )
    angle_list.append(angle_)
    #---------------------------- index 食指角度
    angle_ = vector_2d_angle(
        ((int(hand_['0']['x']+x)-int(hand_['6']['x']+x)),(int(hand_['0']['y']+y)- int(hand_['6']['y']+y))),
        ((int(hand_['7']['x']+x)- int(hand_['8']['x']+x)),(int(hand_['7']['y']+y)- int(hand_['8']['y']+y)))
        )
    angle_list.append(angle_)
    #---------------------------- middle 中指角度
    angle_ = vector_2d_angle(
        ((int(hand_['0']['x']+x)- int(hand_['10']['x']+x)),(int(hand_['0']['y']+y)- int(hand_['10']['y']+y))),
        ((int(hand_['11']['x']+x)- int(hand_['12']['x']+x)),(int(hand_['11']['y']+y)- int(hand_['12']['y']+y)))
        )
    angle_list.append(angle_)
    #---------------------------- ring 无名指角度
    angle_ = vector_2d_angle(
        ((int(hand_['0']['x']+x)- int(hand_['14']['x']+x)),(int(hand_['0']['y']+y)- int(hand_['14']['y']+y))),
        ((int(hand_['15']['x']+x)- int(hand_['16']['x']+x)),(int(hand_['15']['y']+y)- int(hand_['16']['y']+y)))
        )
    angle_list.append(angle_)
    #---------------------------- pink 小拇指角度
    angle_ = vector_2d_angle(
        ((int(hand_['0']['x']+x)- int(hand_['18']['x']+x)),(int(hand_['0']['y']+y)- int(hand_['18']['y']+y))),
        ((int(hand_['19']['x']+x)- int(hand_['20']['x']+x)),(int(hand_['19']['y']+y)- int(hand_['20']['y']+y)))
        )
    angle_list.append(angle_)

    return angle_list
'''
    # 二维约束的方法定义手势,由于受限于没有大量的静态手势数据集原因
    # fist five gun love one six three thumbup yeah
    # finger id: thumb index middle ring pink
'''
def h_gesture(img,angle_list):
    thr_angle = 65.
    thr_angle_thumb = 53.
    thr_angle_s = 49.
    gesture_str = None
    if 65535. not in angle_list:
        if (angle_list[0]>thr_angle_thumb)  and (angle_list[1]>thr_angle) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle):
            gesture_str = "fist"
        elif (angle_list[0]<thr_angle_s)  and (angle_list[1]<thr_angle_s) and (angle_list[2]<thr_angle_s) and (angle_list[3]<thr_angle_s) and (angle_list[4]<thr_angle_s):
            gesture_str = "five"
        elif (angle_list[0]<thr_angle_s)  and (angle_list[1]<thr_angle_s) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle):
            gesture_str = "gun"
        elif (angle_list[0]<thr_angle_s)  and (angle_list[1]<thr_angle_s) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]<thr_angle_s):
            gesture_str = "love"
        elif (angle_list[0]>5)  and (angle_list[1]<thr_angle_s) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle):
            gesture_str = "one"
        elif (angle_list[0]<thr_angle_s)  and (angle_list[1]>thr_angle) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]<thr_angle_s):
            gesture_str = "six"
        elif (angle_list[0]>thr_angle_thumb)  and (angle_list[1]<thr_angle_s) and (angle_list[2]<thr_angle_s) and (angle_list[3]<thr_angle_s) and (angle_list[4]>thr_angle):
            gesture_str = "three"
        elif (angle_list[0]<thr_angle_s)  and (angle_list[1]>thr_angle) and (angle_list[2]>thr_angle) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle):
            gesture_str = "thumbUp"
        elif (angle_list[0]>thr_angle_thumb)  and (angle_list[1]<thr_angle_s) and (angle_list[2]<thr_angle_s) and (angle_list[3]>thr_angle) and (angle_list[4]>thr_angle):
            gesture_str = "yeah"

    return gesture_str

2. 基于深度学习的方法实现简单手势动作识别

​ 这里使用EricLee作者提供的14类静态手势动作,具体数据来源可从下方链接获取。
手势识别(二) - 静态手势动作识别

14类静态手势动作数据集来源:https://codechina.csdn.net/EricLee/classification

​ 由于这里的手势动作数据集,背景都很单一,而且数据集的数量并不大。(每类数据集200张的样子)所以如果我们直接用一个分类网络对上述数据集做一个14个类别的分类。那这个效果的鲁棒性会相当差,在实际场景下并不能使用。

​ 我这边对这些数据做了下处理,引入前面得到的手指关节点信息。如下图所示。
手势识别(二) - 静态手势动作识别

​ 可以看出和之前的区别是,我利用前面的手指关键点识别网络,将14类手势动作数据集进行处理,最终只保留了手指的位置信息,并在图像上绘制出来线条。

​ 我们刚开始学分类算法的时候,第一个任务肯定是手写数字识别。我们在图像上将手指关节点绘制出来的,每个手势相当于是一个手写数字一样。比起用原图去做分类,这样去训练一个分类任务出来的模型能够适用于各种不同的背景。(前提条件是关键点的预测是相对准确的。)

​ 小编这边随便找了一个分类任务的源码:https://github.com/weiaicunzai/pytorch-cifar100。代码非常简单,这里就不再演示训练过程了。只需要通过前面的hand-pose对14类手势动作的数据进行预处理,得到上图手指描绘图就可以训练,实现一个分类网络。

​ 将代码加入到作者EricLee的源码里,如下代码段,通过手指关键点估计得到手势绘制图s_img_mask,再将它传入手势识别网络 gesture_model得到类别。output = gesture_model(s_img_mask)

​ 下面代码是EricLee最初一版的代码,我稍微修改了下,加入了gesture_modelgesture_dict,识别手势动作和字典存储(方便后续使用)。同时,作者制定了规则:食指和大拇指的捏和放开判断,即实现了点击(click)判断。

def handpose_track_keypoints21_pipeline(img,hands_dict,hands_click_dict,track_index,algo_img = None,handpose_model = None,gesture_model = None,
                                        index_finger_track = None, gesture_dict = None,title_boxes= [], icon=None,vis = False,dst_thr = 35,angle_thr = 16.):

    hands_list = []

    if algo_img is not None:

        for idx,id_ in enumerate(sorted(hands_dict.keys(), key=lambda x:x, reverse=False)):

            x_min,y_min,x_max,y_max,score,iou_,cnt_,ui_cnt = hands_dict[id_]

            # x_min,y_min,x_max,y_max,score = bbox
            w_ = max(abs(x_max-x_min),abs(y_max-y_min))
            if w_< 60:
                continue
            w_ = w_*1.26

            x_mid = (x_max+x_min)/2
            y_mid = (y_max+y_min)/2

            x1,y1,x2,y2 = int(x_mid-w_/2),int(y_mid-w_/2),int(x_mid+w_/2),int(y_mid+w_/2)

            x1 = np.clip(x1,0,img.shape[1]-1)
            x2 = np.clip(x2,0,img.shape[1]-1)

            y1 = np.clip(y1,0,img.shape[0]-1)
            y2 = np.clip(y2,0,img.shape[0]-1)

            bbox_ = x1,y1,x2,y2

            pts_ = handpose_model.predict(algo_img[y1:y2,x1:x2,:])

            img_mask = np.ones(algo_img.shape, dtype=np.uint8)
            img_mask[:, :, 0] = 255
            img_mask[:, :, 1] = 255
            img_mask[:, :, 2] = 255

            plam_list = []
            pts_hand = {}
            for ptk in range(int(pts_.shape[0]/2)):
                xh = (pts_[ptk*2+0]*float(x2-x1))
                yh = (pts_[ptk*2+1]*float(y2-y1))
                pts_hand[str(ptk)] = {
                    "x":xh,
                    "y":yh,
                    }
                if ptk in [0,1,5,9,13,17]:
                    plam_list.append((xh+x1,yh+y1))
                if ptk == 0: #手掌根部
                    hand_root_ = int(xh+x1),int(yh+y1)
                if ptk == 4: # 大拇指
                    thumb_ = int(xh+x1),int(yh+y1)
                if ptk == 8: # 食指
                    index_ = int(xh+x1),int(yh+y1)

            # 计算食指和大拇指中心坐标
            choose_pt = (int((index_[0]+thumb_[0])/2),int((index_[1]+thumb_[1])/2))
            # 计算掌心
            plam_list = np.array(plam_list)
            plam_center = (np.mean(plam_list[:,0]),np.mean(plam_list[:,1]))

            # 计算食指大拇指的距离
            dst = np.sqrt(np.square(thumb_[0]-index_[0]) +np.square(thumb_[1]-index_[1]))
            # 计算大拇指和手指相对手掌根部的角度:
            angle_ = vector_2d_angle((thumb_[0]-hand_root_[0],thumb_[1]-hand_root_[1]),(index_[0]-hand_root_[0],index_[1]-hand_root_[1]))
            # 判断手的点击click状态,即大拇指和食指是否捏合
            click_state = False
            if dst<dst_thr and angle_<angle_thr: # 食指和大拇指的坐标欧氏距离,以及相对手掌根部的相对角度,两个约束关系判断是否点击
                click_state = True
                cv2.circle(img, choose_pt, 6, (0,0,255),-1) # 绘制点击坐标,为轨迹的坐标
                cv2.circle(img, choose_pt, 2, (255,220,30),-1)
                cv2.putText(img, 'Click {:.1f} {:.1f}'.format(dst,angle_), (int(x_min+2),y2-1),cv2.FONT_HERSHEY_COMPLEX, 0.45, (255, 0, 0),5)
                cv2.putText(img, 'Click {:.1f} {:.1f}'.format(dst,angle_), (int(x_min+2),y2-1),cv2.FONT_HERSHEY_COMPLEX, 0.45, (0, 0, 255))
            else:
                click_state = False
                cv2.putText(img, 'NONE  {:.1f} {:.1f}'.format(dst,angle_), (int(x_min+2),y2-1),cv2.FONT_HERSHEY_COMPLEX, 0.45, (255, 0, 0),5)
                cv2.putText(img, 'NONE  {:.1f} {:.1f}'.format(dst,angle_), (int(x_min+2),y2-1),cv2.FONT_HERSHEY_COMPLEX, 0.45, (0, 0, 255))

            #----------------------------------------------------
            # 记录手的点击(click)计数器,用于判断click稳定输出状态
            if id_ not in hands_click_dict.keys():# 记录手的点击(click)计数器,用于稳定输出
                hands_click_dict[id_] = 0
            if click_state == False:
                hands_click_dict[id_] = 0
            elif click_state == True:
                hands_click_dict[id_] += 1



            #--------------------- 绘制手的关键点连线
            draw_bd_handpose_c(img,pts_hand,x1,y1,2)
            draw_mask_handpose(img_mask, pts_hand, x1, y1, int(((x2-x1)+(y2-y1))/128))

            s_img_mask = img_mask[y1:y2,x1:x2,:]
            s_img_mask = cv2.resize(s_img_mask, (128, 128))
            # cv2.imwrite("output/test_2/123.jpg", s_img_mask)

            s_img_mask = Image.fromarray(s_img_mask)

            if transform is not None:
                s_img_mask = transform(s_img_mask)
            s_img_mask = s_img_mask.unsqueeze(dim=0).cuda()
            output = gesture_model(s_img_mask)
            print(output)

            pre_tag = torch.argmax(output, dim=1)[0].cpu().detach().tolist()
            gesture_name = tags[str(pre_tag)]
            if gesture_name == "gun":
                gesture_name = "one"
            print('label:', gesture_name)
            if id_ in gesture_dict.keys():
                if gesture_dict[id_][0] == gesture_name:
                    gesture_count = gesture_dict[id_][1]+1
                else:
                    gesture_count =0
            else:
                gesture_count = 0
            #----------------------------------------------------
            hands_list.append((pts_hand,(x1,y1),plam_center,{"id":id_,"click":click_state,"click_cnt":hands_click_dict[id_],
                                                             "gesture_name":gesture_name,"gesture_count":gesture_count,"choose_pt":choose_pt})) # 局部21关键点坐标,全局bbox左上坐标,全局掌心坐标
            # 记录手势状态(gesture)计数器,用于手势稳定输出状态
            gesture_dict[id_] = (gesture_name, gesture_count)

        return hands_list

三、结果展示

手势识别(二) - 静态手势动作识别

四、总结

​ 个人感觉效果还是可以接受的。大家可以自己实验玩一玩,提升一下各部分模型的精度。后面几节再继续讲解跟踪和动态动作识别。

相关内容可能没有讲解那么详细,如有疑问可以通过公众号进群咨询。文章来源地址https://www.toymoban.com/news/detail-439934.html

到了这里,关于手势识别(二) - 静态手势动作识别的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 基于Mediapipe的Python手势识别项目(手势识别游戏)附项目源码

    基于Mediapipe的手势识别,完成的手势识别游戏。运行效果图如下: 首先是初始的界面效果图: 游戏规则:屏幕上会出现蚊子和蜜蜂,当手蜷曲握起时,表示抓的动作。如果抓到右边移动的蚊子,则会增加分数,如果抓到右边的蜜蜂,则会出现被蛰到的声音 :) 调用Mediapip

    2024年02月11日
    浏览(109)
  • opencv实战项目 手势识别-手势控制键盘

    手势识别是一种人机交互技术,通过识别人的手势动作,从而实现对计算机、智能手机、智能电视等设备的操作和控制。 1.  opencv实现手部追踪(定位手部关键点) 2.opencv实战项目 实现手势跟踪并返回位置信息(封装调用) 3.opencv实战项目 手势识别-手势控制鼠标 4.opencv实战

    2024年02月13日
    浏览(43)
  • opencv实战项目 手势识别-手势控制鼠标

    手势识别是一种人机交互技术,通过识别人的手势动作,从而实现对计算机、智能手机、智能电视等设备的操作和控制。 1.  opencv实现手部追踪(定位手部关键点) 2.opencv实战项目 实现手势跟踪并返回位置信息(封装调用) 3.手势识别-手势音量控制(opencv) 4.opencv实战项目

    2024年02月13日
    浏览(50)
  • 姿态识别、手势识别(附代码)

    姿态识别技术是一种基于计算机视觉的人体姿态分析方法,可以通过分析人体的姿态,提取出人体的关键点和骨架信息,并对人体的姿态进行建模和识别。随着深度学习技术的发展,近年来姿态识别技术得到了广泛的应用和研究,其中Pose是一种基于深度学习的姿态识别工具包

    2023年04月25日
    浏览(45)
  • opencv实战项目 手势识别-手势音量控制(opencv)

     本项目是使用了谷歌开源的框架mediapipe,里面有非常多的模型提供给我们使用,例如面部检测,身体检测,手部检测等。 手势识别系列文章 1.opencv实现手部追踪(定位手部关键点) 2.opencv实战项目 实现手势跟踪并返回位置信息(封装调用) 3.手势识别-手势音量控制(open

    2024年02月12日
    浏览(46)
  • Python——基于pytorch的3D视频动作识别

            pytorch初接触——唐宇迪教教程的3D卷积视频动作识别。接触之后,发现pytorch比tensorflow的用户体验要好一点点,TF由于兼容性问题,从其他地方拿到代码,第一感觉就是跑不起来,很多代码都是基于TF1.x写的,跟2.x一堆不兼容问题。由此开始研究pytorch,后面用的顺手

    2023年04月08日
    浏览(37)
  • PAJ7620U2手势识别——配置手势数据寄存器(6)

      我们已经把所有操作寄存器配置好了,接下来就可以读取手势数据了。本章教程会带领各位读者完成对手势数据寄存器的配置,内容比较简单。   结合官方数据手册:   我们读取0x43或者0x44寄存器内的数据,挥手动作将会被这两个寄存器捕捉到,捕捉完成后读取即可

    2024年02月10日
    浏览(51)
  • 基于opencv的手势识别

    大家好,我是一名本科生,我的主要学习方向是计算机视觉以及人工智能。按照目前的学习进度来说,我就是一小白,在这里写下自己编写的程序,与大家分享,记录一下自己的成长。 思路分析 获取图片,在图片中找到手,然后进行一系列的闭运算,降噪平滑处理,轮廓查

    2024年02月03日
    浏览(58)
  • OpenCV-手势语言识别

    本部分包括Python环境、TensorFlow环境和OpenCV-Python环境。 需要Python 3.6及以上配置,在Windows环境下推荐下载Anaconda完成Python所需的配置,下载地址为:https://www.anaconda.com/,也可以下载虚拟机在Linux环境下运行代码。 打开Anaconda Prompt,输入清华仓库镜像,输入命令: conda config–

    2024年02月11日
    浏览(56)
  • opencv之(多)手势识别

    目录 一、开发环境 二、手势识别的概述 三、Hand Landmark模型 四、工具和依赖库安装 4.1 python下载及安装: 4.2 pycharm 安装 五、多手势识别源码 5.1 源码使用注意事项 5.2 运行效果展示 1、pycharm 2、python3.9 3、opencv-python 4、mdiapipe-0.8.3 感知手的形状和运动的能力是改善各种技术领

    2024年02月05日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包