开发者实战 | 自训练 YOLOv5 模型使用 OpenVINO™ 优化并部署在英特尔开发套件

09/18 17:00
阅读数 39

点击蓝字

关注我们,让开发变得更有趣

作者:深圳技术大学 黎逸鹏(电子科学与技术2021级)

指导老师:张阳(英特尔边缘计算创新大使,深圳技术大学, 副教授)


1.1


简介


本文章将在《自训练Pytorch模型使用 OpenVINO™ 优化并部署在英特尔开发套件》文章的基础上进行扩展,将介绍如何使用 OpenVINO Python API 对 YOLOv5 模型进行优化以及部署,完成 YOLOv5 目标检测任务。


本文 Python 程序的开发环境是 Ubuntu20.04 LTS + PyCharm,硬件平台是英特尔开发套件爱克斯开发板 AIxBoard。


本文项目背景:针对 2023 第十一届全国大学生光电设计竞赛赛题 2 “迷宫寻宝”光电智能小车题目。基于该赛项宝藏样式,我通过深度学习训练出能分类四种不同颜色不同标记形状骨牌的模型,骨牌样式详见图 1.1。


图1.1  四种骨牌类型



1.2


YOLOv5 以及目标检测


YOLO (You Only Look Once) 是目标检测模型,目标检测是计算机视觉中一种重要的任务,目的是在一张图片中找出特定的物体,同时要求识别物体的种类和位置。在此之前的文章中 Pytorch 模型是用于图像分类,只要求识别画面中物体的种类。具体的区别通过图 1.2.1 直观可知。


图 1.2.1  图像分类、目标定位、目标检测


01


通过 labelImg 对图像进行数据集构建


Labelimg 是一款数据标注软件,支持输出包括 yolo, PascalVOC 等格式标注数据,这里我们选择 yolo 格式即可。


在环境中执行:

pip install labelimg -i https://mirror.baidu.com/pypi/simple

向右滑动查看完整代码


而后打开 labelimg 软件,如图 1.2.2


图 1.2.2  labelImg 软件界面


如图所示,选择好图片数据目录 (Open Dir) 和数据标注保存目录 (Choose Save Dir),就可以对想要的物体进行人工的数据标注。


02


标注好后检查保存目录中的 label 文件,查看是否无误,如图 1.2.3


图 1.2.3  标注好的文件


03


本次实验共标注 2000 张图片(单分类 500 张共 4 分类)具体训练流程在 YOLOv5 Github 有较为详细的教程,在此不作为重点讲解。得到 YOLOv5 模型后通过 OpenVINO™ Model Optimization 转化成 IR 模型,这样无论从处理速度还是精度都能获得一定程度的优化。



1.3


使用 OpenVINO™ Runtime 对 YOLOv5 模型进行推理


在这一章节里我们将在 Pycharm 中使用 OpenVINO™ Runtime 对我们训练的 YOLOv5 模型进行优化推理。


整个推理流程大致可以分为:


推理核心初始化 → 对输入图进行预处理 → 输入到推理引擎获得结果 → 通过置信度/NMS(非极大值抑制)过滤得到结果 → 将结果通过 OpenCV API 进行可视化


1.3.1

导入功能包


import openvino.runtime as ovimport cv2import numpy as npimport openvino.preprocess as op


本次我们导入四个功能包,分别是 OpenVINO™ Runtime & PreProcess 、Numpy 、OpenCV。与之前不同在于我们需要使用 OpenVINO™ 自带的预处理 API 对我们的模型进行一个预先处理,使得模型能够正常工作在 OpenVINO™ 的推理引擎下。


PreProcess API 介绍:


OpenVINO™ PreProcess 是 OpenVINO™ Python API 大家庭的一员,主要是提供了一个 OpenVINO™ Runtime 原生用于数据预处理的 API 函数库,在不用 PreProcess 时,开发者需要用第三方库例如 OpenCV 来对其进行预处理,但是 OpenCV 作为一个开源的、广泛的功能库,数据预处理只能加载到 CPU 去进行实现,这无疑是增加对 CPU 资源的开销,并且之后需要将处理后数据再次返还到 iGPU 等计算设备进行推理。而 PreProcess 提供了一种方式,使得预处理也能直接集成到模型执行图中去,整个模型工作流程都在 iGPU 上流转,这样无需依赖 CPU,能提高执行效率。


由于输入数据的不同,我们需要预处理来将数据能够正确的进行处理。例如改变精度、改变输入颜色通道、输入数据的 Layout 等等。


整体 PreProcess 的流程大概是:


创建 PPP(PrePostProcess) 对象 → 声明输入数据信息 → 指定 Layout →设置输出张量信息 → 从 PPP 对象中构建 Model 并进行推理


可以明显得知,PreProcess 的存在使得预处理变得非常简单易懂,只需要在在转换前查看模型的输入输出信息,再比对自己环境下的输入数据,进行预处理改变即可。而且整个环境都可以在 iGPU 等计算设备上运行,减轻了 CPU 负担,可以把更多宝贵的资源留在处理其他重要事情上。


1.3.2

模型载入


将模型进行载入:

def Init():    global core    global model    global compiled_modelglobal infer_request#核心创建core = ov.Core() #读取用YOLOv5模型转换而来的IR模型model = core.read_model("best2.xml", "best2.bin") #运用PPP(PrePostProcessor)对模型进行预处理Premodel = op.PrePostProcessor(model)Premodel.input().tensor().set_element_type(ov.Type.u8).set_layout(ov.Layout("NHWC")).set_color_format(op.ColorFormat.BGR)Premodel.input().preprocess().convert_element_type(ov.Type.f32).convert_color(op.ColorFormat.RGB).scale(        [255., 255., 255.])    Premodel.input().model().set_layout(ov.Layout("NCHW"))    Premodel.output(0).tensor().set_element_type(ov.Type.f32)    model = Premodel.build()    compiled_model = core.compile_model(model, "CPU") #加载模型,可用CPU or GPU    infer_request = compiled_model.create_infer_request() #生成推理

向右滑动查看完整代码


1.3.3

图像尺寸调整


由于输入图的尺寸不确定性,在此我们特意加入一个 Resize 环节,用来适应不同分辨率的图像,但是若输入图像尺寸较为稳定,只需要求出其变换图的长宽比例即可。

def resizeimg(image, new_shape):old_size = image.shape[:2]#记录新形状和原生图像矩形形状的比率    ratio = float(new_shape[-1] / max(old_size))     new_size = tuple([int(x * ratio) for x in old_size])    image = cv2.resize(image, (new_size[1], new_size[0]))    delta_w = new_shape[1] - new_size[1]    delta_h = new_shape[0] - new_size[0]color = [100, 100, 100]new_im = cv2.copyMakeBorder(image, 0, delta_h, 0, delta_w, cv2.BORDER_CONSTANT, value=color)    #增广操作    return new_im, delta_w, delta_h

向右滑动查看完整代码


1.3.4

推理过程以及结果展示


在上一节中我们把输入图像所要进行的预处理图像进行了一个定义,在这一小节则是 OpenVINO™ Runtime 推理程序的核心。

#************************************##               推理主程序             #def main(img,infer_request):    push =[]    img_re,dw,dh = resizeimg(img,(640,640)) #尺寸处理    input_tensor = np.expand_dims(img_re, 0) #获得输入张量    infer_request.infer({0: input_tensor}) #输入到推理引擎    output = infer_request.get_output_tensor(0) #获得推理结果    detections = output.data[0] #获得检测数据    boxes = []    class_ids = []    confidences = []    for prediction in detections:        confidence = prediction[4].item() #获取置信度        if confidence >= 0.6: #初步过滤,过滤掉绝大多数的无效数据            classes_scores = prediction[5:]            _, _, _, max_indx = cv2.minMaxLoc(classes_scores)            class_id = max_indx[1]            if (classes_scores[class_id] > .25):                confidences.append(confidence)                class_ids.append(class_id)                x, y, w, h = prediction[0].item(), prediction[1].item(), prediction[2].item(), prediction[3].item() #获取有效信息                xmin = x - (w / 2) #由于NMSBoxes缘故,需要从中心点得到左上角点                ymin = y - (h / 2)                box = np.array([xmin, ymin, w, h]) #记录数据                boxes.append(box)    indexes = cv2.dnn.NMSBoxes(boxes, confidences, 0.5, 0.5) #NMS筛选    detections = []    for i in indexes:        j = i.item()        detections.append({"class_index": class_ids[j], "confidence": confidences[j], "box": boxes[j]}) #储存获取的目标名称和框选位    for detection in detections:        box = detection["box"]        classId = detection["class_index"]        confidence = detection["confidence"]if(confidence<0.88): #再次过滤            continue        else :            push.append(classId)        rx = img.shape[1] / (img_re.shape[1] - dw)        ry = img.shape[0] / (img_re.shape[0] - dh)        img_re = cv2.rectangle(img_re, (int(box[0]), int(box[1])), (int(box[0] + box[2]), int(box[1] + box[3])), (0, 255, 0), 3)        box[0] = rx * box[0] #恢复原尺寸box,如果尺寸不变可以忽略        box[1] = box[1] *ry        box[2] = rx * box[2]        box[3] = box[3] *ry        xmax = box[0] + box[2]        ymax = box[1] + box[3]        img = cv2.rectangle(img, (int(box[0]), int(box[1])), (int(xmax), int(ymax)), (0, 255, 0), 3) #绘制物体框        img = cv2.rectangle(img, (int(box[0]), int(box[1]) - 20), (int(xmax), int(box[1])), (0, 255, 0), cv2.FILLED) #绘制目标名称底色填充矩形        img = cv2.putText(img, str(label[classId])+'  '+str(int(confidence*100))+'%', (int(box[0]), int(box[1]) - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0))  #绘制目标名称    cv2.imshow("d", img_re)    cv2.imshow('w',img)    cv2.waitKey(0)

向右滑动查看完整代码


以上推理函数编写已经完成。以下是运行主程序:

#********************主程序***********************#def MainToSolve(infer):    img = cv2.imread("boundtest.jpg")  #如果需要实时,只需要将输入img变成从摄像机抓取的帧画面main(img,infer)
#从这里开始,初始化以及推理Init()MainToSolve(infer_request)

向右滑动查看完整代码


当我们运行该程序时,会得到如图 1.3.1。


图 1.3.1  图像结果


如图所示,YOLOv5 模型通过转换成IR模型,再经过 PPP 预处理以及 Runtime 引擎,成功运行在 AlxBoard。整体上性能非常不错。


1.4


模型应用场景简述:


原先的 Pytorch 模型只完成了图像分类的任务,本文通过 YOLOv5 训练并运用 OpenVINO™ 技术来完成了目标检测这一更高难度的任务,通过得到物块的位置我们就能更好的给予小车底盘信息,用来精确对物块进行任务(抓取或是推到)


搭载 AlxBoard 的四轮小车如图 1.4.1。


图 1.4.1  AlxBoard 智能小车


通过此小车,我们还能发挥想象去做更多的应用场景,通过 OpenVINO™ 赋能小车系统,我们还能实现例如空对地无图导航等等更具有特色的应用场景。



1.5


结论


自训练 YOLOv5 模型在通过 OpenVINO™ Model Optimizer  模型优化后用 OpenVINO™ PreProcess 先进行预处理,处理后用 OpenVINO™ Runtime 进行推理,推理过程简单清晰。推理整个过程由于加入了 PPP(PrePostProcess) 的预处理技术,整个处理可以放在 iGPU 上运行,有效减少 CPU 的开销。通过 OpenVINO™ 技术优化后的模型优势明显,加上 AlxBoard 开发者板,能让我们迅速构建起智能小车来验证系统。


OpenVINO™ 简单易上手,提供了健全的文档和 OpenVINO™ Notebooks 范例,帮助开发者专注在自身应用的实现和算法搭建。


OpenVINO™

--END--


               
               
               
你也许想了解(点击蓝字查看)⬇️
➡️ 基于 ChatGLM2 和 OpenVINO™ 打造中文聊天助手
➡️ 基于 Llama2 和 OpenVINO™ 打造聊天机器人
➡️ OpenVINO™ DevCon 2023重磅回归!英特尔以创新产品激发开发者无限潜能
➡️ 5周年更新 | OpenVINO™  2023.0,让AI部署和加速更容易
➡️ OpenVINO™5周年重头戏!2023.0版本持续升级AI部署和加速性能
➡️ OpenVINO™2023.0实战 | 在 LabVIEW 中部署 YOLOv8 目标检测模型
➡️ 开发者实战系列资源包来啦!
➡️  以AI作画,祝她节日快乐;简单三步,OpenVINO™ 助你轻松体验AIGC
➡️  还不知道如何用OpenVINO™作画?点击了解教程。
➡️   几行代码轻松实现对于PaddleOCR的实时推理,快来get!
➡️   使用OpenVINO 在“端—边—云”快速实现高性能人工智能推理


              
              
              

扫描下方二维码立即体验 

OpenVINO™ 工具套件 2023.0


点击 阅读原文 立即体验OpenVINO 2023.0
文章这么精彩,你有没有“在看”?

本文分享自微信公众号 - OpenVINO 中文社区(openvinodev)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部