手部动作识别实践

        由于项目需求,对人体姿态识别这块做了一些学习调研,主流的落地方案是基于一种两阶段的思路。先利用一些算法计算人体骨架关键点,再基于关键点检测结果构造动作特征进行动作识别。这里也采用了这种思路来进行尝试。

一、关键点检测

        目前开源的骨架关键点检测算法还比较多,经过比较,选择了以下三种算法进行测试。

1.Openpose

        openpose应该是人体关键点检测比较经典的算法之一了,基于此落地的项目也不少。所以首先对这个项目进行尝试。openpose源码是基于c++编写的,并且已经有python版本基于pytorch、tensorflow等框架的移植项目,均在github上开源。我这里用的是pytorch版本进行测试。配置比较简单,主要是opencv和一些绘图工具,torch用来计算加速,没有的话在cpu也能跑。由于项目主要应用场景是从摄像头实时监测,所以直接运行demo_camera.py。这个文件可以检测两部分内容,一部分是身体关键点共18个,一部分是手部关键点共21个。检测结果精确度非常高,也支持多人,唯一缺点就是fps比较低,一帧一帧地显示,不流畅。我设备的显卡是A3000,跑的时候看了一下cpu与gpu都没满。后面换到另一张3060上测试,fps也没上去。在只检测身体,不检测手部的情况下,fps稍微高了一些,但还是不够。

2.SimDR

       SimDR(现在项目应该叫SimCC)也是一个python开源项目。这个项目主要是看到网上一些大佬的实践经验,将这个算法与yolo系列算法相结合,先用yolo找出图像中的人物框,再对框中的人物做关键点检测,本来以为这种两阶段的会更慢,结果部署完跑通以后发现摄像头实时检测效果非常好,精确度也不错,也支持多人检测。但是这个项目只有身体部分的检测,而没有手部检测,项目当中有手部检测的需求。

3.Mediapipe

        Mediapipe也是一个开源的人体关键点识别库,直接通过pip安装就能使用。经过安装测试,这个库有身体识别模型也有手部识别模型,且在同时检测时也能达到较高的fps,可能的原因是因为这个项目只支持单人的检测,计算量降低了。而项目场景中并没有多人的需求,所以初步采用这个作为关键点检测方案。

二、动作识别

        有了较为精准的关键点检测结果,就能根据结果做动作识别映射。自然想到两种方法:一种是基于动作样式来设计判定规则,比如某些关键点之间的距离、形成的夹角等,为每种动作都设计这样的模板,检测结果符合哪种模版就是那个对应的动作。另一种是基于机器学习的方法,训练一个分类器(svm、knn等),这种方法需要采集若干动作数据,并打上对应动作标签。由于项目的具体动作暂时未知,且后期可能有增加动作的需求,采用第一种方法就需要每增加一种动作都要设计模板,第二种则只需要做动作采集一定数据在训练一次分类器,相比第二种更为简单。这里记录一下对第二种方案的实践过程。

        项目的主要动作集中在手部,身体部分有一些简单动作,只要手部动作识别准确,基本就能满足要求。这里测试了两种任务,一是石头剪刀布手势识别任务,一种是从零到九手势识别任务。

1.数据采集

        Mediapipe输出的landmarklist是关键点的三维度相对坐标,其中z轴表示景深信息。这里没有直接使用坐标值来作为特征,因为动作的判定与每个关键点在图像中的绝对位置是无关的,而与关键点之间的相对位置有关,考虑到手部动作的特点,采用其他20个关键点到掌心关键点(0号关键点)的距离构造特征向量。数据采集方法是写一个采集数据脚本,对着摄像头做分别做剪刀、石头、布的动作,在做对应的动作时,采集的数据就打上对应标签。做每个动作的时候尽量变换不同的角度姿势。贴上部分脚本。思路就是先从landmarks中取21个手部关键点相对坐标值,算出绝对坐标值,然后算出20个关键点到0号点的距离。最后打上标签得到长度为21的列表写进csv文件(0表示剪刀,1表示石头,2表示布)。

        res_hand=[]
        pix=[]
        if hand.multi_hand_landmarks:
            for hand_landmarks in hand.multi_hand_landmarks:
                print('hand_landmarks:', hand_landmarks)
                for item in hand_landmarks.landmark:
                    res_hand.append([item.x,item.y])
                # 关键点可视化
                mpDraw.draw_landmarks(
                    img, hand_landmarks, mphands.HAND_CONNECTIONS)
        image_w, image_h, _ = img.shape
        for xy in res_hand:
            pix.append([xy[0]*image_w,xy[1]*image_h])
        dis=[]
        if pix:
            for i in range(20):
                dis.append(math.pow(pix[i+1][0]-pix[0][0],2)+math.pow(pix[i+1][1]-pix[0][1],2))
            writer.writerow(dis + [0])

2.训练分类器

        这里直接使用knn作为分类器就能够满足分类需求,最后要保存模型。

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score, classification_report


data = pd.read_csv('data.csv')  

X = data.iloc[:, :-1].values  
y = data.iloc[:, -1].values  


X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.1, random_state=42)


knn = KNeighborsClassifier(n_neighbors=5) 
knn.fit(X_train, y_train)


y_pred = knn.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
report = classification_report(y_test, y_pred)

print(f'Accuracy: {accuracy * 100:.2f}%')
print('Classification Report:')
print(report)

# 保存模型
import joblib
joblib.dump(knn, 'knn_classifier.pkl')

3.手势识别

        这里需要注意的就是,训练采用的特征是距离特征,使用模型预测的时候也要将坐标点转化为距离。

knn = joblib.load('knn_classifier.pkl')
predictions = knn.predict(predata)
labels = ['剪刀','石头','布']
for i, prediction in enumerate(predictions):
        print(f'Sample {i + 1} prediction: {prediction}')

效果如下

      数字手势识别实现过程基本相同,贴一张效果图

     4.封装

        写了个简单的界面,方便展示与后面使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值