1.1
簡介
本文章將在《自訓(xùn)練Pytorch模型使用 OpenVINO 優(yōu)化并部署在英特爾開發(fā)套件》文章的基礎(chǔ)上進(jìn)行擴展,將介紹如何使用 OpenVINO Python API 對 YOLOv5 模型進(jìn)行優(yōu)化以及部署,完成 YOLOv5 目標(biāo)檢測任務(wù)。
本文 Python 程序的開發(fā)環(huán)境是 Ubuntu20.04 LTS + PyCharm,硬件平臺是英特爾開發(fā)套件愛克斯開發(fā)板 AIxBoard。
本文項目背景:針對 2023 第十一屆全國大學(xué)生光電設(shè)計競賽賽題 2 “迷宮尋寶”光電智能小車題目?;谠撡愴棇毑貥邮?,我通過深度學(xué)習(xí)訓(xùn)練出能分類四種不同顏色不同標(biāo)記形狀骨牌的模型,骨牌樣式詳見圖 1.1。
圖1.1 四種骨牌類型
1.2
YOLOv5 以及目標(biāo)檢測
YOLO (You Only Look Once) 是目標(biāo)檢測模型,目標(biāo)檢測是計算機視覺中一種重要的任務(wù),目的是在一張圖片中找出特定的物體,同時要求識別物體的種類和位置。在此之前的文章中 Pytorch 模型是用于圖像分類,只要求識別畫面中物體的種類。具體的區(qū)別通過圖 1.2.1 直觀可知。
圖 1.2.1 圖像分類、目標(biāo)定位、目標(biāo)檢測
01
通過 labelImg 對圖像進(jìn)行數(shù)據(jù)集構(gòu)建
Labelimg 是一款數(shù)據(jù)標(biāo)注軟件,支持輸出包括 yolo, PascalVOC 等格式標(biāo)注數(shù)據(jù),這里我們選擇 yolo 格式即可。
在環(huán)境中執(zhí)行:
pip install labelimg -i https://mirror.baidu.com/pypi/simple
而后打開 labelimg 軟件,如圖 1.2.2
圖 1.2.2 labelImg 軟件界面
如圖所示,選擇好圖片數(shù)據(jù)目錄 (Open Dir) 和數(shù)據(jù)標(biāo)注保存目錄 (Choose Save Dir),就可以對想要的物體進(jìn)行人工的數(shù)據(jù)標(biāo)注。
02
標(biāo)注好后檢查保存目錄中的 label 文件,查看是否無誤,如圖 1.2.3
圖 1.2.3 標(biāo)注好的文件
03
本次實驗共標(biāo)注 2000 張圖片(單分類 500 張共 4 分類)具體訓(xùn)練流程在 YOLOv5 Github 有較為詳細(xì)的教程,在此不作為重點講解。得到 YOLOv5 模型后通過 OpenVINO Model Optimization 轉(zhuǎn)化成 IR 模型,這樣無論從處理速度還是精度都能獲得一定程度的優(yōu)化。
1.3
使用 OpenVINO Runtime 對 YOLOv5 模型進(jìn)行推理
在這一章節(jié)里我們將在 Pycharm 中使用 OpenVINO Runtime 對我們訓(xùn)練的 YOLOv5 模型進(jìn)行優(yōu)化推理。
整個推理流程大致可以分為:
推理核心初始化 → 對輸入圖進(jìn)行預(yù)處理 → 輸入到推理引擎獲得結(jié)果 → 通過置信度/NMS(非極大值抑制)過濾得到結(jié)果 → 將結(jié)果通過 OpenCV API 進(jìn)行可視化
1.3.1
導(dǎo)入功能包
import openvino.runtime as ov import cv2 import numpy as np import openvino.preprocess as op
本次我們導(dǎo)入四個功能包,分別是 OpenVINO Runtime & PreProcess 、Numpy 、OpenCV。與之前不同在于我們需要使用 OpenVINO 自帶的預(yù)處理 API 對我們的模型進(jìn)行一個預(yù)先處理,使得模型能夠正常工作在 OpenVINO 的推理引擎下。
PreProcess API 介紹:
OpenVINO PreProcess 是 OpenVINO Python API 大家庭的一員,主要是提供了一個 OpenVINO Runtime 原生用于數(shù)據(jù)預(yù)處理的 API 函數(shù)庫,在不用 PreProcess 時,開發(fā)者需要用第三方庫例如 OpenCV 來對其進(jìn)行預(yù)處理,但是 OpenCV 作為一個開源的、廣泛的功能庫,數(shù)據(jù)預(yù)處理只能加載到 CPU 去進(jìn)行實現(xiàn),這無疑是增加對 CPU 資源的開銷,并且之后需要將處理后數(shù)據(jù)再次返還到 iGPU 等計算設(shè)備進(jìn)行推理。而 PreProcess 提供了一種方式,使得預(yù)處理也能直接集成到模型執(zhí)行圖中去,整個模型工作流程都在 iGPU 上流轉(zhuǎn),這樣無需依賴 CPU,能提高執(zhí)行效率。
由于輸入數(shù)據(jù)的不同,我們需要預(yù)處理來將數(shù)據(jù)能夠正確的進(jìn)行處理。例如改變精度、改變輸入顏色通道、輸入數(shù)據(jù)的 Layout 等等。
整體 PreProcess 的流程大概是:
創(chuàng)建 PPP(PrePostProcess) 對象 → 聲明輸入數(shù)據(jù)信息 → 指定 Layout →設(shè)置輸出張量信息 → 從 PPP 對象中構(gòu)建 Model 并進(jìn)行推理
可以明顯得知,PreProcess 的存在使得預(yù)處理變得非常簡單易懂,只需要在在轉(zhuǎn)換前查看模型的輸入輸出信息,再比對自己環(huán)境下的輸入數(shù)據(jù),進(jìn)行預(yù)處理改變即可。而且整個環(huán)境都可以在 iGPU 等計算設(shè)備上運行,減輕了 CPU 負(fù)擔(dān),可以把更多寶貴的資源留在處理其他重要事情上。
1.3.2
模型載入
將模型進(jìn)行載入:
def Init(): global core global model global compiled_model global infer_request #核心創(chuàng)建 core = ov.Core() #讀取用YOLOv5模型轉(zhuǎn)換而來的IR模型 model = core.read_model("best2.xml", "best2.bin") #運用PPP(PrePostProcessor)對模型進(jìn)行預(yù)處理 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
圖像尺寸調(diào)整
由于輸入圖的尺寸不確定性,在此我們特意加入一個 Resize 環(huán)節(jié),用來適應(yīng)不同分辨率的圖像,但是若輸入圖像尺寸較為穩(wěn)定,只需要求出其變換圖的長寬比例即可。
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
推理過程以及結(jié)果展示
在上一節(jié)中我們把輸入圖像所要進(jìn)行的預(yù)處理圖像進(jìn)行了一個定義,在這一小節(jié)則是 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) #獲得推理結(jié)果 detections = output.data[0] #獲得檢測數(shù)據(jù) boxes = [] class_ids = [] confidences = [] for prediction in detections: confidence = prediction[4].item() #獲取置信度 if confidence >= 0.6: #初步過濾,過濾掉絕大多數(shù)的無效數(shù)據(jù) 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]) #記錄數(shù)據(jù) 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]}) #儲存獲取的目標(biāo)名稱和框選位 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] #恢復(fù)原尺寸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) #繪制目標(biāo)名稱底色填充矩形 ? ? ? ?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)) ?#繪制目標(biāo)名稱 ? ?cv2.imshow("d", img_re) ? ?cv2.imshow('w',img) ? ?cv2.waitKey(0)
以上推理函數(shù)編寫已經(jīng)完成。以下是運行主程序:
#********************主程序***********************# def MainToSolve(infer): img = cv2.imread("boundtest.jpg") #如果需要實時,只需要將輸入img變成從攝像機抓取的幀畫面 main(img,infer) #從這里開始,初始化以及推理 Init() MainToSolve(infer_request)
當(dāng)我們運行該程序時,會得到如圖 1.3.1。
圖 1.3.1 圖像結(jié)果
如圖所示,YOLOv5 模型通過轉(zhuǎn)換成IR模型,再經(jīng)過 PPP 預(yù)處理以及 Runtime 引擎,成功運行在 AlxBoard。整體上性能非常不錯。
1.4
模型應(yīng)用場景簡述:
原先的 Pytorch 模型只完成了圖像分類的任務(wù),本文通過 YOLOv5 訓(xùn)練并運用 OpenVINO 技術(shù)來完成了目標(biāo)檢測這一更高難度的任務(wù),通過得到物塊的位置我們就能更好的給予小車底盤信息,用來精確對物塊進(jìn)行任務(wù)(抓取或是推到)
搭載 AlxBoard 的四輪小車如圖 1.4.1。
圖 1.4.1 AlxBoard 智能小車
通過此小車,我們還能發(fā)揮想象去做更多的應(yīng)用場景,通過 OpenVINO 賦能小車系統(tǒng),我們還能實現(xiàn)例如空對地?zé)o圖導(dǎo)航等等更具有特色的應(yīng)用場景。
1.5
結(jié)論
自訓(xùn)練 YOLOv5 模型在通過 OpenVINO Model Optimizer 模型優(yōu)化后用 OpenVINO PreProcess 先進(jìn)行預(yù)處理,處理后用 OpenVINO Runtime 進(jìn)行推理,推理過程簡單清晰。推理整個過程由于加入了 PPP(PrePostProcess) 的預(yù)處理技術(shù),整個處理可以放在 iGPU 上運行,有效減少 CPU 的開銷。通過 OpenVINO 技術(shù)優(yōu)化后的模型優(yōu)勢明顯,加上 AlxBoard 開發(fā)者板,能讓我們迅速構(gòu)建起智能小車來驗證系統(tǒng)。
OpenVINO 簡單易上手,提供了健全的文檔和 OpenVINO Notebooks 范例,幫助開發(fā)者專注在自身應(yīng)用的實現(xiàn)和算法搭建。
-
英特爾
+關(guān)注
關(guān)注
60文章
9862瀏覽量
171297 -
模型
+關(guān)注
關(guān)注
1文章
3116瀏覽量
48660 -
開發(fā)套件
+關(guān)注
關(guān)注
2文章
150瀏覽量
24250 -
深度學(xué)習(xí)
+關(guān)注
關(guān)注
73文章
5466瀏覽量
120891
原文標(biāo)題:自訓(xùn)練 YOLOv5 模型使用 OpenVINO? 優(yōu)化并部署在英特爾開發(fā)套件 | 開發(fā)者實戰(zhàn)
文章出處:【微信號:英特爾物聯(lián)網(wǎng),微信公眾號:英特爾物聯(lián)網(wǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論