手势识别检测——基于PP-YOLOE算法模型剪枝及推理
1 环境准备
1.1 工具及环境要求
工具
本地环境要求
- openvino==2022.2.0
- paddle2onnx==1.0.5
- paddlepaddle==2.4.2
- opencv-python==4.2.0.32
- onnx==1.11.0
- tensorflow==2.9.1
1.2 工具介绍
1.2.1 python
可以安装python到本地,也可以直接跳过本步,去anaconda创建python的虚拟环境
1.2.2 anaconda
anaconda仅作为虚拟环境,会创建及进入虚拟环境即可,此处不需要费时间
HINT:建议虚拟环境安装python3.7.9版本,此版本经本人实测无问题,其余版本不敢保证后续依赖项是否出错。
自学参考教程(仅供参考,本人非本人严格审核过文章):
Anaconda介绍、安装及使用保姆级教程 - 知乎 (zhihu.com)
安装conda搭建python环境(保姆级教程)_conda创建python虚拟环境-CSDN博客
Anaconda安装(Python) - 知乎 (zhihu.com)
1.2.3 labelimg
labelimg是一个有图形界面的图像标注工具,用来给数据打标签。
conda activate [虚拟环境名称] #激活anaconda虚拟环境
pip3 install labelimg #安装labelimg
pip3 install labelimg -i https://pypi.tuna.tsinghua.edu.cn/simple
#上一个命令如果速度慢,可以试着开VPN,速度应该会快,无VPN可使用国内清华镜像,即https://pypi.tuna.tsinghua.edu.cn/simple
1.2.4 AI Studio
打个广告,帮我助力一下,我的算力用完了
AI Studio学习与实训社区上线 Tesla A100!为我助力赢10点免费算力,助力成功你可领100点算力卡哦~https://aistudio.baidu.com/aistudio/newbie?invitation=1&sharedUserId=3735541&sharedUserName=CharlyX%20Lee
AI Studio是基于百度深度学习平台飞桨的人工智能学习与实训社区,提供在线编程环境、免费GPU 算力、海量开源算法和开放数据,帮助开发者快速创建和部署模。初次使用的小伙伴记得注册之后完成新手礼包获取算力卡。
完成任务过后,你也可以拥有和很多的GPU使用时间。
1.2.5 YOLO2COCO
因为我们拿到的数据集是yolo格式的,所以需要借助这个工具把YOLO格式的标签数据转成COCO格式数据集。
1.2.6 PaddleUtils
这是一个paddlepaddle模型剪枝工具,我们需要把训练得到的ppyoloe手势检测模型进行裁剪。
1.2.7 paddleyolo
paddleyolo里面有很多目标检测算法,其中包括ppyoloe这个算法,使用的时候只需要配置一些文件就可以训练我们的模型了,非常方便。因为训练模型需要GPU,所以需要在AI Studio里面使用paddleyolo。
1.3 库的安装
pip install paddlepaddle -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install openvino-dev[ONNX,pytorch,tensorflow]==2022.3.1 -i https://pypi.tuna.tsinghua.edu.cn/simple
本人记不清所有的环境配置有哪些,后续运行过程中,如果发现有依赖包缺失及时安装即可。
2 数据准备
2.1 数据介绍
目录结构
----dataset
----000-one
----gesture-one-2021-03-07_23-07-48-2_61613.jpg
----gesture-one-2021-03-07_23-07-48-4_40096.jpg
·
·
·
----013-pink
----gesture-pink-2021-03-07_23-07-55-1_39459.jpg
----gesture-pink-2021-03-07_23-07-55-2_5978.jpg
.
.
.
图片数量有2017张,每类手势的数据集数量不一。图片大致如下所示:
数据来自校赛官方提供的数据集,我们将使用labelimg对数据进行标注,即打标签。
2.2 数据标注
conda activate [环境名称] #打开anaconda虚拟环境
labelimg #启动labelimg
指定要标注的图片文件夹,我们的文件夹是dataset。指定新建一个labels文件夹,被标注的图片生成的.txt标签文件会自动保存到labels文件夹下。
数据格式选用yolo,因为公开数据集是yolo格式,所以统一用yolo。图片中是手势“1”,所以数据标注为000-one。
生成的标签txt文件。除了根据图片名字生成相应的标签txt,还有一个classes.txt。
本次手势识别共有14中标签,classes.txt如下所示:
生成的标签txt文件如下图所示:
0,1,…,13表示标签号,图片中标注了多少个框,相应的生成几排数据。如下图中,假如图片是680x640,0或者1后面的几个小数分别代表框的中心坐标x,y,框的宽w,框的高h。为什么是小数?是因为x/680,y/640,w/680,h/640。
2.3 数据转换
数据集拆分,严格意义上讲,应该把数据拆成训练集、测试集、验证集,我们为了方便,就只需要拆分成训练集和验证集就可以了。在YOLO2COCO\dataset文件下建立一个yolo_mask文件夹,然后将dataset文件夹和label文件夹移动到此文件夹下。点击YOLO2COCO获取工具。
下载到本地,自选文件夹解压即可。(这里我们为了方便,保存在本次项目同目录下)
我们先看看YOLO2COCO给的模板
打开train.txt看一下内容(val.txt里面的内容类似),其实就是从dataset开始的图像的相对路径。
先把我们之前的数据集及打好标签的数据标注移动至gesture文件夹(自行新建文件夹可自由命名)下面,再将数据标注文件夹中产生的classes.txt移动至gesture文件夹,形如这样。
写一个python脚本帮我们生成需要的train.txt,val.txt(我们将原数据集按7:3的比例拆分成训练集和验证集)
import os
def train_val(labels_path, data_path, ratio=0.3):
one_num = 0#计数000-one的数量
five_num = 0#计数001-five的数量
fist_num = 0#计数002-fist的数量
ok_num = 0#计数003-ok的数量
heartSingle_num = 0#计数004-heartSingle的数量
yearh_num = 0#计数005-yearh的数量
three_num = 0#计数006-three的数量
four_num = 0#计数007-four的数量
six_num = 0#计数008-six的数量
Iloveyou_num = 0#计数009-Iloveyou的数量
gun_num = 0#计数010-gun的数量
thumbUp_num = 0#计数011-thumbUp的数量
nine_num = 0#计数012-nine的数量
pink_num = 0#计数013-pink的数量
image_dir = "\\".join(data_path.split("\\")[-2:]) + "\\dateset"#根据yolo2coco要求制定路径
txt_files = os.listdir(labels_path)
f_train = open("train.txt", "w")
f_val = open("val.txt", "w")
n1 = 0
n0 = 0
n2 = 0
n3 = 0
n4 = 0
n5 = 0
n6 = 0
n7 = 0
n8 = 0
n9 = 0
n10 = 0
n11 = 0
n12 = 0
n13 = 0
for txt in txt_files:
f_txt = open(os.path.join(labels_path, txt), 'r' ,encoding='utf-8')#打开txt文件
first_two_chars = f_txt.read(2)
if first_two_chars == "1 ":#读取每个文件的第一行,判断是000-one
one_num += 1#000-one加1
elif first_two_chars == "0 ":#读取每个文件的第一行,判断是001-five
five_num += 1#001-five加1
elif first_two_chars == "2 ":#读取每个文件的第一行,判断是002-fist
fist_num += 1#002-fist加1
elif first_two_chars == "3 ":#读取每个文件的第一行,判断是003-ok
ok_num += 1#003-ok加1
elif first_two_chars == "4 ":#读取每个文件的第一行,判断是004-heartSingle
heartSingle_num += 1#004-heartSingle加1
elif first_two_chars == "5 ":#读取每个文件的第一行,判断是005-yearh
yearh_num += 1#005-yearh加1
elif first_two_chars == "6 ":#读取每个文件的第一行,判断是006-three
three_num += 1#006-three加1
elif first_two_chars == "7 ":#读取每个文件的第一行,判断是007-four
four_num += 1#007-four加1
elif first_two_chars == "8 ":#读取每个文件的第一行,判断是008-six
six_num += 1#008-six加1
elif first_two_chars == "9 ":#读取每个文件的第一行,判断是009-Iloveyou
Iloveyou_num += 1#009-Iloveyou加1
elif first_two_chars == "10":#读取每个文件的第一行,判断是010-gun
gun_num += 1#010-gun加1
elif first_two_chars == "11":#读取每个文件的第一行,判断是011-thumbUp
thumbUp_num += 1#011-thumbUp加1
elif first_two_chars == "12":#读取每个文件的第一行,判断是012-nine
nine_num += 1#012-nine加1
elif first_two_chars == "13":#读取每个文件的第一行,判断是013-pink
pink_num += 1#013-pink加1
f_txt.close()
for txt in txt_files:
f_txt = open(os.path.join(labels_path, txt), 'r')
first_two_chars = f_txt.read(2)
if first_two_chars == "1 ":#读取每个文件的第一行,判断是000-one
n1 += 1
if n1 >= int(one_num * ratio):
f_train.writelines(image_dir+"\\" + "000-one"+"\\"+txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
f_val.writelines(image_dir+"\\" + "000-one"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
elif first_two_chars == "0 ":#读取每个文件的第一行,判断是001-five
n0 += 1
if n0 >= int(five_num * ratio):
f_train.writelines(image_dir+"\\"+ "001-five"+"\\" + txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
f_val.writelines(image_dir+"\\"+ "001-five"+"\\" + txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
elif first_two_chars == "2 ":#读取每个文件的第一行,判断是002-fist
n2 += 1
if n2 >= int(fist_num * ratio):
f_train.writelines(image_dir+"\\" + "002-fist"+"\\" + txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
f_val.writelines(image_dir+"\\" + "002-fist"+"\\" + txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
elif first_two_chars == "3 ":#读取每个文件的第一行,判断是003-ok
n3 += 1
if n3 >= int(ok_num * ratio):
f_train.writelines(image_dir+"\\" + "003-ok"+"\\" + txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
f_val.writelines(image_dir+"\\" + "003-ok"+"\\" + txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
elif first_two_chars == "4 ":#读取每个文件的第一行,判断是004-heartSingle
n4 += 1
if n4 >= int(heartSingle_num * ratio):
f_train.writelines(image_dir+"\\" + "004-heartSingle"+"\\" + txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
f_val.writelines(image_dir+"\\" + "004-heartSingle"+"\\" + txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
elif first_two_chars == "5 ":#读取每个文件的第一行,判断是005-yearh
n5 += 1
if n5 >= int(yearh_num * ratio):
f_train.writelines(image_dir+"\\" + "005-yearh"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
f_val.writelines(image_dir+"\\" + "005-yearh"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
elif first_two_chars == "6 ":#读取每个文件的第一行,判断是006-three
n6 += 1
if n6 >= int(three_num * ratio):
f_train.writelines(image_dir+"\\" + "006-three"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
f_val.writelines(image_dir+"\\" + "006-three"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
elif first_two_chars == "7 ":#读取每个文件的第一行,判断是007-four
n7 += 1
if n7 >= int(four_num * ratio):
f_train.writelines(image_dir+"\\" + "007-four"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
f_val.writelines(image_dir+"\\" + "007-four"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
elif first_two_chars == "8 ":#读取每个文件的第一行,判断是008-six
n8 += 1
if n8 >= int(six_num * ratio):
f_train.writelines(image_dir+"\\" + "008-six"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
f_val.writelines(image_dir+"\\" + "008-six"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
elif first_two_chars == "9 ":#读取每个文件的第一行,判断是009-Iloveyou
n9 += 1
if n9 >= int(Iloveyou_num * ratio):
f_train.writelines(image_dir+"\\" + "009-Iloveyou"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
f_val.writelines(image_dir+"\\" + "009-Iloveyou"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
elif first_two_chars == "10":#读取每个文件的第一行,判断是010-gun
n10 += 1
if n10 >= int(gun_num * ratio):
f_train.writelines(image_dir+"\\" + "010-gun"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
f_val.writelines(image_dir+"\\" + "010-gun"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
elif first_two_chars == "11":#读取每个文件的第一行,判断是011-thumbUp
n11 += 1
if n11 >= int(thumbUp_num * ratio):
f_train.writelines(image_dir+"\\" + "011-thumbUp"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
f_val.writelines(image_dir+"\\" + "011-thumbUp"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
elif first_two_chars == "12":#读取每个文件的第一行,判断是012-nine
n12 += 1
if n12 >= int(nine_num * ratio):
f_train.writelines(image_dir+"\\" + "012-nine"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
f_val.writelines(image_dir+"\\" + "012-nine"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
elif first_two_chars == "13":#读取每个文件的第一行,判断是013-pink
n13 += 1
if n13 >= int(pink_num * ratio):
f_train.writelines(image_dir+"\\" + "013-pink"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
else:
f_val.writelines(image_dir+"\\" + "013-pink"+"\\"+ txt.split(".")[0] + ".jpg" + "\n")#往文件里面写路径,记得换行
f_txt.close()
f_train.close()
f_val.close()
if __name__ == "__main__":
data_path = os.path.join(os.getcwd(), 'E:\大学学习\大三活动\人工智能挑战赛\YOLO2COCO-main\dataset\gesture')#获取文件夹gesture的绝对路径
labels_path = os.path.join(data_path, "labels")#获取labels文件夹的绝对路径
train_val(labels_path=labels_path, data_path=data_path, ratio=0.3)
生成的train.txt,val.txt如下,convert.py是上述源代码文件,放置位置如下图所示。(本人使用的是pycharm进行编写及运行,也完全可以通过记事本写好代码,扩展名转成.py格式,再通过命令行窗口运行)
接下来可以转换数据了。在YOLO2COCO-main文件夹下启动cmd
进入之前创建的conda虚拟环境,根据自己的路径目录情况执行以下命令。(注意:一定要是全英文路径,本人亲测中文路径各种报错)
python yolov5_2_coco.py --data_dir C:\Users\12637\Desktop\YOLO2COCO-main\dataset\gesture
到此数据准备已全部结束,我们将新生成的文件夹压缩,准备上传至AI Studio开始训练模型。
3 模型训练
3.1 创建数据集
因为数据集过大,启动一个项目,然后直接上传是不行的,所以先创建一个数据集,再在数据集基础上创建项目。
3.2 创建项目
点进创建好的数据集,选择创建项目
我们先选择基础版,将文件配置好,节省算力卡时间(个人建议:算力卡只有训练模型的时候再用)
你创建好的数据集存放位置在data目录下,该环境内命令类似于Linux(没有相关经验也毫无影响,想用到啥直接上网搜Linux相关操作命令即可)
git clone paddleyolo github 链接
注意:在ppyoloe.ipynb文件里面写的代码或者markdown都要ctrl+s保存一下,养成一个优良的习惯。
3.3 模型配置
在configs下面有很多模型,文件夹名就是模型名字,除了可以使用ppyoloe,还有yolov5,yolov8等。
本人本打算使用经典的YOLO_V5算法,但PP-YOLOE表现实在过于惊艳,如上图所示,故本次模型将基于PP-YOLOE
选择一个模型文件夹,修改文件夹里面的配置文件,我们选择的是small版本的ppyoloe,需要训练80 epochs。
我们需要把num_classes修改成14,因为我们的数据集只有14个标签。改完记得ctrl+s保存。
因为配置文件中要求数据放到dataset/mask里面,所以需要把数据集放置到此处。
新建一个gesture文件夹,把解压过后的数据文件夹拖到gesture里面,并移动到PaddleYOLO/dataset目录下。
3.4 模型训练
因为我们是基础版本环境,没有GPU,所以需要切换环境。如果你像下面这样切换环境,大概率你是切换不了的,因为文件过多过大。而且可能会导致死机等一系列不必要现象发生。
切到这个页面停止。
重启环境,但是这次要选择GPU环境,根据自己的算力卡富余量决定选哪个选项,土豪的话,选择付费资源中 四个GPU直接起飞。个人建议,该项目V100 32GB完全足够使用
在ppyoloe模型文件夹里面有个readme_cn.md,里面有训练模型的命令。将改命令复制到ppyoloe.ipynb文件里面执行。
命令是多卡训练,我们需要稍作修改,换成单卡训练,因为你启动工程的时候就选了一个卡。
!python tools/train.py -c configs/ppyoloe/ppyoloe_plus_crn_s_80e_coco.yml
模型训练标志,此时是0 epoch,eta时间估计大概要38分22秒训练完成。时间会变,但是大概就40分钟左右。
(注意:本人此次训练模型时仅做以测试,只训练了前4个标签,实际14个标签训练时长一定会比这个长)
找到自己的模型放置位置,在生成的output文件夹下。如下图操作可以获取模型model_final.pdparams的绝对路径,然后粘贴至weights=后面,导出模型。
!python tools/export_model.py -c configs/ppyoloe/ppyoloe_plus_crn_s_80e_coco.yml -o weights=模型参数绝对路径
导出模型会放置output_inference文件夹下。下载导出的模型,用于下一步模型转换当中。
4 模型转换及剪枝
我们要把paddlepaddle模型转成OpenVINO能使用的模型文件
4.1 可视化模型
把之前下载的四个文件打包进ppyoloe_crn_s_80里面。
进入模型可视化网站,选择我们上一课程训练好的模型。
可以看到模型输入是3x640x640,3是图像的RGB通道,也就是说我们传入的图像是有色图。
定位到模型的输出,在可视化图像的最下方,我们要找到需要模型减支的地方。
4.2 模型剪枝
模型减支的目的就是从输入到两个输出concat_14.tmp_0,tmp_16为止,后面的节点都删掉。
在模型减支工具的paddle目录下打开cmd,进入虚拟环境。
我们将运行这个模型减支py文件。
python prune_paddle_model.py --model_dir ppyoloe_crn_s_80 --model_filename model.pdmodel --params_filename model.pdiparams --output_names tmp_16 concat_14.tmp_0 --save_dir export_model
运行成功后会新增一个剪枝完成的模型文件夹export_model。
再次打开可视化工具查看模型的情况。可以看到我们保留的两个结点作为输出,部分结点被裁掉了。
4.3 模型转换
先把paddle模型转换为onnx,需要在环境里面提前安装paddle2onnx。执行以下命令。
paddle2onnx --model_dir export_model --model_filename model.pdmodel --params_filename model.pdiparams --input_shape_dict "{'image':[1,3,640,640]}" --opset_version 11 --save_file ppyoloe_crn_s_80.onnx
执行生成的ppyoloe_crn_s_80.onnx,将onnx转xml,bin(OpenVINO)。
mo --input_model ppyoloe_crn_s_80.onnx
生成三个文件(.bin .mapping .xml)下一步模型推理的时候要用到的模型2文件
5 模型推理
5.1 模型推理准备
我们新建一个文件夹,将之前的三个文件放进其中,并新建一个labels.txt用于区分14种标签(可以将之前的class.txt重命名),再新建一个python文件,开始模型推理代码编写。
5.2 推理代码编写
from openvino.runtime import Core
import openvino.runtime as ov
import cv2 as cv
import numpy as np
import tensorflow as tf
OpenVINO 模型推理器(class)
class Predictor:
"""
OpenVINO 模型推理器
"""
def __init__(self, model_path):
ie_core = Core()
model = ie_core.read_model(model=model_path)
self.compiled_model = ie_core.compile_model(model=model, device_name="CPU")
def get_inputs_name(self, num):
return self.compiled_model.input(num)
def get_outputs_name(self, num):
return self.compiled_model.output(num)
def predict(self, input_data):
return self.compiled_model([input_data])
def get_request(self):
return self.compiled_model.create_infer_request()
图像预处理
def process_image(input_image, size):
"""输入图片与处理方法,按照PP-Yoloe模型要求预处理图片数据
Args:
input_image (uint8): 输入图片矩阵
size (int): 模型输入大小
Returns:
float32: 返回处理后的图片矩阵数据
"""
max_len = max(input_image.shape)
img = np.zeros([max_len,max_len,3],np.uint8)
img[0:input_image.shape[0],0:input_image.shape[1]] = input_image # 将图片放到正方形背景中
img = cv.cvtColor(img,cv.COLOR_BGR2RGB) # BGR转RGB
img = cv.resize(img, (size, size), cv.INTER_NEAREST) # 缩放图片
img = np.transpose(img,[2, 0, 1]) # 转换格式
img = img / 255.0 # 归一化
img = np.expand_dims(img,0) # 增加维度
return img.astype(np.float32)
图像后处理
def process_result(box_results, conf_results):
"""按照PP-Yolove模型输出要求,处理数据,非极大值抑制,提取预测结果
Args:
box_results (float32): 预测框预测结果
conf_results (float32): 置信度预测结果
Returns:
float: 预测框
float: 分数
int: 类别
"""
conf_results = np.transpose(conf_results,[0, 2, 1]) # 转置
# 设置输出形状
box_results =box_results.reshape(8400,4)
conf_results = conf_results.reshape(8400,4)
scores = []
classes = []
boxes = []
for i in range(8400):
conf = conf_results[i,:] # 预测分数
score = np.max(conf) # 获取类别
# 筛选较小的预测类别
if score > 0.5:
classes.append(np.argmax(conf))
scores.append(score)
boxes.append(box_results[i,:])
scores = np.array(scores)
boxes = np.array(boxes)
result_box = []
result_score = []
result_class = []
# 非极大值抑制筛选重复的预测结果
if len(boxes) != 0:
# 非极大值抑制结果
indexs = tf.image.non_max_suppression(boxes,scores,len(scores),0.25,0.35)
for i, index in enumerate(indexs):
result_score.append(scores[index])
result_box.append(boxes[index,:])
result_class.append(classes[index])
# 返回结果
return np.array(result_box),np.array(result_score),np.array(result_class)
画出预测框
def draw_box(image, boxes, scores, classes, labels):
"""将预测结果绘制到图像上
Args:
image (uint8): 原图片
boxes (float32): 预测框
scores (float32): 分数
classes (int): 类别
lables (str): 标签
Returns:
uint8: 标注好的图片
"""
colors = [(0, 0, 255), (0, 255, 0), (0, 0, 0), (255, 0, 0)]
scale = max(image.shape) / 640.0 # 缩放比例
if len(classes) != 0:
for i in range(len(classes)):
box = boxes[i,:]
x1 = int(box[0] * scale)
y1 = int(box[1] * scale)
x2 = int(box[2] * scale)
y2 = int(box[3] * scale)
label = labels[classes[i]]
score = scores[i]
cv.rectangle(image, (x1, y1), (x2, y2), colors[classes[i]], 2, cv.LINE_8)
cv.putText(image,label+":"+str(score),(x1,y1-10),cv.FONT_HERSHEY_SIMPLEX, 0.55, colors[classes[i]], 2)
return image
读取标签
def read_label(label_path):
with open(label_path, 'r') as f:
labels = f.read().split()
return labels
同步推理
程序等待模型推理完成后,再去获取下一张图片,进行相应的处理,然后交给模型进行推理。
label_path = "labels.txt"
yoloe_model_path = "ppyoloe_crn_s_80.xml"
predictor = Predictor(model_path = yoloe_model_path)
boxes_name = predictor.get_outputs_name(0)
conf_name = predictor.get_outputs_name(1)
labels = read_label(label_path=label_path)
cap = cv.VideoCapture(0)
while cap.isOpened():
ret, frame = cap.read()
frame = cv.flip(frame, 180)
cv.namedWindow("GestureDetection", 0) # 0可调大小,注意:窗口名必须imshow里面的一窗口名一直
cv.resizeWindow("GestureDetection", 640, 480) # 设置长和宽
input_frame = process_image(frame, 640)
results = predictor.predict(input_data=input_frame)
boxes, scores, classes = process_result(box_results=results[boxes_name], conf_results=results[conf_name])
result_frame = draw_box(image=frame, boxes=boxes, scores=scores, classes=classes, labels=labels)
cv.imshow('GestureDetection', result_frame)
key = cv.waitKey(1)
if key == 27: #esc退出
break
cap.release()
cv.destroyAllWindows()
异步推理
把一张图片交给模型推理的同时,程序立即获取下一张图片,进行相应的预处理,然后等待上一次推理结束,再把该图片交给模型推理。
label_path = "labels.txt"
yoloe_model_path = "ppyoloe_crn_s_80.xml"
predictor = Predictor(model_path = yoloe_model_path)
input_layer = predictor.get_inputs_name(0)
labels = read_label(label_path=label_path)
cap = cv.VideoCapture(0)
curr_request = predictor.get_request()
next_request = predictor.get_request()
ret, frame = cap.read()
curr_frame = process_image(frame, 640)
curr_request.set_tensor(input_layer, ov.Tensor(curr_frame))
curr_request.start_async()
while cap.isOpened():
ret, next_frame = cap.read()
next_frame = cv.flip(next_frame, 180)
cv.namedWindow("GestureDetection", 0) # 0可调大小,注意:窗口名必须imshow里面的一窗口名一直
cv.resizeWindow("GestureDetection", 640, 480) # 设置长和宽
in_frame = process_image(next_frame, 640)
next_request.set_tensor(input_layer, ov.Tensor(in_frame))
next_request.start_async()
if curr_request.wait_for(-1) == 1:
boxes_name = curr_request.get_output_tensor(0).data
conf_name = curr_request.get_output_tensor(1).data
boxes, scores, classes = process_result(box_results=boxes_name, conf_results=conf_name)
frame = draw_box(image=frame, boxes=boxes, scores=scores, classes=classes, labels=labels)
cv.imshow('GestureDetection', frame)
frame = next_frame
curr_request, next_request = next_request, curr_request
key = cv.waitKey(1)
if key == 27: #esc退出
break
cap.release()
cv.destroyAllWindows()
我们在本程序中决定使用异步推理
5.3 总结
推理结果显示,用Intel CPU很丝滑,在OpenVINO 模型推理器类中,device_name除了可以使用Intel的CPU、GPU、VPU还有FPGA。至此,手势识别实时检测到此结束。
到此本项目基本做完,后续本人有可能还会有新的突发奇想,本人有时间将继续更新~