深度学习落地实战:手势识别

news2024/9/20 22:50:03

 前言

大家好,我是机长

本专栏将持续收集整理市场上深度学习的相关项目,旨在为准备从事深度学习工作或相关科研活动的伙伴,储备、提升更多的实际开发经验,每个项目实例都可作为实际开发项目写入简历,且都附带完整的代码与数据集。可通过百度云盘进行获取,实现开箱即用

正在跟新中~

项目背景

(基于CNN实现手势识别)

在5G时代,随着网络速度的大幅提升和低延迟特性的普及,手势识别技术迎来了前所未有的发展机遇,特别是在视频直播、智能家居和智能驾驶等领域。这些应用场景都极大地依赖于用户与设备之间的高效、直观交互,而手势识别正是实现这一目标的关键技术之一。

项目环境

  • 平台:windows 10
  • 语言环境:python 3.8
  • 编辑器:PyCharm
  • PyThorch版本:1.8

1.创建并跳转到虚拟环境

python -m venv myenv

myenv\Scripts\activate.bat

2. 虚拟环境pip命令安装其他工具包

pip install torch torchvision torchaudio

注:此处只示范安装pytorch,其他工具包安装类似,可通过运行代码查看所确实包提示进行安装

3.pycharm 运行环境配置

进入pytcharm =》点击file =》点击settings=》点击Project:...=》点击 Python Interpreter,进入如下界面

点击add =》点击Existing environment  =》 点击 ... =》选择第一步1创建虚拟环境目录myenv\Scripts\下的python.exe文件点击ok完成环境配置

数据集介绍

       

                                                     训练数据样式                       

数据集是一个包含手势识别信息的综合数据集,具体特点如下:

参与者数量:数据集由14个不同的个体(或“受试者”)组成,每个人都在数据集中贡献了他们的手势数据。

手势种类:每个参与者执行了10种不同的手势,这些手势可能代表了特定的命令、符号或动作,具体取决于数据集的设计目的。

数据重复性:为了增加数据集的多样性和鲁棒性,每种手势都被每个参与者重复了10次。这意味着对于每个手势,数据集都包含了来自每个参与者的10个样本。

总数据量:综合以上信息,数据集总共包含了14(人)x 10(手势种类)x 10(重复次数)= 1400个手势样本。

数据来源:

Kinect数据:这些数据通过Microsoft Kinect传感器获取,可能包括深度图像、彩色图像、骨骼跟踪数据等。Kinect的校准参数也被提供,这对于确保数据的准确性和一致性至关重要。

Leap Motion数据:Leap Motion是一种小型的手部追踪设备,能够提供高精度的手部姿势和手指运动数据。Leap SDK(软件开发工具包)提供的所有相关参数都被包含在内,这些参数可能包括手掌位置、手指关节角度、指尖位置等。

训练数据获取:

私信博主获取

LeNet网络介绍

        LeNet网络,由Yann LeCun及其团队在1990年代初期设计并优化,是卷积神经网络(CNN)领域的先驱之作。其最为人熟知的版本是LeNet-5,该网络在1998年被正式提出,主要用于手写数字识别,尤其是MINIST数据集上的表现尤为出色。

LeNet-5的结构相对简洁而高效,包括两个卷积层(C1、C3)、两个池化层(S2、S4)、两个全连接层(F6、OUTPUT)以及输入层和输出层。卷积层通过卷积核自动提取图像中的特征,池化层则通过下采样减少数据的空间尺寸,同时保留重要信息。全连接层则将提取的特征映射到最终的分类结果上。

LeNet网络的核心优势在于其自动提取特征的能力,这大大减少了传统图像识别方法中对手动设计特征的依赖。此外,其结构简单、计算量相对较小,使得在当时的硬件条件下也能实现较快的训练和推理速度。

然而,受限于当时的硬件条件和计算资源,LeNet网络的规模相对较小,难以处理更大规模或更复杂的图像识别任务。随着计算机硬件和深度学习技术的飞速发展,更加深层、更加复杂的卷积神经网络被设计出来,如AlexNet、VGG、ResNet等,它们在图像识别、分类、检测等领域取得了更加卓越的性能。

尽管如此,LeNet网络作为卷积神经网络的开山之作,其设计思想和基本结构仍然对后来的研究产生了深远的影响。它证明了卷积神经网络在图像识别领域的巨大潜力,并为后续的研究提供了宝贵的经验和启示。在今天,LeNet网络仍然被广泛应用于教学和科研领域,作为学习深度学习和卷积神经网络的基础模型之一。

定义CNN网络

  • 卷积层:用于图像的高级特征
  • 输出层:将卷积提取出的特征进行分类
class LeNet5(nn.Module):
    def __init__(self,num_class=10):
        super(LeNet5,self).__init__()
        self.conv1 = nn.Conv2d(3, 8, 5)
        self.pool1 = nn.AvgPool2d((2, 2))
        
        self.conv2 = nn.Conv2d(8, 16, 5)
        self.pool2 = nn.AvgPool2d((2, 2))
        
        self.conv3 = nn.Conv2d(16, 32, 5)
        
        self.relu = nn.ReLU()

        self.fc1 = nn.Linear(28800, 1024)
        self.fc2 = nn.Linear(1024, num_class)
        
    def forward(self, x):
        # x: torch.Size([32, 3, 150, 150])
        
        x = self.conv1(x) # torch.Size([32, 8, 146, 146])
        x = self.relu(x)
        x = self.pool1(x) # torch.Size([32, 8, 73, 73])

        x = self.conv2(x) # torch.Size([32, 16, 69, 69])
        x = self.relu(x)
        x = self.pool2(x) # torch.Size([32, 16, 34, 34])

        x = self.conv3(x) # torch.Size([32, 32, 30, 30])
        x = self.relu(x)
        
        x = x.flatten(start_dim=1) # torch.Size([32, 28800])
        
        x = self.fc1(x) # torch.Size([32, 2024])
        x = self.relu(x)
        x = self.fc2(x) # torch.Size([32, 4])

        return x

加载数据集

# 1.数据转换
data_transform = {
    # 训练中的数据增强和归一化
    'train': transforms.Compose([
        transforms.RandomResizedCrop(150), # 随机裁剪
        transforms.ToTensor(), # 均值方差归一化
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])
}

# 2.形成训练集
train_dataset = datasets.ImageFolder(root=os.path.join(image_path),
                                     transform=data_transform['train'])


# 3.形成迭代器
train_loader = torch.utils.data.DataLoader(train_dataset,
                                           batch_size,
                                           True)

print('using {} images for training.'.format(len(train_dataset)))

测试代码

# 加载索引与标签映射字典
with open('class_dict.pk', 'rb') as f:
    class_dict = pickle.load(f)

# 数据变换
data_transform = transforms.Compose([
     transforms.CenterCrop(150),
     transforms.ToTensor(),
     transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])

# 图片路径
img_path = r'./data/000-one/gesture-one-2021-03-07_23-07-48-1_37388.jpg'

# 打开图像
img = Image.open(img_path)
print(np.array(img).shape)

# 对图像进行变换
img = data_transform(img)

plt.imshow(img.permute(1,2,0))
plt.show()


# 将图像升维,增加batch_size维度
img = torch.unsqueeze(img, dim=0)

# 获取预测结果
pred = class_dict[model(img).argmax(axis=1).item()]
print('【预测结果分类】:%s' % pred)

完整运行代码

import math
import pickle
import os

import numpy as np
import pandas as pd
import torch
import torch.nn as nn
from keras.preprocessing.sequence import pad_sequences
from sklearn.model_selection import train_test_split
from torch.utils.data import TensorDataset
from torchvision import transforms, datasets
from torch import optim
from torchnet import meter
from tqdm import tqdm
from PIL import Image

import matplotlib.pyplot as plt

# 模型输入参数,需要自己根据需要调整
num_class = 14 # 分类数
epochs = 20 # 迭代次数
batch_size = 64 # 每个批次样本大小
lr = 0.003 # 学习率
image_path = './data' # 图像数据路径
save_path = './best_model.pkl' # 模型保存路径
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu') # 设备

# 1.数据转换
data_transform = {
    # 训练中的数据增强和归一化
    'train': transforms.Compose([
        transforms.RandomResizedCrop(150), # 随机裁剪
        transforms.ToTensor(), # 均值方差归一化
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])
}

# 2.形成训练集
train_dataset = datasets.ImageFolder(root=os.path.join(image_path),
                                     transform=data_transform['train'])


# 3.形成迭代器
train_loader = torch.utils.data.DataLoader(train_dataset,
                                           batch_size,
                                           True)

print('using {} images for training.'.format(len(train_dataset)))

# 4.建立分类标签与索引的关系
cloth_list = train_dataset.class_to_idx
class_dict = {}
for key, val in cloth_list.items():
    class_dict[val] = key
with open('class_dict.pk', 'wb') as f:
    pickle.dump(class_dict, f)

class LeNet5(nn.Module):
    def __init__(self,num_class=10):
        super(LeNet5,self).__init__()
        self.conv1 = nn.Conv2d(3, 8, 5)
        self.pool1 = nn.AvgPool2d((2, 2))
        
        self.conv2 = nn.Conv2d(8, 16, 5)
        self.pool2 = nn.AvgPool2d((2, 2))
        
        self.conv3 = nn.Conv2d(16, 32, 5)
        
        self.relu = nn.ReLU()

        self.fc1 = nn.Linear(28800, 1024)
        self.fc2 = nn.Linear(1024, num_class)
        
    def forward(self, x):
        # x: torch.Size([32, 3, 150, 150])
        
        x = self.conv1(x) # torch.Size([32, 8, 146, 146])
        x = self.relu(x)
        x = self.pool1(x) # torch.Size([32, 8, 73, 73])

        x = self.conv2(x) # torch.Size([32, 16, 69, 69])
        x = self.relu(x)
        x = self.pool2(x) # torch.Size([32, 16, 34, 34])

        x = self.conv3(x) # torch.Size([32, 32, 30, 30])
        x = self.relu(x)
        
        x = x.flatten(start_dim=1) # torch.Size([32, 28800])
        
        x = self.fc1(x) # torch.Size([32, 2024])
        x = self.relu(x)
        x = self.fc2(x) # torch.Size([32, 4])

        return x

# 6.模型训练
model = LeNet5(num_class)
model = model.to('cpu')
criterion = nn.CrossEntropyLoss() # 损失函数
optimizer = torch.optim.Adam(model.parameters(), lr=lr) # 优化器

best_acc = 0 # 最优精确率
best_model = None # 最优模型参数

for epoch in range(epochs):
    model.train()
    running_loss = 0 # 损失
    epoch_acc = 0  # 每个epoch的准确率
    epoch_acc_count = 0  # 每个epoch训练的样本数
    train_count = 0  # 用于计算总的样本数,方便求准确率
    train_bar = tqdm(train_loader)
    for data in train_bar:
        images, labels = data
        optimizer.zero_grad()
        output = model(images.to(device))
        loss = criterion(output, labels.to(device))
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        train_bar.desc = "train epoch[{}/{}] loss:{:.3f}".format(epoch + 1,
                                                                 epochs,
                                                                 loss)
        # 计算每个epoch正确的个数
        epoch_acc_count += (output.argmax(axis=1) == labels.view(-1)).sum()
        train_count += len(images)

    # 每个epoch对应的准确率
    epoch_acc = epoch_acc_count / train_count

    # 打印信息
    print("【EPOCH: 】%s" % str(epoch + 1))
    print("训练损失为%s" % str(running_loss))
    print("训练精度为%s" % (str(epoch_acc.item() * 100)[:5]) + '%')

    if epoch_acc > best_acc:
        best_acc = epoch_acc
        best_model = model.state_dict()

    # 在训练结束保存最优的模型参数
    if epoch == epochs - 1:
        # 保存模型
        torch.save(best_model, save_path)

print('Finished Training')

# 加载索引与标签映射字典
with open('class_dict.pk', 'rb') as f:
    class_dict = pickle.load(f)

# 数据变换
data_transform = transforms.Compose([
     transforms.CenterCrop(150),
     transforms.ToTensor(),
     transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])])

# 图片路径
img_path = r'./data/000-one/gesture-one-2021-03-07_23-07-48-1_37388.jpg'

# 打开图像
img = Image.open(img_path)
print(np.array(img).shape)

# 对图像进行变换
img = data_transform(img)

plt.imshow(img.permute(1,2,0))
plt.show()


# 将图像升维,增加batch_size维度
img = torch.unsqueeze(img, dim=0)

# 获取预测结果
pred = class_dict[model(img).argmax(axis=1).item()]
print('【预测结果分类】:%s' % pred)

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

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

相关文章

部署运维之二:虚拟化

摘要: 在21世纪初的曙光中,虚拟化技术悄然萌芽,标志着计算领域的一次革命性飞跃。这一时期,通过引入虚拟化技术,业界实现了在单一物理服务器之上并行运行多个虚拟机的壮举,每个虚拟机均构筑起一个隔离而独…

【计算机网络】学习指南及导论

个人主页:【😊个人主页】 系列专栏:【❤️计算机网络】 文章目录 前言我们为什么要学计算机网络?计算机网络概述计算机网络的分类按交换技术分类按使用者分类按传输介质分类按覆盖网络分类按覆盖网络分类 局域网的连接方式有线连接…

从零实现大模型-BERT微调

The Annotated Transformer注释加量版:复现Transformer,训练翻译模型 The Annotated GPT2注释加量版:GPT2预训练 The Annotated BERT注释加量版:BERT预训练 从零实现大模型-GPT2指令微调:GPT2指令微调 按照顺序&am…

海外媒体发稿:葡萄牙-实现高效媒体软文发稿计划-大舍传媒

一、葡萄牙媒体环境概述 葡萄牙,位于欧洲大陆西南端的国家,拥有丰富的文化和历史。在这个国家,媒体行业也有着相当大的影响力。葡萄牙的媒体环境多元化,包括电视、广播、报纸、杂志和互联网等各个领域。 二、葡萄牙媒体发稿的重…

Win10+Docker配置TensorRT环境

1.Docker下载和安装 Docker下载:Install Docker Desktop on Windows Docker安装: 勾选直接下一步就行,安装完成后需要电脑重启。 重启后,选择Accept—>Continue without signing in—>skip survey. 可以进入下面页面,并且左下角是绿色的,显示e…

前端开发之盒子模型

目录 盒子分类 display属性 盒子内部结构特征 padding填充区 border边框区 margin外边距 盒子width和height边界 盒子分类 块级盒子(又叫块级元素、块级标签) 特征:独占一行,对宽度高度支持 如:p div ul li h1…

Vue3项目基于Axios封装request请求

在 Vue 3 的项目开发中,使用 Axios 进行 HTTP 请求是非常常见的作法,为了更方便开发者更高效的进行代码编写和项目的维护,可以通过再次封装 Axios 来实现。 在本文中,博主将详细指导你如何在自己的 Vue 3 项目中使用 Axios 二次封…

【Java开发实训】day04——可变参数和递归练习

目录 一、可变参数 1.1定义 1.2注意 1.3示例 二、递归 2.1定义 2.2注意 2.3示例 2.4练习 🌈嗨!我是Filotimo__🌈。很高兴与大家相识,希望我的博客能对你有所帮助。 💡本文由Filotimo__✍️原创,首发于CSDN&…

CSS3实现提示工具的渐入渐出效果及CSS3动画简介

上一篇文章用CSS3实现了一个提示工具,本文介绍如何利用CSS3实现提示工具以渐入的方式呈现,以渐出的方式消失。 CSS3主要可以通过两个样式来实现动画效果:animation和transition。 其中,animation需要自己定义一组关键帧从而实现…

css实现前端水印

单处水印 代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Watermark Example</title>&l…

昇思25天学习打卡营第24天|应用实践之Pix2Pix实现图像转换

基本介绍 今日要实践的模型是Pix2Pix模型&#xff0c;用于图像转换。使用官方的指定数据集&#xff0c;该数据集是已经经过处理的外墙&#xff08;facades&#xff09;数据&#xff0c;可以直接使用mindspore.dataset的方法读取。由于Pix2Pix模型是基于cGAN&#xff08;条件生成…

SQL常用数据过滤---IN操作符

在SQL中&#xff0c;IN操作符常用于过滤数据&#xff0c;允许在WHERE子句中指定多个可能的值。如果列中的值匹配IN操作符后面括号中的任何一个值&#xff0c;那么该行就会被选中。 以下是使用IN操作符的基本语法&#xff1a; SELECT column1, column2, ... FROM table_name WH…

MQ四兄弟:如何实现延时消息

RabbitMQ延时消息 RabbitMQ 本身并没有直接支持延时消息的功能&#xff0c;但是可以通过使用 RabbitMQ 插件或构建消息死信队列&#xff08;Dead Letter Exchange, DLX&#xff09;的方式来实现延时消息。以下是两种实现延时消息的方法&#xff1a; 1、死信队列 (Dead-Letter …

kubernetes集群部署elasticsearch集群,包含无认证和有认证模式

1、背景&#xff1a; 因公司业务需要&#xff0c;需要在测试、生产kubernetes集群中部署elasticsearch集群&#xff0c;因不同环境要求&#xff0c;需要部署不同模式的elasticsearch集群&#xff0c; 1、测试环境因安全性要求不高&#xff0c;是部署一套默认配置&#xff1b; 2…

【LeetCode】十七、并查集

文章目录 1、并查集Union Find2、并查集find的优化&#xff1a;路径压缩 Quick find3、并查集union的优化&#xff1a;权重标记 1、并查集Union Find 并查集&#xff0c;一种树形的数据结构&#xff0c;处理不相交的两个集合的合并与查询问题。 【参考&#xff1a;&#x1f4…

Linux·基本指令(下)

1. mv 指令 (move) 语法&#xff1a;mv[选项] 源文件或目录 目标文件或目录 功能&#xff1a;将源文件或目录剪贴到一个新位置&#xff0c;或给源文件或目录改名但不会改变其内容 常用选项&#xff1a; -f &#xff1a;force 强制&#xff0c;如果目标文件已经存在&#xff0c;…

Unty 崩溃问题(Burst 1.8.2)

错误代码&#xff1a; Assertion failed on expression: exception SCRIPTING_NULL UnityEngine.StackTraceUtility:ExtractStackTrace () Unity.Burst.BurstCompiler:SendRawCommandToCompiler (string Unity版本&#xff1a;2021.3.17F1&#xff0c;Burst 1.8.2 表现&…

openstack设置IP直接登录,不需要加dashboard后缀

openstack 实验环境&#xff0c;openstack-t版&#xff0c;centos2009 修改配置文件 [rootcontroller ~]# vim /WEBROOT /etc/openstack-dashboard/local_settings #将dashboard去掉 WEBROOT /dashboard/ #改为 WEBROOT /[rootcontroller ~]# vim /etc/httpd/conf.d/openst…

pytorch学习(七):池化层的使用

MaxPool2d&#xff1a; 参数详解&#xff1a; kernel_size: int or tuple。 stride&#xff1a;窗口的步长&#xff0c;默认值是kernel_size的值。&#xff08;卷积层默认值为1&#xff09; dilation&#xff1a;如下图&#xff0c;控制窗口内内元素之间的距离。学名空洞卷积…

浅析stm32启动文件

浅析stm32启动文件 文章目录 浅析stm32启动文件1.什么是启动文件&#xff1f;2.启动文件的命名规则3.stm32芯片的命名规则 1.什么是启动文件&#xff1f; 我们来看gpt给出的答案&#xff1a; STM32的启动文件是一个关键的汇编语言源文件&#xff0c;它负责在微控制器上电或复位…