【2023 Intel有奖征文】自训练YOLOv5模型使用OpenVINO™优化并部署在AIxBoard™

SZTU_Liiii 更新于 7月前

指导教授:深圳技术大学 张阳(英特尔全球创新大使)

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

1.1 简介

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

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

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


图1.1  四种骨牌类型

1.1 YOLOv5以及目标检测

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


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

1. 通过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),就可以对想要的物体进行人工的数据标注。

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

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

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

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

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

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

1.3.1 导入功能包

import openvino.runtime as ov
import cv2
import numpy as np
import openvino.preprocess as op

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

1. 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_model
global 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.NMSBoxe***oxes, 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)

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

                                     

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

1.4 模型应用场景简述:

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

        搭载AlxBoard的四轮小车如图。

                                             

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

1.5 结论:

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

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

0个评论