YOLOv5手势物体识别(附代码)

之前是做的yolov3手势物体识别,最近几天我将该项目进行了重新的整理和升级,实现了yolov5手势物体识别,同时为了方便更多的人直接拿来应用,我生成了支持windows系统的应用小程序,即便你电脑上没有安装pytorch,没有安装cuda、python,都可以使用~!


相关资料:

yolov3手势物体识别


应用程序效果如下:

yolov5手势物体识别


技术说明

本项目使用了三个算法模型进行的功能实现。yolov5做手部目标检测,ReXNet(支持Resnet系列)做手部21关键点回归检测,Resnet50做物体分类识别。(其实就是三个算法做的级联)

yolov5手部目标检测

使用yolov5s进行训练,数据集共有3W+,因本地训练环境受限,我只训练到mAP 64%左右,因此准确率并不是很高,大家有需要的可以自行再去训练~

数据集说明

数据集链接:

(ps:这里的数据集采用的公共数据集,没有做过数据清洗)

链接:https://pan.baidu.com/s/1jnXH3yBuGJ8_DRXu-gKtNg 
提取码:yypn 

数据集格式:

images:存放所有的数据集

labels:已经归一化后的label信息

train.txt:训练集划分,25934张

val.txt:验证集划分,3241张

test.txt:测试集划分,3242张图

训练实验记录

采用马赛克数据增强,如下图:

 评价指标:

(我这里只训练了大该十多个epoch,没服务器训练太慢了~(T^T)~,所以准确率比较低,有需要的可以自己训练一下)

PRmAP 0.5mAP 0.5:0.95
0.753960.590750.646710.27652

手部21关键点检测

手部关键点识别采用的网络为ReXNet(支持Resnet系列),这里需要说明的是关键点预测并没有采用openpose网络!而是采用的坐标回归方法,这个问题需要强调一下,不然总有小伙伴问我,而且还很质疑~ 在本任务中,由于有yolo作为前置滤波器算法将手部和背景进行分离,分离后的图像前景和背景相对平衡,而且前景(手部)占主要部分,因此任务其实相对简单,可以采用坐标回归方法。

网络的定义在yolov5_hand_pose/components/hand_keypoints/models/。

21个关键点,那就是有42个坐标(x,y坐标)。因此代码中num_classes=42,代码:

class handpose_x_model(object):
    def __init__(self,
        model_path = '',
        img_size= 256,
        num_classes = 42,# 手部关键点个数 * 2 : 21*2
        model_arch = "rexnetv1"
        ):

再看下网络最终的输出:

这里输出的通道数为num_classes!所以和openpose是有一定区别的!这里必须要说明一下,不然老有人不信哈哈~

    features.append(nn.AdaptiveAvgPool2d(1))
        self.features = nn.Sequential(*features)
        self.output = nn.Sequential(
            nn.Dropout(dropout_factor),
            nn.Conv2d(pen_channels, num_classes, 1, bias=True))

    def forward(self, x):
        x = self.features(x)
        x = self.output(x).squeeze()
        return x

言归正传,21关键点识别效果如下,首先是通过yolov5提取手部box信息,再把手分离出来,将手部图像resize为256x256大小,用关键点网络进行关键点回归。

 

训练

训练代码在train.py中。

可采用提供的预权重进行fine tune训练。

输入以下命令开始训练:

python train.py --model resnet_50 --train_path [数据集路径] --fintune_model 【fine tune模型路径】--batch_size 16

 如果是fine tune训练,建议初始学习率(init_lr)设置为5e-4,否则建议设置为1e-3。

损失函数此次采用的是MSE,还可支持wing loss。

训练好的权重会保存在model_exp中,对应的tensorboard会存储在logs中


分类网络

这里的分类网络采用是resnet50网络,权重为ImageNet数据集上的(1000个类),可以根据自己任务需求去训练。

网络定义在yolov5_hand_pose/components/classify_imagenet/models。

那具体是如何分类的呢?

首先触发分类模型的手势是食指和大拇指捏合。主要是计算两个关键点的欧式距离,当距离小于阈值则为触发状态click_state=True

            # 计算食指大拇指的距离
            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

那么又有个问题了,总不能我两个手指一捏合就触发,这带来了很大的误触,这里解决的方法是判断触发状态(可以理解为时间长短)。代码如下:

'''
    判断各手的click状态是否稳定(点击稳定充电环),即click是否持续一定阈值
    注意:charge_cycle_step 充电步长越大,触发时间越短
'''
def judge_click_stabel(img,handpose_list,charge_cycle_step = 32):
    flag_click_stable = True
    for i in range(len(handpose_list)):
        _,_,_,dict_ = handpose_list[i]
        id_ = dict_["id"]
        click_cnt_ = dict_["click_cnt"]
        pt_ = dict_["choose_pt"]
        if click_cnt_ > 0:
            # print("double_en_pts --->>> id : {}, click_cnt : <{}> , pt : {}".format(id_,click_cnt_,pt_))
            # 绘制稳定充电环
            # 充电环时间控制
            charge_cycle_step = charge_cycle_step # 充电步长越大,触发时间越短
            fill_cnt = int(click_cnt_*charge_cycle_step)
            if fill_cnt < 360:
                cv2.ellipse(img,pt_,(16,16),0,0,fill_cnt,(255,255,0),2)
            else:
                cv2.ellipse(img,pt_,(16,16),0,0,fill_cnt,(0,150,255),4)
            # 充电环未充满,置为 False
            if fill_cnt<360:
                flag_click_stable = False
        else:
            flag_click_stable = False
    return flag_click_stable

当两个手都触发捏合动作,那么判断是有效动作,同时将左右手选定的区域截出来(和yolo的操作类似),送入分类网络进行分类识别。


语音播报

当手势动作触发成功后会触发语音播报函数,此时会自动语音播放"正在识别物体请等待",如果成功识别,并且也有该物体的语音包(需要自己录制),那么会说“您识别的物体为。。。”

语音播报函数:

'''
    启动识别语音进程  该项目中主要用到下面的自定义函数
'''
def audio_process_recognize_up_edge(info_dict):

    while (info_dict["handpose_procss_ready"] == False): # 等待 模型加载
        time.sleep(2)

    gesture_names = ["double_en_pts"] # 姿态列表,
    gesture_dict = {}

    for k_ in gesture_names:#k_= double_en_pts
        gesture_dict[k_] = None #gesture_dict[double_en_pts]=None

    while True:
        time.sleep(0.01)
        # print(" --->>> audio_process")
        try:
            for g_ in gesture_names:  # 输出 double_en_pts ,因为gesture_name列表内容为str,所以g_也是str,输出的g_=double_en_pts
                if gesture_dict[g_] is None:  # gesture_dict[double_en_pts] 为真
                    gesture_dict[g_] = info_dict[g_]  #info_dict[g_]为False
                else:

                    if ("double_en_pts"==g_):
                        if (info_dict[g_]^gesture_dict[g_]) and info_dict[g_]==True:# 判断Click手势信号为上升沿,Click动作开始
                            playsound("./materials/audio/sentences/IdentifyingObjectsWait.mp3")
                            playsound("./materials/audio/sentences/ObjectMayBeIdentified.mp3")
                            if info_dict["reco_msg"] is not None:
                                print("process - (audio_process_recognize_up_edge) reco_msg : {} ".format(info_dict["reco_msg"]))
                                doc_name = info_dict["reco_msg"]["label_msg"]["doc_name"]
                                reco_audio_file = "./materials/audio/imagenet_2012/{}.mp3".format(doc_name)
                                if os.access(reco_audio_file,os.F_OK):# 判断语音文件是否存在
                                    playsound(reco_audio_file)

                                info_dict["reco_msg"] = None

                    gesture_dict[g_] = info_dict[g_]

        except Exception as inst:
            print(type(inst),inst)    # exception instance

        if info_dict["break"] == True:
            break

整个项目,也就是三个算法、语音播报等是采用多进程显现的,四个功能之间的通信是利用的共享字典进行的数据共享。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Web Storage 笔记12 操作购物车

相关内容&#xff1a;购物车实例 WebStorage存储空间足够大&#xff0c;访问都在客户端(Client)完成。有些客户端先处理或检查数据&#xff0c;就可以直接使用WebStorage进行存储&#xff0c;不仅可以提高访问速度&#xff0c;还可以降低服务器的练习。负担。例如&#xff0c;购…

【linux】重定向

重定向 什么是重定向如何实现一个简单的重定向关于重定向的系统调用接口 注意&#xff1a;在看这篇博客之前&#xff0c;最好是要对文件在系统中是如何被打开的以及操作系统是如何管理文件有一个初步了解&#xff0c;如果不了解的话&#xff0c;可以看看这篇博客《初步认识文件…

PyTorch如何修改模型(魔改)

文章目录 PyTorch如何修改模型&#xff08;魔改&#xff09;1.修改模型层(模型框架⭐)1.1通过继承修改模型1.2通过组合修改模型(重点学&#x1f440;)1.3通过猴子补丁修改模型 2.添加外部输入3.添加额外输出参考 PyTorch如何修改模型&#xff08;魔改&#xff09; 对模型缝缝补…

一加12/11/10/Ace2/Ace3手机上锁回锁BL无限重启黑屏9008模式救砖

一加12/11/10/Ace2/Ace3手机官方都支持解锁BL&#xff0c;搞机的用户也比较多&#xff0c;相对于其他品牌来说&#xff0c;并没有做出限制&#xff0c;这也可能是搞机党最后的救命稻草。而厌倦了root搞机的用户&#xff0c;就习惯性回锁BL&#xff0c;希望彻底变回官方原来的样…

QT:输入类控件的使用

LineEdit 录入个人信息 #include "widget.h" #include "ui_widget.h" #include <QDebug> #include <QString>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);// 初始化输入框ui->lineEdit…

数据结构与算法之经典排序算法

一、简单排序 在我们的程序中&#xff0c;排序是非常常见的一种需求&#xff0c;提供一些数据元素&#xff0c;把这些数据元素按照一定的规则进行排序。比如查询一些订单按照订单的日期进行排序&#xff0c;再比如查询一些商品&#xff0c;按照商品的价格进行排序等等。所以&a…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-10.1-NXP SDK 移植

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

随便聊一下 显控科技 控制屏 通过 RS485 接口 上位机 通讯 说明

系统搭建&#xff1a; 1、自己研发的一个小系统&#xff08;采集信号&#xff0c;将采集的信号数字化&#xff09;通过COM口&#xff0c;连接显控屏 COM3 口采用 485 协议送到显控屏&#xff08;显控科技&#xff09;的显示屏展示出来&#xff09;。 2、显控屏 将 展示的数据…

【C++ | 关键字】C++ 关键字介绍

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; ⏰发布时间⏰&#xff1a;2024-05-04 0…

鸿蒙内核源码分析(汇编传参篇) | 如何传递复杂的参数

汇编如何传复杂的参数? 汇编基础篇 中很详细的介绍了一段具有代表性很经典的汇编代码&#xff0c;有循环&#xff0c;有判断&#xff0c;有运算&#xff0c;有多级函数调用。但有一个问题没有涉及&#xff0c;就是很复杂的参数如何处理? 在实际开发过程中函数参数往往是很复…

小程序账号设置以及request请求的封装

一般开发在小程序时&#xff0c;都会有测试版和正式版&#xff0c;这样在开发时会比较方便。 在开发时。产品经理都会给到测试账号和正式账号&#xff0c;后端给的接口也都会有测试环境用到的接口和正式环境用到的接口。 这里讲一讲我这边如何去做的。 1.在更目录随便命名一…

吴恩达2022机器学习专项课程(一)正则化(正则化成本函数正则化线性回归正则化逻辑回归)

目录 一.正则化1.1 正则化的好处1.2 正则化的实现方式 二.正则化改进线性回归的成本函数2.1 正则化后的成本函数的意义2.2 λ参数的作用2.3 不同λ对算法的影响2.4 为什么参数b没有正则化项 三.正则化线性回归的梯度下降3.1 为什么正则化可以在梯度下降迭代中减小w3.2 导数的计…

如何在Mac上恢复格式化硬盘的数据?

“嗨&#xff0c;我格式化了我的一个Mac硬盘&#xff0c;而没有使用Time Machine备份数据。这个硬盘被未知病毒感染了&#xff0c;所以我把它格式化为出厂设置。但是&#xff0c;我忘了备份我的文件。现在&#xff0c;我想恢复格式化的硬盘驱动器并恢复我的文档&#xff0c;您能…

面试算法题精讲:最长公共子串

面试算法题精讲&#xff1a;最长公共子串 最长公共子串问题是指给定两个字符串S1和S2&#xff0c;求它们的公共子串中最长的那一个。其实就是求两个字符串的最长重复子串。 最朴素的算法就是枚举S1和S2的每一对子串&#xff0c;然后判断它们是否相等&#xff0c;时间复杂度是…

手搓堆(C语言)

Heap.h #pragma once#include <stdio.h> #include <stdlib.h> #include <assert.h> #include <stdbool.h> #include <string.h> typedef int HPDataType; typedef struct Heap {HPDataType* a;int size;int capacity; }Heap;//初始化 void Heap…

Java Jackson-jr 库是干什么用的

Jackson-jr 是一个轻量级的Java JSON 处理库。这个库被设计用来替代 Jackson 的复杂性。对比 Jackson 的复杂 API&#xff0c;Jackson-jr 的启动速度更快&#xff0c;包大小更小。 虽然Jackson databind&#xff08;如ObjectMapper&#xff09;是通用数据绑定的良好选择&#…

如何从Mac电脑恢复任何删除的视频

Microsoft Office是包括Mac用户在内的人们在世界各地创建文档时使用的最佳软件之一。该软件允许您创建任何类型的文件&#xff0c;如演示文稿、帐户文件和书面文件。您可以使用 MS Office 来完成。所有Microsoft文档都可以在Mac上使用。大多数情况下&#xff0c;您处理文档&…

苹果CEO对未来一代人工智能投资持乐观态度

尽管在动荡的第二季度&#xff0c;苹果的收入和iPhone销量有所下降&#xff0c;但其新兴的人工智能技术可能会带来急需的提振。 在5月2日的电话财报会议上&#xff0c;苹果公布季度收入为908亿美元&#xff0c;比去年下降4%。iPhone的收入也下降了10%&#xff0c;至460亿美元。…

《Python编程从入门到实践》day19

#昨日知识点回顾 使用unittest模块测试单元和类 #今日知识点学习 第12章 武装飞船 12.1 规划项目 游戏《外星人入侵》 12.2 安装pygame 终端管理器执行 pip install pygame 12.3 开始游戏项目 12.3.1 创建Pygame窗口及响应用户输入 import sysimport pygameclass…

SpringCloud微服务项目创建流程

为了模拟微服务场景&#xff0c;学习中为了方便&#xff0c;先创建一个父工程&#xff0c;后续的工程都以这个工程为准&#xff0c;实用maven聚合和继承&#xff0c;统一管理子工程的版本和配置。 后续使用中只需要只有配置和版本需要自己规定之外没有其它区别。 微服务中分为…