AI项目二十一:视频动态手势识别

若该文为原创文章,转载请注明原文出处。

一、简介

人工智能的发展日新月异,也深刻的影响到人机交互领域的发展。手势动作作为一种自然、快捷的交互方式,在智能驾驶、虚拟现实等领域有着广泛的应用。手势识别的任务是,当操作者做出某个手势动作后,计算机能够快速准确的判断出该手势的类型。本文将使用ModelArts开发训练一个视频动态手势识别的算法模型,对上滑、下滑、左滑、右滑、打开、关闭等动态手势类别进行检测,实现类似隔空手势的功能。

在前面也有使用mediapipe实现类似功能。具体自行参考。

本文章参考CNN-VIT 视频动态手势识别【玩转华为云】-云社区-华为云

二、环境

使用的是AUTODL,配置如下:

镜像:PyTorch  1.7.0   Python  3.8(ubuntu18.04)   Cuda  11.0

GPU :RTX 2080 Ti(11GB) * 1升降配置

CPU12 vCPU Intel(R) Xeon(R) Platinum 8255C CPU @ 2.50GHz

三、环境搭建

1、创建虚拟环境

conda create -n cnn_hand_gesture_env python=3.8

2、激活

conda activate cnn_hand_gesture_env

3、安装依赖项

conda install cudatoolkit=11.3.1 cudnn=8.2.1 -y --override-channels --channel https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main

pip install tensorflow-gpu==2.5.0 -i https://pypi.doubanio.com/simple --userpip install opencv-contrib-python
pip install imageio
pip install imgaug
pip install tqdm
pip install IPythonpip install numpy==1.19.3 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install matplotlib==3.6

这里需要注意的是numpy版本和matplotlib版本,tensorflow2.5版本对应的numpy版本是1.19.3

如果版本过高会一直出错错误。

四、数据下载

下载数据使用的是华为云,可以自行下载或联系我。

import os
import moxing as moxif not os.path.exists('hand_gesture'):mox.file.copy_parallel('obs://modelbox-course/hand_gesture', 'hand_gesture')

五、算法简介

视频动态手势识别算法首先使用预训练网络InceptionResNetV2逐帧提取视频动作片段特征,然后输入Transformer Encoder进行分类。我们使用动态手势识别样例数据集对算法进行测试,总共包含108段视频,数据集包含无效手势、上滑、下滑、左滑、右滑、打开、关闭等7种手势的视频,具体操作流程如下:

六、流程

1、将采集的视频文件解码抽取关键帧,每隔4帧保存一次,然后对图像进行中心裁剪和预处理

2、创建图像特征提取器,使用预训练模型InceptionResNetV2提取图像特征

3、提取视频特征向量,如果视频不足40帧就创建全0数组进行补白

4、创建VIT Mode

5、视频推理

6、加载VIT Model,获取视频类别索引标签

7、使用图像特征提取器InceptionResNetV2提取视频特征

8、将视频序列的特征向量输入Transformer Encoder进行预测

9、打印模型预测结果

七、测试

Autodl自带有JupyterLab, 直接运行一遍。

代码解析:

1、创建视频输入管道获取视频类别标签

videos = glob.glob('hand_gesture/*.mp4')
np.random.shuffle(videos)
labels = [int(video.split('_')[-2]) for video in videos]
videos[:5], len(videos), labels[:5], len(videos)

2、视频抽帧预处理

def load_video(file_name):cap = cv2.VideoCapture(file_name) # 每隔多少帧抽取一次frame_interval = 4frames = []count = 0while True:ret, frame = cap.read()if not ret:break# 每隔frame_interval帧保存一次if count % frame_interval == 0:# 中心裁剪    frame = crop_center_square(frame)# 缩放frame = cv2.resize(frame, (IMG_SIZE, IMG_SIZE))# BGR -> RGB  [0,1,2] -> [2,1,0]frame = frame[:, :, [2, 1, 0]]frames.append(frame)count += 1return np.array(frames)   

3、创建图像特征提取器

def get_feature_extractor():feature_extractor = keras.applications.inception_resnet_v2.InceptionResNetV2(weights = 'imagenet',include_top = False,pooling = 'avg',input_shape = (IMG_SIZE, IMG_SIZE, 3))preprocess_input = keras.applications.inception_resnet_v2.preprocess_inputinputs = keras.Input((IMG_SIZE, IMG_SIZE, 3))preprocessed = preprocess_input(inputs)outputs = feature_extractor(preprocessed)model = keras.Model(inputs, outputs, name = 'feature_extractor')return model

4、提取视频图像特征

def load_data(videos, labels):video_features = []for video in tqdm(videos):frames = load_video(video)counts = len(frames)# 如果帧数小于MAX_SEQUENCE_LENGTHif counts < MAX_SEQUENCE_LENGTH:# 补白diff = MAX_SEQUENCE_LENGTH - counts# 创建全0的numpy数组padding = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3))# 数组拼接frames = np.concatenate((frames, padding))# 获取前MAX_SEQUENCE_LENGTH帧画面frames = frames[:MAX_SEQUENCE_LENGTH, :]# 批量提取特征video_feature = feature_extractor.predict(frames)video_features.append(video_feature)return np.array(video_features), np.array(labels)

5、编码器

# 编码器
class TransformerEncoder(layers.Layer):def __init__(self, num_heads, embed_dim):super().__init__()self.p_embedding = PositionalEmbedding(MAX_SEQUENCE_LENGTH, NUM_FEATURES)self.attention = layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim, dropout=0.1)self.layernorm = layers.LayerNormalization()def call(self,x):# positional embeddingpositional_embedding = self.p_embedding(x)# self attentionattention_out = self.attention(query = positional_embedding,value = positional_embedding,key = positional_embedding,attention_mask = None)# layer norm with residual connection        output = self.layernorm(positional_embedding + attention_out)return output

6、训练模式

history = model.fit(train_dataset,epochs = 1000,steps_per_epoch = train_count // batch_size, validation_steps = test_count // batch_size, validation_data = test_dataset,callbacks = [checkpoint, earlyStopping, rlp])

7、测试

# 视频预测
def testVideo():test_file = random.sample(videos, 1)[0]label = test_file.split('_')[-2]print('文件名:{}'.format(test_file) )print('真实类别:{}'.format(label_to_name.get(int(label))) )# 读取视频每一帧frames = load_video(test_file)# 挑选前帧MAX_SEQUENCE_LENGTH显示frames = frames[:MAX_SEQUENCE_LENGTH].astype(np.uint8)# 保存为GIFimageio.mimsave('animation.gif', frames, duration=10)# 获取特征feat = getVideoFeat(frames)# 模型推理prob = model.predict(tf.expand_dims(feat, axis=0))[0]print('预测类别:')for i in np.argsort(prob)[::-1][:5]:print('{}: {}%'.format(label_to_name[i], round(prob[i]*100, 2)))#return display(Image(open('animation.gif', 'rb').read()))

8、源码

import cv2
import glob
import numpy as np
from tqdm import tqdm
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import matplotlib.pyplot as pltfrom collections import Counter
import random
import imageio
from IPython.display import Imagefrom tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau#%matplotlib inlineMAX_SEQUENCE_LENGTH = 40
IMG_SIZE = 299
NUM_FEATURES = 1536# 图像中心裁剪
def crop_center_square(img):h, w = img.shape[:2]square_w = min(h, w)start_x = w // 2 - square_w // 2end_x = start_x + square_wstart_y = h // 2 - square_w // 2end_y = start_y + square_wresult = img[start_y:end_y, start_x:end_x]return result# 视频抽帧预处理
def load_video(file_name):cap = cv2.VideoCapture(file_name) # 每隔多少帧抽取一次frame_interval = 4frames = []count = 0while True:ret, frame = cap.read()if not ret:break# 每隔frame_interval帧保存一次if count % frame_interval == 0:# 中心裁剪    frame = crop_center_square(frame)# 缩放frame = cv2.resize(frame, (IMG_SIZE, IMG_SIZE))# BGR -> RGB  [0,1,2] -> [2,1,0]frame = frame[:, :, [2, 1, 0]]frames.append(frame)count += 1return np.array(frames) # 创建图像特征提取器
def get_feature_extractor():feature_extractor = keras.applications.inception_resnet_v2.InceptionResNetV2(weights = 'imagenet',include_top = False,pooling = 'avg',input_shape = (IMG_SIZE, IMG_SIZE, 3))preprocess_input = keras.applications.inception_resnet_v2.preprocess_inputinputs = keras.Input((IMG_SIZE, IMG_SIZE, 3))preprocessed = preprocess_input(inputs)outputs = feature_extractor(preprocessed)model = keras.Model(inputs, outputs, name = 'feature_extractor')return model# 提取视频图像特征
def load_data(videos, labels):video_features = []for video in tqdm(videos):frames = load_video(video)counts = len(frames)# 如果帧数小于MAX_SEQUENCE_LENGTHif counts < MAX_SEQUENCE_LENGTH:# 补白diff = MAX_SEQUENCE_LENGTH - counts# 创建全0的numpy数组padding = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3))# 数组拼接frames = np.concatenate((frames, padding))# 获取前MAX_SEQUENCE_LENGTH帧画面frames = frames[:MAX_SEQUENCE_LENGTH, :]# 批量提取特征video_feature = feature_extractor.predict(frames)video_features.append(video_feature)return np.array(video_features), np.array(labels)# 位置编码
class PositionalEmbedding(layers.Layer):def __init__(self, seq_length, output_dim):super().__init__()# 构造从0~MAX_SEQUENCE_LENGTH的列表self.positions = tf.range(0, limit=MAX_SEQUENCE_LENGTH)self.positional_embedding = layers.Embedding(input_dim=seq_length, output_dim=output_dim)def call(self,x):# 位置编码positions_embedding = self.positional_embedding(self.positions)# 输入相加return x + positions_embedding# 编码器
class TransformerEncoder(layers.Layer):def __init__(self, num_heads, embed_dim):super().__init__()self.p_embedding = PositionalEmbedding(MAX_SEQUENCE_LENGTH, NUM_FEATURES)self.attention = layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim, dropout=0.1)self.layernorm = layers.LayerNormalization()def call(self,x):# positional embeddingpositional_embedding = self.p_embedding(x)# self attentionattention_out = self.attention(query = positional_embedding,value = positional_embedding,key = positional_embedding,attention_mask = None)# layer norm with residual connection        output = self.layernorm(positional_embedding + attention_out)return outputdef video_cls_model(class_vocab):# 类别数量classes_num = len(class_vocab)# 定义模型model = keras.Sequential([layers.InputLayer(input_shape=(MAX_SEQUENCE_LENGTH, NUM_FEATURES)),TransformerEncoder(2, NUM_FEATURES),layers.GlobalMaxPooling1D(),layers.Dropout(0.1),layers.Dense(classes_num, activation="softmax")])# 编译模型model.compile(optimizer = keras.optimizers.Adam(1e-5), loss = keras.losses.SparseCategoricalCrossentropy(from_logits=False),metrics = ['accuracy'])return model# 获取视频特征
def getVideoFeat(frames):frames_count = len(frames)# 如果帧数小于MAX_SEQUENCE_LENGTHif frames_count < MAX_SEQUENCE_LENGTH:# 补白diff = MAX_SEQUENCE_LENGTH - frames_count# 创建全0的numpy数组padding = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3))# 数组拼接frames = np.concatenate((frames, padding))# 取前MAX_SEQ_LENGTH帧frames = frames[:MAX_SEQUENCE_LENGTH,:]# 计算视频特征 N, 1536video_feat = feature_extractor.predict(frames)return video_feat# 视频预测
def testVideo():test_file = random.sample(videos, 1)[0]label = test_file.split('_')[-2]print('文件名:{}'.format(test_file) )print('真实类别:{}'.format(label_to_name.get(int(label))) )# 读取视频每一帧frames = load_video(test_file)# 挑选前帧MAX_SEQUENCE_LENGTH显示frames = frames[:MAX_SEQUENCE_LENGTH].astype(np.uint8)# 保存为GIFimageio.mimsave('animation.gif', frames, duration=10)# 获取特征feat = getVideoFeat(frames)# 模型推理prob = model.predict(tf.expand_dims(feat, axis=0))[0]print('预测类别:')for i in np.argsort(prob)[::-1][:5]:print('{}: {}%'.format(label_to_name[i], round(prob[i]*100, 2)))#return display(Image(open('animation.gif', 'rb').read()))if __name__ == '__main__':print('Tensorflow version: {}'.format(tf.__version__))print('GPU available: {}'.format(tf.config.list_physical_devices('GPU')))# 创建视频输入管道获取视频类别标签videos = glob.glob('hand_gesture/*.mp4')np.random.shuffle(videos)labels = [int(video.split('_')[-2]) for video in videos]videos[:5], len(videos), labels[:5], len(videos)print(labels)# 显示数据分布情况counts = Counter(labels)print(counts)plt.figure(figsize=(8, 4))plt.bar(counts.keys(), counts.values())plt.xlabel('Class label')plt.ylabel('Number of samples')plt.title('Class distribution in videos')plt.show()# 显示视频label_to_name = {0:'无效手势', 1:'上滑', 2:'下滑', 3:'左滑', 4:'右滑', 5:'打开', 6:'关闭', 7:'放大', 8:'缩小'}print(label_to_name.get(labels[0]))frames = load_video(videos[0])frames = frames[:MAX_SEQUENCE_LENGTH].astype(np.uint8)imageio.mimsave('test.gif', frames, durations=10)print('mim save test.git')#display(Image(open('test.gif', 'rb').read()))#frames.shapeprint(frames.shape)feature_extractor = get_feature_extractor()feature_extractor.summary()video_features, classes = load_data(videos, labels)video_features.shape, classes.shapeprint(video_features.shape)print(classes.shape)# Datasetbatch_size = 16dataset = tf.data.Dataset.from_tensor_slices((video_features, classes))dataset = dataset.shuffle(len(videos))test_count = int(len(videos) * 0.2)train_count = len(videos) - test_countdataset_train = dataset.skip(test_count).cache().repeat()dataset_test = dataset.take(test_count).cache().repeat()train_dataset = dataset_train.shuffle(train_count).batch(batch_size)test_dataset = dataset_test.shuffle(test_count).batch(batch_size)train_dataset, train_count, test_dataset, test_countprint(train_dataset)print(train_count)print(test_dataset)print(test_count)# 模型实例化model = video_cls_model(np.unique(labels))# 打印模型结构model.summary()# 保存检查点checkpoint = ModelCheckpoint(filepath='best.h5', monitor='val_loss', save_weights_only=True, save_best_only=True, verbose=1, mode='min')# 提前终止earlyStopping = EarlyStopping(monitor='loss', patience=50, mode='min', baseline=None)# 减少learning raterlp = ReduceLROnPlateau(monitor='loss', factor=0.7, patience=30, min_lr=1e-15, mode='min', verbose=1)# 开始训练history = model.fit(train_dataset,epochs = 1000,steps_per_epoch = train_count // batch_size, validation_steps = test_count // batch_size, validation_data = test_dataset,callbacks = [checkpoint, earlyStopping, rlp])# 绘制结果plt.plot(history.epoch, history.history['loss'], 'r', label='loss')plt.plot(history.epoch, history.history['val_loss'], 'g--', label='val_loss')plt.title('VIT Model')plt.xlabel('Epoch')plt.ylabel('Loss')plt.legend()plt.plot(history.epoch, history.history['accuracy'], 'r', label='acc')plt.plot(history.epoch, history.history['val_accuracy'], 'g--', label='val_acc')plt.title('VIT Model')plt.xlabel('Epoch')plt.ylabel('Accuracy')plt.legend()# 加载训练最优权重model.load_weights('best.h5')# 模型评估model.evaluate(dataset.batch(batch_size))# 保存模型model.save('saved_model')print('save model')# 手势识别# 加载模型model = tf.keras.models.load_model('saved_model')# 类别标签label_to_name = {0:'无效手势', 1:'上滑', 2:'下滑', 3:'左滑', 4:'右滑', 5:'打开', 6:'关闭', 7:'放大', 8:'缩小'}# 视频推理for i in range(20):testVideo()

运行后会训练模型

并保存模型测试,

测试结果

如有侵权,或需要完整代码,请及时联系博主。

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

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

相关文章

【QT教程】QT6图形渲染与OpenGL编程

QT6图形渲染与OpenGL编程 使用AI技术辅助生成 QT界面美化视频课程 QT性能优化视频课程 QT原理与源码分析视频课程 QT QML C扩展开发视频课程 免费QT视频课程 您可以看免费1000个QT技术视频 免费QT视频课程 QT统计图和QT数据可视化视频免费看 免费QT视频课程 QT性能优化视频免…

写文献综述常用的几种深度神经网络模型!

写文献综述常用的几种深度神经网络模型 卷积神经网络&#xff08;CNN&#xff09; 解释说明&#xff1a;专门用于处理图像和图像数据的深度学习模型。它通过卷积层、池化层等操作提取图像特征。应用&#xff1a;图像分类、目标检测、人脸识别等。未来改进&#xff1a;进一步提…

Linux 文件管理命令sum setfacl getfacl chacl

文章目录 2.Linux 文件管理命令2.58 sum&#xff1a;计算文件的校验和&#xff0c;以及文件占用的块数案例练习 2.59 setfacl&#xff1a;设定文件访问控制列表案例练习 2.60 getfacl&#xff1a;获取文件访问控制列表案例练习 2.61 chacl&#xff1a;更改文件或目录的访问控制…

Docker使用方法

Docker是一种容器化平台&#xff0c;它可以帮助开发人员将应用程序和其依赖项打包成一个独立的、可移植的容器&#xff0c;以便在不同的环境中运行。 以下是使用Docker的基本步骤&#xff1a; 安装Docker&#xff1a;首先&#xff0c;您需要在您的机器上安装Docker。您可以从D…

Pytorch:神经网络过程代码详解

文章目录 一、基本概念1、epoch2、遍历DataLoader 二、神经网络训练过程代码详解步骤一&#xff1a;选择并初始化优化器步骤二&#xff1a;计算损失步骤三&#xff1a;反向传播步骤四&#xff1a;更新模型参数步骤五&#xff1a;清空梯度组合到训练循环中步骤六&#xff1a;保存…

windows和mac 电脑 部署Ollama

官网地址&#xff1a;https://ollama.com/ github地址&#xff1a;https://github.com/ollama/ollama 一、windows下 https://github.com/ollama/ollama 安装大模型 ollama run llama3 下载的大模型地址&#xff1a; C:\Users\dengg\.ollama 4.34G

二维数组-----刷题2

题目不是傻子题目&#xff0c;但很简单&#xff01;定义一个变量k&#xff0c;在嵌套中不断累加输出即可。 #include<cstdio> int k,n; int main(){scanf("%d",&n);for(int i1;i<n;i){for(int j1;j<n;j){k;printf("%d ",k);}printf("…

Python基础学习之记录中间文件

倘若想记录代码运行过程中的结果文件&#xff0c;那么以下函数仅供参考 代码示例&#xff1a; import os import datetime import sys import pandas as pd# 定义总的文件夹路径 base_folder E:\\D\\log\\product_data_compare_log# 定义一个函数来创建带时间戳的文件夹 def…

LoRa模块在智能灌溉系统中的应用特点介绍

LoRa模块在智能灌溉系统中的应用特点主要体现在以下几个方面&#xff1a; 低功耗与长寿命&#xff1a; LoRa模块具有极低的功耗&#xff0c;使其在待机状态下耗电量极低&#xff0c;能够支持长时间连续运行&#xff0c;减少了频繁更换电池或充电的需求&#xff0c;确保了智能灌…

【Godot4.2】有序和无序列表函数库 - myList

概述 在打印输出或其他地方可能需要构建有序或无序列表。本质就是构造和维护一个纯文本数组。并用格式化文本形式&#xff0c;输出带序号或前缀字符的多行文本。 为此我专门设计了一个类myList&#xff0c;来完成这项任务。 代码 以下是myList类的完整代码&#xff1a; # …

SQL Sever无法连接服务器

SQL Sever无法连接服务器&#xff0c;报错证书链是由不受信任的颁发机构颁发的 解决方法&#xff1a;不用ssl方式连接 1、点击弹框中按钮“选项” 2、连接安全加密选择可选 3、不勾选“信任服务器证书” 4、点击“连接”&#xff0c;可连接成功

python安卓自动化pyaibote实践------学习通自动刷课

前言 欢迎来到我的博客 个人主页:北岭敲键盘的荒漠猫-CSDN博客 本文是一个完成一个自动播放课程&#xff0c;避免人为频繁点击脚本的构思与源码。 加油&#xff01;为实现全部电脑自动化办公而奋斗&#xff01; 为实现摆烂躺平的人生而奋斗&#xff01;&#xff01;&#xff…

视觉语言模型详解

视觉语言模型可以同时从图像和文本中学习&#xff0c;因此可用于视觉问答、图像描述等多种任务。本文&#xff0c;我们将带大家一览视觉语言模型领域: 作个概述、了解其工作原理、搞清楚如何找到真命天“模”、如何对其进行推理以及如何使用最新版的 trl 轻松对其进行微调。 什…

【C语言】指针篇-精通库中的快速排序算法:巧妙掌握技巧(4/5)

&#x1f308;个人主页&#xff1a;是店小二呀 &#x1f308;C语言笔记专栏&#xff1a;C语言笔记 &#x1f308;C笔记专栏&#xff1a; C笔记 &#x1f308;喜欢的诗句:无人扶我青云志 我自踏雪至山巅 文章目录 一、回调函数二、快速排序(Qsort)2.1 Qsort参数部分介绍2.2 不…

报错“Install Js dependencies failed”【鸿蒙开发Bug已解决】

文章目录 项目场景:问题描述原因分析:解决方案:此Bug解决方案总结Bug解决方案寄语项目场景: 最近也是遇到了这个问题,看到网上也有人在询问这个问题,本文总结了自己和其他人的解决经验,解决了【报错“Install Js dependencies failed”】的问题。 报错如下 问题描述 …

leetcode 92. 反转链表 II

class Solution(object):def reverseBetween(self, head, left, right):""":type head: ListNode:type left: int:type right: int:rtype: ListNode""" right right -1left left -1while( right-left>0 ):print(right-left)# 左侧节点l …

零基础玩转Linux+Ubuntu实战视频课程

零基础玩转LinuxUbuntu实战视频课程 Linux发行版之间的关系jpg 1-1课程简介及Linux学习路线介绍.mp4 1-10什么是环境变量.mp4 1-11文件系统管理.mp4 1-12用户账户管理.mp4 1-13文件的访问权限.mp4 1-14进程管理.mp4 1-15软件源码包的编译、安装与卸裁.mp4 1-16制作自己的deb软…

WRF原理与基本操作

WRF介绍 WPS是三个&#xff0c;它们协同工作&#xff0c;为真实数据模拟的输入准备输出资料,为真实数据模拟做预处理。 geogrid定义模式范围&#xff0c;将静态地形资料插值到格点 ; ungrib将气象数据从GRIB格式解码 提取气象场; metgrid将ungrib解码的气象场水平地插值到geog…

【C++语法练习】计算梯形的面积

题目链接&#xff1a;https://www.starrycoding.com/problem/158 题目描述 已知一个梯形的上底 a a a&#xff0c;下底 b b b和高 h h h&#xff0c;请求出它的面积&#xff08;结果保留两位小数&#xff09;。 输入格式 第一行一个整数 T T T表示测试用例个数。 ( 1 ≤ T …

Linux 的静态库和动态库

本文目录 一、静态库1. 创建静态库2. 静态库的使用 二、动态库1. 为什么要引入动态库呢&#xff1f;2. 创建动态库3. 动态库的使用4. 查看可执行文件依赖的动态库 一、静态库 在编译程序的链接阶段&#xff0c;会将源码汇编生成的目标文件.o与引用到的库&#xff08;包括静态库…