使用mediapipe训练手指数字识别

news2024/9/27 19:17:08

mediapipe手指数字识别

本文是从0开始创建一个识别手势的机器学习模型,为了识别手势,采用mediapipe模型,这个模型会返回手指的位置,之后再通过训练一个模型将这些位置分类得到手势

一、导入依赖

import cv2
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
import seaborn as sns
import mediapipe as mp
import time
import pyautogui
import warnings
%matplotlib inline

二、定义mediapipe识别手指的模型

mpHands = mp.solutions.hands
#参数1:是否是静态图片  参数2:最多检测几只手,默认2   参数3:模型的复杂度(0,1)   参数4:最低检测的置信度[0,1]   参数5:最低追踪的置信度
hands = mpHands.Hands(max_num_hands=1)
mpDraw = mp.solutions.drawing_utils  # 将坐标值画在手上

handLmsStytle = mpDraw.DrawingSpec(color=(0, 0, 255), thickness=5)  # 点的样式
handConStytle = mpDraw.DrawingSpec(color=(0, 255, 0), thickness=3)  # 线的样式

三、定义获取手指的坐标

通过mediapipe中的hand获取手指的坐标,一共21个点,每个手指有4个点,手掌上一个点

def get_points(frame,imgWidth,imgHeight):
    
    output_points = []
    imgRGB = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    result = hands.process(imgRGB)
    
    if result.multi_hand_landmarks:
        for handLms in result.multi_hand_landmarks:
            # 参数1:目标图像  参数2:识别的坐标值  参数3[可选]:可以将识别的坐标连接起来  参数4:设置点的样式  参数5:设置线的样式
            # mpDraw.draw_landmarks(frame, handLms, mpHands.HAND_CONNECTIONS, handLmsStytle,
            #                       handConStytle)  # 将识别的坐标值画在img图像上
            
            # 打印所有点的坐标
            for i, lm in enumerate(handLms.landmark):
                # print(i, lm.x, lm.y)  #lm.x是图像的x比例,lm.y是图像的y比例[例如x=0.29  y=0.34 则坐标为[0.29*图像宽,0.34*图像高]]
                xPos = int(lm.x * imgWidth)
                yPos = int(lm.y * imgHeight)
                output_points.append(xPos)
                output_points.append(yPos)
                # print(i, xPos, yPos)
                
    else:
        output_points = [None] * 42

    return output_points

定义图像处理(如果想进行其他操作,可以在此处添加代码)

def process_image(frame):
    frameWidth = frame.shape[1]
    frameHeight = frame.shape[0]
    return get_points(frame,frameHeight,frameWidth)

四、图像处理

数据集下载地址:https://github.com/ardamavi/Sign-Language-Digits-Dataset/tree/master/Dataset

为了处理数据集中的所有图像,我们可以遍历“数字手势”文件夹中的所有文件夹。然后循环遍历这些文件夹中的所有图像。使用’process_image’方法,逐个处理这些图像。将输出数组添加到 array 中。

from IPython.display import clear_output
import os

folder = '../Dataset'

array = []
a = 0

# iterate through all folders
for subfolder in os.listdir(folder):
    sub = folder +'/'+ subfolder
    subfolder_size = len(os.listdir(sub))
    i = 0
    # iterate through all subfolders
    for file in os.listdir(sub):
        frame = cv2.imread(sub + '/' + file)
        points = process_image(frame)
        a = max(a,len(points))
        points_gesture = np.append(points, subfolder, axis=None)
        array.append(points_gesture)
        print("processing: " + subfolder)
        i += 1
        print((i/subfolder_size) * 100, '%')
        clear_output(wait=True)

五、转为dataframe

在处理所有图像后,该阵列可以转换为一个 dataframe。前 42 列是点是位置。例如,列’0’和’1’表示第一个点,'3’和’4’表示第二个点,等等。最后一栏是手势的含义

processed = pd.DataFrame(array)
processed = processed.rename(columns={processed.columns[-1]:"gesture"})
processed.head()

输出的结果:
在这里插入图片描述
将数据写入csv格式,下次直接读取csv格式的文件即可

processed.to_csv('../model/dataframes/gesture-points-raw.csv', index=None)
processed = pd.read_csv('../model/dataframes/gesture-points-raw.csv')

六、修复none值

查看是否有none值

processed[processed.columns[::2]].isnull().sum()

我的没有none值,如果有none值,则需要填充该姿态中的平均值
在这里插入图片描述
填充平均值代码:

for name, group in processed.groupby(["gesture"]):
    # loop through each column
    for label, content in group.items():
        av = content.loc[content.notna()].mean()
        form = processed['gesture'] == name 
        processed.loc[form, label] = processed.loc[form, label].fillna(int(av))
print('There are {} missing values'.format(processed.isnull().sum().sum()))

七、标准化

在接下来的步骤中,需要将数据帧分割成点和手势数据帧。

gesture_points = processed.drop(['gesture'], axis=1)
gesture_meaning = processed['gesture']

由于手指里摄像头远近等影响,导致手指的坐标不能真实代表其手势,所以需要标准话
按照比例表示:

         (value - min) / (max - min)
for index, row in gesture_points.iterrows():
    reshape = np.asarray([row[i::2] for i in range(2)])
    min = reshape.min(axis=1, keepdims=True)
    max = reshape.max(axis=1, keepdims=True)
    normalized = np.stack((reshape-min)/(max-min), axis=1).flatten()
    gesture_points.iloc[[index]] = [normalized]
gesture_points.head(3)

结果如下:
在这里插入图片描述
可以看一下处理后的手势是如何进行拉伸的

blank = np.zeros((100,100,3), np.uint8)
index = 1100
copy, copy_norm = np.copy(blank), np.copy(blank)
for n, m in zip(np.reshape(processed.drop(['gesture'], axis=1).iloc[[index]].values, (-1, 2)), 
                np.reshape(gesture_points.iloc[[index]].values, (-1, 2))):
    cv2.circle(copy, (int(n[0]), int(n[1])), 1, (0, 255, 0))
    cv2.circle(copy_norm, (int(m[0]*100), int(m[1]*100)), 1, (0, 255, 0))

fig, axes = plt.subplots(1, 2, figsize=(8, 3))
axes[0].imshow(copy)
axes[0].set_title('gesture')
axes[1].imshow(copy_norm)
axes[1].set_title('normalized gesture')
plt.show()

拉伸后的结果:
在这里插入图片描述

八、反转手势

flipped_gesture_points = gesture_points.copy()
for c in flipped_gesture_points.columns.values[::2]:
    flipped_gesture_points.loc[:, c] = (1 - flipped_gesture_points.loc[:, c])
flipped_gesture_points.head(3)
copy, copy_flipped = np.copy(blank), np.copy(blank)
for n, f in zip(np.reshape(gesture_points.iloc[[index]].values, (-1, 2)),  
                    np.reshape(flipped_gesture_points.iloc[[index]].values, (-1, 2))):
    cv2.circle(copy, (int(n[0]*100), int(n[1]*100)), 1, (0, 255, 0))
    cv2.circle(copy_flipped, (int(f[0]*100), int(f[1]*100)), 1, (0, 255, 0))
    
fig, axes = plt.subplots(1, 2, figsize=(8, 3))
axes[0].imshow(copy)
axes[0].set_title('gesture')
axes[1].imshow(copy_flipped)
axes[1].set_title('flipped gesture')

翻转后的结果
在这里插入图片描述

gestures = pd.concat([gesture_points, gesture_meaning], axis=1)
reverse_gestures = pd.concat([flipped_gesture_points, gesture_meaning], axis=1)
gesture_dataframe = pd.concat([gestures,reverse_gestures], ignore_index=True)
#结果保存csv
gesture_dataframe.to_csv('../dataframes/gesture-points-processed.csv', index=None)
gesture_dataframe = pd.read_csv('../dataframes/gesture-points-processed.csv')

九、建模

首先,我们需要准备好数据。为了训练模型,需要一个训练和测试集。训练集用于训练模型,测试集用于验证模型。训练和测试都有一个X和y坐标系。x数据是点数据。y数据是标签数据。

from sklearn.model_selection import train_test_split

X_train, X_test, y_train, y_test = train_test_split(gesture_dataframe.drop('gesture', axis=1), 
                                                    gesture_dataframe['gesture'],
                                                    test_size = 0.2, 
                                                    random_state=42)

为了选择正确的算法,我们将使用 Sklearn 的这张图表。不同的算法更适合于不同类型的数据和不同的问题。所以,我们将按照这个图表。首先,我们必须检查数据集是否有超过 50 个样本,因为我们有超过 4000 行。它问我们是否在预测一个类别/标签,我们试图预测一个手势的标签/含义,所以这将是一个“是”。然后我们必须检查我们的数据是否有标签。是的,因为最后一列有手势的意思。然后,我们需要检查数据集的样本是否小于100000。最后我们得到了 ‘LinearSVC’ 估计量。
训练:

from sklearn.svm import SVC
import time

start = time.time()
#Training model
svm_model = SVC(kernel='poly', random_state=42, C=1.0, probability=True)
svm_model.fit(X_train, y_train)

#Calculating elapsed time
stop = time.time()
elapsed_time = ((stop - start) / 60)
print('Training time: {} minutes and {} seconds'
.format(int(elapsed_time), int(((elapsed_time % 1) * 60))))
    
#Calculating score
print('Score:',svm_model.score(X_test, y_test).round(2))

训练结果:可以看到准确率在99%
在这里插入图片描述

十、输出模型

import joblib
joblib.dump(svm_model, '../model/gesture_model_media.pkl', compress=9)

十一、模型调用

import cv2
import mediapipe as mp
import time
import joblib
import numpy as np
import warnings

mpHands = mp.solutions.hands
#参数1:是否是静态图片  参数2:最多检测几只手,默认2   参数3:模型的复杂度(0,1)   参数4:最低检测的置信度[0,1]   参数5:最低追踪的置信度
hands = mpHands.Hands()
mpDraw = mp.solutions.drawing_utils  # 将坐标值画在手上

handLmsStytle = mpDraw.DrawingSpec(color=(0, 0, 255), thickness=5)  # 点的样式
handConStytle = mpDraw.DrawingSpec(color=(0, 255, 0), thickness=3)  # 线的样式

# 求帧数需要
preTime = 0
curTime = 0

# cap = cv2.VideoCapture(0)
cap = cv2.VideoCapture(cv2.CAP_DSHOW)
# cap = cv2.VideoCapture('D:\\360\\renshenglubushou.mp4')

# 从磁盘中加载模型文件
gesture_model = joblib.load(r'..\model\gesture_model_media.pkl')

#获取预测结果
def getPre(xPos,yPos):
    if len(xPos) != 0 and len(yPos) != 0:
        points = np.asarray([xPos, yPos])
        min = points.min(axis=1, keepdims=True)
        max = points.max(axis=1, keepdims=True)
        normalized = np.stack((points - min) / (max - min), axis=1).flatten()  #对数据归一化处理
        predicted_gesture = gesture_model.predict([normalized])
        return predicted_gesture[0]   #返回预测结果


def getHandWay(result, imgWidth, imgHeight, hand):
    for idx, handLms in enumerate(result.multi_hand_landmarks):
        # 判断是左右手
        if result.multi_handedness[idx].classification[0].label == hand:
            xPos = []
            yPos = []
            # 打印所有点的坐标
            for i, lm in enumerate(handLms.landmark):
                xPos.append(int(lm.x * imgWidth))
                yPos.append(int(lm.y * imgHeight))
            pre = getPre(xPos=xPos, yPos=yPos)
            # print(hand + ' ' + str(pre))
            warnings.filterwarnings("ignore")
            if(hand == 'Left'):
                figureShow(img, int(490*1.5), 0, int(555*1.5), 60, hand, pre)
            if(hand == 'Right'):
                figureShow(img, int(555*1.5), 0, int(640*1.5), 60, hand, pre)
            # figureShow(img, 490, 0, 640, 60, hand, pre)


def figureShow(image, l_u, l_d, r_u, r_d, hand, pre_value):
    cv2.rectangle(image, (l_u, l_d), (r_u, r_d), (245, 117, 16), -1)
    # Rep data
    cv2.putText(image, hand, (l_u + 15, l_d + 12),
                cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1, cv2.LINE_AA)
    cv2.putText(image, str(pre_value),
                (l_u + 15, l_d + 50),
                cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)

while True:
    ret, img = cap.read()
    if ret:
        img = cv2.resize(img, (0, 0), fx=1.5, fy=1.5)
        imgRGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # mediapipe处理的事RGB格式的数据
        result = hands.process(imgRGB)  # 引用识别手的函数处理图像

        imgHeight = img.shape[0]
        imgWidth = img.shape[1]  # 获取图像的长和宽

        if result.multi_hand_landmarks:

            for handLms in result.multi_hand_landmarks:
                # 参数1:目标图像  参数2:识别的坐标值  参数3[可选]:可以将识别的坐标连接起来  参数4:设置点的样式  参数5:设置线的样式
                mpDraw.draw_landmarks(img, handLms, mpHands.HAND_CONNECTIONS, handLmsStytle,
                                      handConStytle)  # 将识别的坐标值画在img图像上

                getHandWay(result,imgWidth,imgHeight,'Left')
                getHandWay(result,imgWidth,imgHeight,'Right')

        curTime = time.time()
        fps = 1 / (curTime - preTime)
        preTime = curTime
        cv2.putText(img, f"FPS: {int(fps)}", (30, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 0, 0), 2)

        cv2.imshow('img', img)
        # cv2.imshow('imgRGB',imgRGB)

    if cv2.waitKey(1) == ord('q'):
        break

识别结果:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/809128.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

分库分表之基于Shardingjdbc+docker+mysql主从架构实现读写分离(一)

说明:请先自行安装好docker再来看本篇文章,本篇文章主要实现通过使用docker部署mysql实现读写分离,并连接数据库测试。第二篇将实现使用Shardingjdbc实现springboot的读写分离实现。 基于Docker去创建Mysql的主从架构 #创建主从数据库文件夹…

小黑子—JavaWeb:第四章 Request与Response

JavaWeb入门4.0 1. Request(请求)& Response (响应)2. Request2.1 Request 继承体系2.2 Request 获取请求数据2.2.1 通用方式获取请求参数2.2.2 IDEA模板创建Servlet2.2.3 请求参数中文乱码处理2.2.3 - I POST解决方案2.2.3 - II GET解决方案 2.3 Request 请求转发 3. Resp…

常见网关对比

常见网关对比 目前常见的开源网关大致上按照语言分类有如下几类: Nginxlua :OpenResty、Kong、Orange、Abtesting gateway 等 Java :Zuul/Zuul2、Spring Cloud Gateway、Kaazing KWG、gravitee、Dromara soul 等 Go :Janus、fa…

简单学会MyBatis原生API注解

😀前言 本篇博文是关于MyBatis原生API&注解的使用,希望能够帮助到你😊 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我的文章可以帮助到大家,您…

2023深圳杯数学建模B题

B题 电子资源版权保护问题 版权又称著作权,包括发表权、署名权、修改权、保护作品完整权、复制权、发行权、出租权、展览权、表演权、放映权、广播权、信息网络传播权、摄制权、改编权、翻译权、汇编权及应当由著作权人享有的其他权利。 在计算机网络广泛应用的今…

39.手机导航

手机导航 html部分 <div class"phone"><div class"content"><img class"active" src"./static/20180529205331_yhGyf.jpeg" alt"" srcset""><img src"./static/20190214214253_hsjqw…

基于 ThinkPHP 5.1(稳定版本) 开发wms 进销存系统源码

基于ThinkPHP 5.1&#xff08;LTS版本&#xff09;开发的WMS进销存系统源码 管理员账号密码&#xff1a;admin 一、项目简介 这个系统是一个基于ThinkPHP框架的WMS进销存系统。 二、实现功能 控制台 – 权限管理&#xff08;用户管理、角色管理、节点管理&#xff09; – 订…

BUUCTF——reverse3 适合新手的关于base64加密算法代码的分析

作为一个逆向小白&#xff0c;学了点加密算法就来BUU找点乐子&#xff0c;前7题蛮简单的&#xff0c;然后做到了reverse3&#xff0c;典型的base64加密算法&#xff0c;让我折腾了好久&#xff0c;写篇博客记录一下 顺便说下很多博客并没有对这里的加密算法进行代码上的分析&a…

秋招备战笔试Day1

目录 单选 1. 在 Java 中&#xff0c;存放字符串常量的对象属于&#xff08; &#xff09;类对象。 2.已知如下类定义&#xff1a; 如下哪句可以正确地加入子类中&#xff1f; 3. 下列选项中属于面向对象编程主要特征的是&#xff08;&#xff09; 4.关于下列程序段的输出结…

并发编程可能出现的核心问题

2.1非可见性 如果主内存里有个静态变量flagfalse&#xff0c;然后线程A和B在工作内存都需要操作flag&#xff0c;线程A是while(!false){}&#xff0c;而线程B将flag改为true&#xff0c;但是由于线程A和线程B之间工作内存互相不可见&#xff0c;线程A就会陷入死循环。 2.2指令…

排序链表——力扣148

文章目录 题目描述法一 自顶向下归并排序法二&#xff09;自底向上归并排序 题目描述 题目的进阶问题要求达到 O(nlogn) 的时间复杂度和 O(1) 的空间复杂度&#xff0c;时间复杂度是 O(nlogn) 的排序算法包括归并排序、堆排序和快速排序&#xff08;快速排序的最差时间复杂度是…

【RabbitMQ(day2)】默认(直连)交换机的应用

文章目录 一、第一种模型&#xff08;Hello World&#xff09;二、第二种模型&#xff08;work queue&#xff09;自动确认机制的后果和公平分配 三、阐述默认交换机 这篇博客是以下资料学后的总结&#xff1a; 不良人的RabbitMQ的教学视频 官方启动教程 RabbitMQ中文文档 一、…

php 生成连续递增的Excel列索引 可以控制多少列

今天遇到需要生成对应的下拉&#xff0c;下拉的类 需要PHP 输出一个数组 如 A、B、C、D 到Z 列后 Excel 的列就变成 AA 、AB、 AC 依次类推 查询得知 Excel 最大列数 16384 最大行数 1048576 下面演示3000列或行 <?php$idx [idx > 0];for ($i …

WIZnet W6100-EVB-Pico 静态IP配置教程(二)

W6100是全球第一款支持IPv4/IPv6双核的新一代全硬件以太网TCP/IP协议栈控制器。W6100在WIZnet核心专利技术——全硬件TCP/IP协议栈IPv4的基础上增加了IPv6&#xff0c;解决了嵌入式以太网的接入问题&#xff0c;简单易用&#xff0c;安全稳定&#xff0c;是物联网设备的首选解决…

某文化馆三维建模模型-glb格式-三维漫游-室内导航测试

资源描述 某文化馆某个楼层的三维建模模型&#xff0c;glb格式&#xff0c;适用于three.js开发&#xff0c;可用来做一些三维室内漫游测试和室内导航测试 资源下载地址

Java框架学习(三)spring5高级49讲

文章目录 1、BeanFactory与ApplicationContext2、BeanFactory与ApplicationContext的容器实现BeanFactory的容器实现后处理器排序 ApplicationContext的容器实现 3、Bean的生命周期Bean后处理器 4、常见的Bean后处理器5、常见BeanFactory后处理器6、Aware和InitializingBean接口…

移动零——力扣283

题目描述 双指针 class Solution{ public:void moveZeroes(vector<int>& nums){int n nums.size(), left0, right0;while(right<n){if(nums[right]){swap(nums[right], nums[left]);left;}right;}} };

Golang之路---02 基础语法——常量 (包括特殊常量iota)

常量 //显式类型定义const a string "test" //隐式类型定义const b 20 //多个常量定义 const(c "test2"d 2.3e 27)iota iota是Golang语言的常量计数器&#xff0c;只能在常量表达式中使用 iota在const关键字出现时将被重置为0&#xff0c;const中每新…

Flowable-任务-接受任务

定义 接收任务是一种简单任务&#xff0c;它会等待对应消息的到达。当流程执行到达接收任务时&#xff0c;流程状态会持 久化到数据库中&#xff0c;这意味着该流程将一直处于等待状态&#xff0c;直到引擎接收到一个特定的消息为止&#xff0c;该消息 将触发离开接收任务继续…

Echarts 文字太长用省略号代替

xAxis: [{type: category,data: [materialUserEchartsDate.value[0] ? materialUserEchartsDate.value[0].name : ,materialUserEchartsDate.value[1] ? materialUserEchartsDate.value[1].name : ,materialUserEchartsDate.value[2] ? materialUserEchartsDate.value[2].na…