1. 項(xiàng)目介紹
1.1 PP-YOLOE模型
?目標(biāo)檢測(cè)作為計(jì)算機(jī)視覺領(lǐng)域的頂梁柱,不僅可以獨(dú)立完成車輛、商品、缺陷檢測(cè)等任務(wù),也是人臉識(shí)別、視頻分析、以圖搜圖等復(fù)合技術(shù)的核心模塊,在自動(dòng)駕駛、工業(yè)視覺、安防交通等領(lǐng)域的商業(yè)價(jià)值有目共睹。
?PaddleDetection為基于飛槳PaddlePaddle的端到端目標(biāo)檢測(cè)套件,內(nèi)置30+模型算法及250+預(yù)訓(xùn)練模型,覆蓋目標(biāo)檢測(cè)、實(shí)例分割、跟蹤、關(guān)鍵點(diǎn)檢測(cè)等方向,其中包括服務(wù)器端和移動(dòng)端高精度、輕量級(jí)產(chǎn)業(yè)級(jí)SOTA模型、冠軍方案和學(xué)術(shù)前沿算法,并提供配置化的網(wǎng)絡(luò)模塊組件、十余種數(shù)據(jù)增強(qiáng)策略和損失函數(shù)等高階優(yōu)化支持和多種部署方案,在打通數(shù)據(jù)處理、模型開發(fā)、訓(xùn)練、壓縮、部署全流程的基礎(chǔ)上,提供豐富的案例及教程,加速算法產(chǎn)業(yè)落地應(yīng)用。
? PP-YOLOE 是PaddleDetection推出的一種高精度SOTA目標(biāo)檢測(cè)模型,基于PP-YOLOv2的卓越的單階段Anchor-free模型,超越了多種流行的YOLO模型。
- 尺寸多樣:PP-YOLOE根據(jù)不同應(yīng)用場(chǎng)景設(shè)計(jì)了s/m/l/x,4個(gè)尺寸的模型來支持不同算力水平的硬件,無(wú)論是哪個(gè)尺寸,精度-速度的平衡都超越當(dāng)前所有同等計(jì)算量下的YOLO模型!可以通過width multiplier和depth multiplier配置。
- 性能卓越:具體來說,PP-YOLOE-l在COCO test-dev上以精度51.4%,TRT FP16推理速度149 FPS的優(yōu)異數(shù)據(jù),相較YOLOX,精度提升1.3%,加速25%;相較YOLOv5,精度提升0.7%,加速26.8%。訓(xùn)練速度較PP-YOLOv2提高33%,降低模型訓(xùn)練成本。
- 部署友好:與此同時(shí),PP-YOLOE在結(jié)構(gòu)設(shè)計(jì)上避免使用如deformable convolution或者matrix NMS之類的特殊算子,使其能輕松適配更多硬件。當(dāng)前已經(jīng)完備支持NVIDIA V100、T4這樣的云端GPU架構(gòu)以及如Jetson系列等邊緣端GPU和FPGA開發(fā)板。
1.2 OpenVINOTM
?OpenVINOTM是英特爾基于自身現(xiàn)有的硬件平臺(tái)開發(fā)的一種可以加快高性能計(jì)算機(jī)視覺和深度學(xué)習(xí)視覺應(yīng)用開發(fā)速度工具套件,用于快速開發(fā)應(yīng)用程序和解決方案,以解決各種任務(wù)(包括人類視覺模擬、自動(dòng)語(yǔ)音識(shí)別、自然語(yǔ)言處理和推薦系統(tǒng)等)。

?該工具套件基于最新一代的人工神經(jīng)網(wǎng)絡(luò),包括卷積神經(jīng)網(wǎng)絡(luò) (CNN)、遞歸網(wǎng)絡(luò)和基于注意力的網(wǎng)絡(luò),可擴(kuò)展跨英特爾? 硬件的計(jì)算機(jī)視覺和非視覺工作負(fù)載,從而最大限度地提高性能。它通過從邊緣到云部署的高性能、人工智能和深度學(xué)習(xí)推理來為應(yīng)用程序加速,并且允許直接異構(gòu)執(zhí)行。極大的提高計(jì)算機(jī)視覺、自動(dòng)語(yǔ)音識(shí)別、自然語(yǔ)言處理和其他常見任務(wù)中的深度學(xué)習(xí)性能;使用使用流行的框架(如TensorFlow,PyTorch等)訓(xùn)練的模型;減少資源需求,并在從邊緣到云的一系列英特爾?平臺(tái)上高效部署;支持在Windows與Linux系統(tǒng),且官方支持編程語(yǔ)言為Python與C++語(yǔ)言。
?OpenVINOTM工具套件2022.1版于2022年3月22日正式發(fā)布,與以往版本相比發(fā)生了重大革新,提供預(yù)處理API函數(shù)、ONNX前端API、AUTO 設(shè)備插件,并且支持直接讀入飛槳模型,在推理中中支持動(dòng)態(tài)改變模型的形狀,這極大地推動(dòng)了不同網(wǎng)絡(luò)的應(yīng)用落地。2022年9月23日,OpenVINOTM 工具套件2022.2版推出,對(duì)2022.1進(jìn)行了微調(diào),以包括對(duì)英特爾最新 CPU 和離散 GPU 的支持,以實(shí)現(xiàn)更多的人工智能創(chuàng)新和機(jī)會(huì)。
1.3 項(xiàng)目環(huán)境
- 操作系統(tǒng):Windows11
- OpenVINO:2022.2
- OpenCV:4.5.5
- Visual Studio:2022
- Python:3.9.13
2.模型下載與轉(zhuǎn)換
2.1 模型下載
?首先下載PP-YOLOE官方訓(xùn)練模型,該模型由PaddleDetection提供,基于COCO數(shù)據(jù)集訓(xùn)練,可以識(shí)別80種常見物體。此處采用的是PaddleDetection release/2.5版本,PP-YOLOE+模型,具體可以參考官方文件PP-YOLOE。
?使用命令,導(dǎo)出我們要使用的模型,在命令行種依次輸入以下指令,導(dǎo)出我們所使用的模型文件:
// 打開PaddleDetection代碼文件
cd ./PaddleDetection
// 導(dǎo)出指定模型
python tools/export_model.py -c configs/ppyoloe/ppyoloe_plus_crn_l_80e_coco.yml -o weights=https://paddledet.bj.bcebos.com/models/ppyoloe_plus_crn_l_80e_coco.pdparams
?此處導(dǎo)出的是PP-YOLOE+模型,l_80e格式,導(dǎo)出命令輸出如下圖所示。

?模型導(dǎo)出后可以在下述文件夾中找到該模型文件:

?利用模型查看器可以看出該模型,包含兩個(gè)輸入、兩個(gè)輸出。

2.2 模型裁剪
?直接導(dǎo)出的模型在OpenVINO中無(wú)法直接使用,需要對(duì)模型進(jìn)行裁剪,將模型后處理過程去掉,使用下面大神的提供的工具可以直接實(shí)現(xiàn)對(duì)Paddle模型直接裁剪:jiangjiajun/PaddleUtils: Some tools to operate PaddlePaddle model 。
?首先克隆改代碼倉(cāng)到本地:
git clone https://github.com/jiangjiajun/PaddleUtils.git
?然后打開到該代碼文件中下面的一個(gè)文件夾下,并將上一步導(dǎo)出的模型復(fù)制到該文加夾中

?在命令提示符中依次輸入以下命令:
// 打開指定文件
cd E:\Paddle\PaddleUtils\paddle
// 模型裁剪
python prune_paddle_model.py --model_dir ppyoloe_plus_crn_l_80e_coco --model_filename model.pdmodel --params_filename model.pdiparams --output_names tmp_16 concat_14.tmp_0 --save_dir export_model
?指令說明:
| 標(biāo)志位 | 說明 | 輸入 |
|---|---|---|
| --model_dir | 模型文件路徑 | ppyoloe_plus_crn_l_80e_coco |
| --model_filename | 靜態(tài)圖模型文件 | model.pdmodel |
| --params_filename | 模型配置文件信息 | model.pdiparams |
| --output_names | 輸出節(jié)點(diǎn)名 | tmp_16 concat_14.tmp_0 |
| --save_dir | 模型保存路徑 | export_model |
?此處主要關(guān)注輸出節(jié)點(diǎn)名這一項(xiàng)輸入,由于原模型輸入包含后處理這一部分,在模型部署時(shí)會(huì)出錯(cuò),所以模型裁剪的主要目的就是將模型后處理這一步去掉,因此將模型輸出設(shè)置為后處理開始前的模型節(jié)點(diǎn),此處主要存在兩個(gè)節(jié)點(diǎn):
?第一個(gè)節(jié)點(diǎn)包含模型預(yù)測(cè)的置信度輸出參數(shù),其位置如下圖所示:

?第二個(gè)節(jié)點(diǎn)是模型預(yù)測(cè)狂輸出節(jié)點(diǎn),其位置如下圖所示:

?輸入上述指令后,會(huì)獲得以下結(jié)果:

?在export_model文件夾下,可以獲得裁剪后的模型文件:

?使用模型查看器,可以看出導(dǎo)出的模型,輸入輸出發(fā)生了改變。模型的輸入僅包含image一項(xiàng),原有的scale_factor輸入由于在模型中使用不到,被一并削減掉。模型的輸出變成我們指定的節(jié)點(diǎn)輸出。

2.3 模型轉(zhuǎn)換ONNX
?由于Paddle模型未指定bath_size大小,在使用時(shí)會(huì)出現(xiàn)問題,因此通過將該模型轉(zhuǎn)為ONNX并指定bath_size大小,此處使用paddle2onnx工具便可以實(shí)現(xiàn)。
?在命令提示符中依次輸入以下指令,將上一步導(dǎo)出的模型轉(zhuǎn)為ONNX格式:
cd E:\Paddle\PaddleUtils\paddle
// 模型轉(zhuǎn)換
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_plus_crn_l_80e_coco.onnx
?此處需要指定模型的輸入形狀,--input_shape_dict "{'image':[1,3,640,640]}",其他設(shè)置按照常規(guī)設(shè)置即可,模型輸出如下圖所示:


2.4 轉(zhuǎn)為IR格式
? IR格式模型為OpenVINOTM推理工具原生支持模型,且對(duì)模型進(jìn)行了進(jìn)一步優(yōu)化,使得推理速度大大提升,此處我們使用OpenVINOTM 自帶的模型優(yōu)化工具進(jìn)行轉(zhuǎn)換。
?首先利用命令提示窗口打開OpenVINOTM工具路徑,然后輸入轉(zhuǎn)換命令,在該文件夾中會(huì)生成三個(gè)轉(zhuǎn)換后的文件,其輸出如圖所示,出現(xiàn)三個(gè)SUCCESS表示轉(zhuǎn)換成功。
cd .\openvino\tools
mo --input_model ppyoloe_plus_crn_l_80e_coco.onnx

3.C++部署實(shí)現(xiàn)
3.1 C++項(xiàng)目配置
? 當(dāng)前項(xiàng)目部署套件使用的是OpenVINOTM,圖片處理使用的是OpenCV,所以此處需要配置OpenVINOTM和OpenCV兩個(gè)附加依賴項(xiàng)。
?項(xiàng)目使用OpenVINOTM版本為2022.2,OpenCV版本使用的是4.5.5,具體安裝方式可以參考下面三個(gè)鏈接:
【OpenVINO】OpenVINO 2022.1 安裝教程(Windows)
【OpenVINO】OpenVINO 2022.1更新2022.2教程
3.2 模型推理類 Predictor
3.2.1 推理結(jié)構(gòu)體核心
// @brief 推理核心結(jié)構(gòu)體
typedef struct openvino_core {
ov::Core core; // core對(duì)象
std::shared_ptr<ov::Model> model_ptr; // 讀取模型指針
ov::CompiledModel compiled_model; // 模型加載到設(shè)備對(duì)象
ov::InferRequest infer_request; // 推理請(qǐng)求對(duì)象
} CoreStruct;
?為了方便使用OpenVINOTM推理工具,此處將該套件較為重要的幾個(gè)成員變量封裝成推理核心結(jié)構(gòu)體,方便模型在不同方法之間傳遞。
3.2.2 Predictor類結(jié)構(gòu)
class Predictor {
public:
// 構(gòu)造函數(shù)
Predictor(std::string& model_path, std::string& device_name);
// 析構(gòu)函數(shù)
~Predictor() { delete p; }
// 獲取節(jié)點(diǎn)張量
ov::Tensor get_tensor(std::string node_name);
// 填充圖片數(shù)據(jù)
void fill_tensor_data_image(ov::Tensor& input_tensor, const cv::Mat& input_image);
void fill_tensor_data_image(ov::Tensor& input_tensor, const std::vector<cv::Mat> input_image);
// 模型推理
void infer();
// 獲取模型輸出
std::vector<float> get_output_data(std::string output_node_name);
private:
CoreStruct* p;
};
?由于我們此處只進(jìn)行PP-YOLOE模型的推理,所以無(wú)需構(gòu)建太復(fù)雜的推理類,主要包括
- 構(gòu)造函數(shù)
Predictor(model_path, device_name):主要實(shí)現(xiàn)功能為初始化推理核心,包括讀取本地模型,將模型加載到設(shè)備,創(chuàng)建推理通道三個(gè)步驟; - 獲取節(jié)點(diǎn)張量
get_tensor(node_name):主要實(shí)現(xiàn)功能獲取指定節(jié)點(diǎn)的張量,主要用于獲取輸入節(jié)點(diǎn)張量; - 填充輸入數(shù)據(jù)
fill_tensor_data_image(input_tensor, input_image):主要實(shí)現(xiàn)將帶推理數(shù)據(jù)添加到模型中,支持添加單張圖片數(shù)據(jù)和多通道推理下的多張圖片添加; - 模型推理
infer():模型推理功能,按照讀取的模型和加載的推理數(shù)據(jù)計(jì)算模型; - 讀取模型輸出
get_output_data(output_node_name):讀取推理模型的輸出。
?針對(duì)上訴方法的實(shí)現(xiàn)可以參考源碼,此處不做詳細(xì)解釋。
3.3 圖片數(shù)據(jù)處理類 ImageProcess
class ImageProcess {
public:
// 預(yù)處理圖片
cv::Mat image_normalize(cv::Mat& sourse_mat, cv::Size& size);
// 處理推理結(jié)果
cv::Mat yoloe_result_process(cv::Mat& sourse_mat, std::vector<float>& vector_box, std::vector<float>& vector_conf);
// 讀取lable文件
void read_class_names(std::string path_name);
// 設(shè)置縮放比例
void set_scale_factor(double scale);
private:
// 縮放比例
double scale_factor;
// lable容器
std::vector<std::string> class_names;
};
?此處設(shè)置一個(gè)數(shù)據(jù)處理類主要用于處理模型的輸入輸出數(shù)據(jù),主要不包括先買幾個(gè)方法:
- 預(yù)處理圖片
image_normalize(sourse_mat, size);:預(yù)處理圖像數(shù)據(jù),包括以下幾個(gè)處理步驟:1.轉(zhuǎn)換RGB 2.縮放圖片 3.圖片歸一化; - 處理PP-YOLOE結(jié)果
yoloe_result_process(sourse_mat, vector_box, vector_conf):由于我們將模型進(jìn)行了裁剪,去除了模型自帶的后處理以及非極大值抑制,因此模型的輸出數(shù)據(jù)較復(fù)雜,此處封裝該方法主要實(shí)現(xiàn)將模型讀取的數(shù)據(jù)按照指定要求進(jìn)行處理,并繪制到結(jié)果圖片上; - 讀取lable文件
read_class_names(path_name):讀取本地lable.txt文件; - 設(shè)置縮放比例
set_scale_factor(scale):設(shè)置原圖與模型輸入的縮放比例,用于回復(fù)模型預(yù)測(cè)框。
3.4 模型推理實(shí)現(xiàn)
3.4.1 定義相關(guān)信息
// 模型路徑
//std::string model_path = "../model/ppyoloe_plus_crn_s_80e_coco.onnx";
std::string model_path = "../model/ir/ppyoloe_plus_crn_s_80e_coco.xml";
// 設(shè)備名稱
std::string device_name = "CPU";
// 輸入節(jié)點(diǎn)
std::string input__node_name = "image";
// 輸出節(jié)點(diǎn)名
std::string output_box_node_name = "tmp_16";
std::string output_conf_node_name = "concat_14.tmp_0";
// 測(cè)試圖片
std::string image_path = "../image/demo_3.jpg";
// 類別文件
std::string lable_path = "../model/lable.txt";
?首先定義相關(guān)的信息,此處推理可以使用ONNX模型和IR模型;模型輸入節(jié)點(diǎn)為image,輸出節(jié)點(diǎn)為:
tmp_16預(yù)測(cè)框輸出節(jié)點(diǎn),concat_14.tmp_0置信度輸出,利用OpenVINOTM工具可以直接讀取該模型節(jié)點(diǎn)名稱,此處直接定義使用。
3.4.2 預(yù)處理推理數(shù)據(jù)
// 創(chuàng)建數(shù)據(jù)處理類
ImageProcess image_pro;
// 讀取類別文件
image_pro.read_class_names(lable_path);
// 圖片預(yù)處理
cv::Mat image = cv::imread(image_path);
cv::Size input_size(640, 640);
// 將圖片放到方形背景中
int length = image.rows > image.cols ? image.rows : image.cols;
cv::Mat input_mat = cv::Mat::zeros(length, length, CV_8UC3);
cv::Rect roi(0, 0, image.cols, image.rows);
image.copyTo(input_mat(roi));
// 設(shè)置縮放比例
image_pro.set_scale_factor((double)length / 640.0);
// 歸一化處理
cv::Mat input_data = image_pro.image_normalize(input_mat, input_size);
?此處推理數(shù)據(jù)預(yù)處理主要使用類ImageProcess中定義的方法實(shí)現(xiàn),PP-YOLOE模型輸入為3×640×640,形狀為正方形,因此此處采用將推理數(shù)據(jù)放在一個(gè)較大的正方形背景中,防止圖片形狀在縮放時(shí)發(fā)生變形。
3.4.3 模型推理
// 創(chuàng)建推理通道
Predictor predictor(model_path, device_name);
// 加載模型推理數(shù)據(jù)
ov::Tensor input_tensor = predictor.get_tensor(input__node_name);
predictor.fill_tensor_data_image(input_tensor, input_data);
// 模型推理
predictor.infer();
?此處通過調(diào)用前面定義的Predictor類實(shí)現(xiàn),初始化Predictor類,將模型讀取到內(nèi)存中,并加載到指定設(shè)備,創(chuàng)建推理通道;接下來就是加載預(yù)處理完的推理數(shù)據(jù)到模型上;最后進(jìn)行模型推理。
3.4.4 處理推理結(jié)果
// 讀取推理結(jié)果
std::vector<float> result_boxes = predictor.get_output_data(output_box_node_name);
std::vector<float> result_conf = predictor.get_output_data(output_conf_node_name);
// 處理推理結(jié)果
cv::Mat result_image = image_pro.yoloe_result_process(image, result_boxes, result_conf);
?PP-TYOLOE模型經(jīng)過我們裁剪后共有兩個(gè)輸出節(jié)點(diǎn),一個(gè)負(fù)責(zé)輸出預(yù)測(cè)框,一個(gè)負(fù)責(zé)輸出置信值,將預(yù)測(cè)結(jié)果讀取后,調(diào)用推理結(jié)果處理方法進(jìn)行處理。
3.4.5 模型推理效果
?使用該模型,分別預(yù)測(cè)了兩個(gè)圖片,預(yù)測(cè)結(jié)果可以查看下面兩個(gè)圖。其中第二張圖為飛槳測(cè)試圖,可與飛槳推理套件識(shí)別結(jié)果進(jìn)行對(duì)比。


4.Python實(shí)現(xiàn)
4.1 環(huán)境安裝
?OpenVINOTM工具套件2022.1版于2022年3月22日正式發(fā)布,與以往版本相比發(fā)生了重大革新,提供預(yù)處理API函數(shù)、ONNX前端API、AUTO 設(shè)備插件,并且支持直接讀入飛槳模型,在推理中中支持動(dòng)態(tài)改變模型的形狀,這極大地推動(dòng)了不同網(wǎng)絡(luò)的應(yīng)用落地。2022年9月23日,OpenVINOTM 工具套件2022.2版推出,對(duì)2022.1進(jìn)行了微調(diào),以包括對(duì)英特爾最新 CPU 和離散 GPU 的支持,以實(shí)現(xiàn)更多的人工智能創(chuàng)新和機(jī)會(huì)。
?此處選用OpenVINOTM 2022.2 版本,對(duì)于Python本,我們可以直接使用PIP命令安裝。建議使用Anaconda 創(chuàng)建虛擬環(huán)境安裝,對(duì)于最新版,在創(chuàng)建好的虛擬環(huán)境下直接輸入以下命令進(jìn)行安裝:
// 更新pip
python -m pip install --upgrade pip
// 安裝
pip install openvino-dev[ONNX,tensorflow2]==2022.2.0
?安裝過程中如出現(xiàn)下載安裝包錯(cuò)誤以及網(wǎng)絡(luò)等原因時(shí),可以重新運(yùn)行安裝命令,會(huì)繼續(xù)上一次的安裝。
4.2 創(chuàng)建推理類 Predictor
from openvino.runtime import Core
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])
?此處由于只進(jìn)行PP-YOLOE模型推理,所以只簡(jiǎn)單地封裝一下Predictor類:主要包括初始化函數(shù),負(fù)責(zé)讀取本地模型并加載到指定設(shè)備中;獲取輸入輸出名稱函數(shù)以及模型預(yù)測(cè)函數(shù)。
4.3數(shù)據(jù)處理方法
4.3.1 輸入圖片預(yù)處理
def process_image(input_image, size):
"""輸入圖片與處理方法,按照PP-Yoloe模型要求預(yù)處理圖片數(shù)據(jù)
Args:
input_image (uint8): 輸入圖片矩陣
size (int): 模型輸入大小
Returns:
float32: 返回處理后的圖片矩陣數(shù)據(jù)
"""
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轉(zhuǎn)RGB
img = cv.resize(img, (size, size), cv.INTER_NEAREST) # 縮放圖片
img = np.transpose(img,[2, 0, 1]) # 轉(zhuǎn)換格式
img = img / 255.0 # 歸一化
img = np.expand_dims(img,0) # 增加維度
return img
?根據(jù) PP-YOLOE模型輸入要求,處理圖片數(shù)據(jù),主要包括圖片通道轉(zhuǎn)換、圖片縮放、轉(zhuǎn)換矩陣、數(shù)據(jù)歸一化以及增加矩陣維度。按照PP-YOLOE模型輸入設(shè)置,歸一化方式是直接將像素點(diǎn)除255,將輸入數(shù)據(jù)整合到0~1之間,加快模型的計(jì)算。PP-YOLOE模型ONNX格式只支持bath_size=1的推理,所以最后將數(shù)據(jù)矩陣維度直接增加一個(gè)維度即可。
4.3.2 模型輸出結(jié)果處理
def process_result(box_results, conf_results):
"""按照PP-Yolove模型輸出要求,處理數(shù)據(jù),非極大值抑制,提取預(yù)測(cè)結(jié)果
Args:
box_results (float32): 預(yù)測(cè)框預(yù)測(cè)結(jié)果
conf_results (float32): 置信度預(yù)測(cè)結(jié)果
Returns:
float: 預(yù)測(cè)框
float: 分?jǐn)?shù)
int: 類別
"""
conf_results = np.transpose(conf_results,[0, 2, 1]) # 轉(zhuǎn)置
# 設(shè)置輸出形狀
box_results =box_results.reshape(8400,4)
conf_results = conf_results.reshape(8400,80)
scores = []
classes = []
boxes = []
for i in range(8400):
conf = conf_results[i,:] # 預(yù)測(cè)分?jǐn)?shù)
score = np.max(conf) # 獲取類別
# 篩選較小的預(yù)測(cè)類別
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)
# 非極大值抑制篩選重復(fù)的預(yù)測(cè)結(jié)果
indexs = tf.image.non_max_suppression(boxes,scores,len(scores),0.25,0.35)
# 處理非極大值抑制后的結(jié)果
result_box = []
result_score = []
result_class = []
for i, index in enumerate(indexs):
result_score.append(scores[index])
result_box.append(boxes[index,:])
result_class.append(classes[index])
# 返滬結(jié)果轉(zhuǎn)為矩陣
return np.array(result_box),np.array(result_score),np.array(result_class)
?由于我們所使用的PP-YOLOE被我們裁剪過,因此模型的輸出是未進(jìn)行處理的結(jié)果數(shù)據(jù),模型輸出節(jié)點(diǎn)有兩個(gè),一個(gè)為預(yù)測(cè)框輸出,一個(gè)節(jié)點(diǎn)為置信值輸出,所以后期需要對(duì)輸出結(jié)果進(jìn)行處理。
?置信度結(jié)果輸出形狀為[1, 80, 8400],而實(shí)際80代表的一個(gè)預(yù)測(cè)結(jié)果對(duì)應(yīng)的80個(gè)類別的置信值,而8400表示有8400個(gè)預(yù)測(cè)結(jié)果;而預(yù)測(cè)框輸出結(jié)果為形狀為[1, 8400, 4],對(duì)應(yīng)了8400個(gè)預(yù)測(cè)結(jié)果的預(yù)測(cè)框,其中4代表預(yù)測(cè)框的左上頂點(diǎn)預(yù)右下頂點(diǎn)的橫縱坐標(biāo)。
?因此結(jié)果處理主要包含以下幾個(gè)方面:
- 置信度結(jié)果轉(zhuǎn)置處理,并提取預(yù)測(cè)結(jié)果最大的類別、預(yù)測(cè)分?jǐn)?shù)和對(duì)應(yīng)的預(yù)測(cè)框;
- 非極大值抑制提取預(yù)測(cè)框和類別。
4.3.3 繪制預(yù)測(cè)結(jié)果
def draw_box(image, boxes, scores, classes, lables):
"""將預(yù)測(cè)結(jié)果繪制到圖像上
Args:
image (uint8): 原圖片
boxes (float32): 預(yù)測(cè)框
scores (float32): 分?jǐn)?shù)
classes (int): 類別
lables (str): 標(biāo)簽
Returns:
uint8: 標(biāo)注好的圖片
"""
scale = max(image.shape) / 640.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)
lable = lables[classes[i]]
score = scores[i]
cv.rectangle(image, (x1, y1), (x2, y2), (0,0,255), 2, cv.LINE_8)
cv.putText(image,lable+":"+str(score),(x1,y1-10),cv.FONT_HERSHEY_SIMPLEX, 0.55, (0, 0, 255), 2)
return image
?上一步經(jīng)過結(jié)果處理,最終獲得預(yù)測(cè)框、分?jǐn)?shù)以及類別,最后通過OpenCV將預(yù)測(cè)結(jié)果繪制到圖片上,主要是一個(gè)預(yù)測(cè)框繪制和分?jǐn)?shù)、類別的書寫兩步。
4.4 模型推理
'''-------------------1. 導(dǎo)入相關(guān)信息 ----------------------'''
# yoloe_model_path = "E:/Text_Model/pp-yoloe/ppyoloe_plus_crn_s_80e_coco.onnx"
yoloe_model_path = "E:/Text_Model/pp-yoloe/ppyoloe_plus_crn_s_80e_coco.xml"
image_path = "E:/Text_dataset/YOLOv5/0001.jpg"
lable_path = "E:/Git_space/基于OpenVINO部署PP-YOLOE模型/model/lable.txt";
'''-------------------2. 創(chuàng)建模型預(yù)測(cè)器 ----------------------'''
predictor = Predictor(model_path = yoloe_model_path)
'''-------------------3. 預(yù)處理模型輸入數(shù)據(jù) ----------------------'''
image = cv.imread(image_path)
input_image = process_image(image, 640)
'''-------------------4. 模型推理 ----------------------'''
results = predictor.predict(input_data=input_image)
'''-------------------5. 后處理預(yù)測(cè)結(jié)果 ----------------------'''
boxes_name = predictor.get_outputs_name(0)
conf_name = predictor.get_outputs_name(1)
boxes, scores, classes = process_result(box_results=results[boxes_name], conf_results=results[conf_name]) # 處理結(jié)果
lables = read_lable(lable_path=lable_path) # 讀取lable
result_image = draw_box(image=image, boxes=boxes, scores=scores, classes=classes, lables=lables) # 繪制結(jié)果
cv.imshow("result",result_image)
cv.waitKey(0)
?根據(jù)模型推理流程,最后調(diào)用模型推理類進(jìn)行實(shí)現(xiàn):
- 導(dǎo)入相關(guān)信息:主要是定義模型地址、待預(yù)測(cè)圖片地址和類別文件;
- 創(chuàng)建模型預(yù)測(cè)器:主要初始化預(yù)測(cè)類,讀取本地模型,此處可以讀取ONNX模型和IR模型兩種格式;
- 預(yù)處理圖片:調(diào)用定義的圖片處理方法,將本地圖片數(shù)據(jù)轉(zhuǎn)為模型推理的數(shù)據(jù);
- 模型推理:將處理好的圖片數(shù)據(jù)加載到模型中,并獲取模型推理結(jié)果;
- 處理模型結(jié)果:主要是調(diào)用結(jié)果處理方法實(shí)現(xiàn),如果需要可視化,可以將預(yù)測(cè)結(jié)果繪制到圖片中。