YOLOv5在最新OpenVINO 2021R02版本的部署與代碼演示詳解
點擊上方“小白學視覺”,選擇加"星標"或“置頂”
重磅干貨,第一時間送達
本文轉載自:OpenCV學堂
?YOLOv5在OpenVINO上的部署,網(wǎng)上有很多python版本的代碼,但是基本都有個很內傷的問題,就是還在用pytorch的一些庫做解析,C++的代碼有個更大的內傷就是自定義解析解釋的不是很清楚,所以本人閱讀YOLOv5的pytorch代碼推理部分,從原始的三個輸出層解析實現(xiàn)了boxes, classes, nms等關鍵C++代碼輸出,實現(xiàn)了純OpenVINO+OpenCV版本的YOLOv5s模型推理的代碼演示。下面就是詳細的系統(tǒng)環(huán)境與各個部分解釋,以及代碼實現(xiàn)與演示圖像。
系統(tǒng)版本信息與依賴
Window 10 64bit
Pytorch1.7+CUDA10.0
Python3.8.5
VS2015
OpenVINO_2021.2.185
01
YOLOv5下載與測試運行
YOLOv5是第二個非官方的YOLO對象檢測版本,也是第一個Pytorch實現(xiàn)的YOLO對象檢測版本。Github地址如下:
https://github.com/ultralytics/yolov5需要克隆到本地
git clone https://github.com/ultralytics/yolov5.git然后運行
pip install -r requirements.txt安裝所有依賴。
最后運行一段視頻或者圖像完成測試
python detect.py --source D:\images\video\SungEun.avi --weights yolov5s.pt --conf 0.25
視頻測試結果如下:

圖像測試結果如下:

02
模型轉換
模型轉換主要是把原始的YOLOv5的pytorch模型文件轉換為通用的開放模型格式ONNX與OpenVIN特有的文件格式IR(*.xml與*.bin)。
OpenVINO從2020R02以后版本開始支持直接讀取ONNX格式文件,所以我們既可以通過腳本直接導出onnx格式文件,直接給OpenVINO調用,也可以對得到ONNX文件通過OpenVINO的模型轉換腳本做進一步轉換生成IR中間格式(*.bin文件與*.xml文件)。
Pytorch的YOLOv5項目本身已經(jīng)提供了轉換腳本,命令行運行方式如下:
# export at 640x640 with batch size 1python models/export.py --weights yolov5s.pt --img 640 --batch 1

然后生成的yolov5s.onnx文件就在同一目錄下面。
把ONNX轉換為IR中間格式,運行結果如下

03
OpenVINO SDK+YOLOv5s代碼演示
上面我們已經(jīng)成功轉換為YOLOv5s模型IR,現(xiàn)在就可以基于最新的SDK來說完成調用解析與調用。
第一步:
初始Core對象,讀取模型(加載ONNX格式或者IR格式均可以,親測有效)
//?創(chuàng)建IE插件,?查詢支持硬件設備
Core?ie;
vector<string>?availableDevices?=?ie.GetAvailableDevices();
for?(int?i?=?0;?i?????printf("supported?device?name?:?%s?\n",?availableDevices[i].c_str());
}
//??加載檢測模型
auto?network?=?ie.ReadNetwork("D:/python/yolov5/yolov5s.xml",?"D:/python/yolov5/yolov5s.bin");
//?auto?network?=?ie.ReadNetwork("D:/python/yolov5/yolov5s.onnx");第二步:
設置輸入與輸出格式,YOLOv5s輸入的圖像被歸一化到0~1之間,而且是RGB通道順序,輸入與輸出格式設置數(shù)據(jù)為浮點數(shù),這部分的代碼如下:
//?設置輸入格式
for?(auto?&item?:?input_info)?{
????auto?input_data?=?item.second;
????input_data->setPrecision(Precision::FP32);
????input_data->setLayout(Layout::NCHW);
????input_data->getPreProcess().setResizeAlgorithm(RESIZE_BILINEAR);
????input_data->getPreProcess().setColorFormat(ColorFormat::RGB);
}
//?設置輸出格式
for?(auto?&item?:?output_info)?{
????auto?output_data?=?item.second;
????output_data->setPrecision(Precision::FP32);
}
auto?executable_network?=?ie.LoadNetwork(network,?"CPU");第三步:
設置輸入圖像數(shù)據(jù)并實現(xiàn)推理預測
int64?start?=?getTickCount();
/**?Iterating?over?all?input?blobs?**/
for?(auto?&?item?:?input_info)?{
????auto?input_name?=?item.first;
????/**?Getting?input?blob?**/
????auto?input?=?infer_request.GetBlob(input_name);
????size_t?num_channels?=?input->getTensorDesc().getDims()[1];
????size_t?h?=?input->getTensorDesc().getDims()[2];
????size_t?w?=?input->getTensorDesc().getDims()[3];
????size_t?image_size?=?h*w;
????Mat?blob_image;
????resize(src,?blob_image,?Size(w,?h));
????cvtColor(blob_image,?blob_image,?COLOR_BGR2RGB);
????//?NCHW
????float*?data?=?static_cast<float*>(input->buffer());
????for?(size_t?row?=?0;?row?????????for?(size_t?col?=?0;?col?????????????for?(size_t?ch?=?0;?ch?????????????????data[image_size*ch?+?row*w?+?col]?=?float(blob_image.at(row,?col)[ch])/255.0;
????????????}
????????}
????}
}
//?執(zhí)行預測
infer_request.Infer(); 上面的代碼跟SSD對象檢測的OpenVINO調用基本上沒有太大區(qū)別。主要的區(qū)別是在對推理完成結果的解析部分。
第四步:
解析輸出結果,實現(xiàn)顯示輸出。要完成這個部分,首先需要看一下YOLOv5項目中的yolo.py中對推理部分的組裝。首先輸出層,從YOLOv3開始到YOLOv5,輸出層都是3層,分別對應的降采樣的倍數(shù)是32、16、8。

以輸入640x640大小的圖像為例,得到三個輸出層大小應該分別是20、40、80。每個層上對應三個尺度的anchor,表示如下:

模型的預測是在20x20、40x40、80x80每個輸出層的每個特征點上預測三個框,每個框預測分類!每個框的維度大小為 cx,cy,w,h,conf + number of class, 圖示如下:

以YOLOv5項目的預訓練模型是基于COCO的80個對象類別為例,在檢測階段最終三個輸出層是:
1x3x20x20x851x3x40x40x851x3x80x80x85
我電腦上實際加載模型之后的三個輸出層實現(xiàn)運行結果:

然后循環(huán)每個輸出層,解析每個特征點對應的3個框與相關數(shù)據(jù)。由于在導出的時候ONNX格式文件時模型的推理得到的三個輸出層原始結果,所以還需要對每個數(shù)據(jù)先完成sigmoid歸一化,然后再計算相關值,這部分的代碼實現(xiàn)我參考了項目中的yolo.py中的Detection部分,得到初始每個對象的檢測框之后,采用OpenVINO中自帶非最大抑制函數(shù),完成非最大抑制,就得到了最終的預測框,然后繪制顯示。所以最終的解析輸出層部分的代碼如下:
for?(int?i?=?0;?i?????for?(int?c?=?0;?c?????????int?row?=?i?/?side_h;
????????int?col?=?i?%?side_h;
????????int?object_index?=?c*side_data_square?+?row*side_data_w?+?col*side_data;
????????//?閾值過濾
????????float?conf?=?sigmoid_function(output_blob[object_index?+?4]);
????????if?(conf?0.25)?{
????????????continue;
????????}
????????//?解析cx,?cy,?width,?height
????????float?x?=?(sigmoid_function(output_blob[object_index])?*?2?-?0.5?+?col)*stride;
????????float?y?=?(sigmoid_function(output_blob[object_index?+?1])?*?2?-?0.5?+?row)*stride;
????????float?w?=?pow(sigmoid_function(output_blob[object_index?+?2])?*?2,?2)*anchors[anchor_index?+?c?*?2];
????????float?h?=?pow(sigmoid_function(output_blob[object_index?+?3])?*?2,?2)*anchors[anchor_index?+?c?*?2?+?1];
????????float?max_prob?=?-1;
????????int?class_index?=?-1;
????????//?解析類別
????????for?(int?d?=?5;?d?85;?d++)?{
????????????float?prob?=?sigmoid_function(output_blob[object_index?+?d]);
????????????if?(prob?>?max_prob)?{
????????????????max_prob?=?prob;
????????????????class_index?=?d?-?5;
????????????}
????????}
????????//?轉換為top-left,?bottom-right坐標
????????int?x1?=?saturate_cast<int>((x?-?w?/?2)?*?scale_x);??//?top?left?x
????????int?y1?=?saturate_cast<int>((y?-?h?/?2)?*?scale_y);??//?top?left?y
????????int?x2?=?saturate_cast<int>((x?+?w?/?2)?*?scale_x);??//?bottom?right?x
????????int?y2?=?saturate_cast<int>((y?+?h?/?2)?*?scale_y);?//?bottom?right?y
????????//?解析輸出
????????classIds.push_back(class_index);
????????confidences.push_back((float)conf);
????????boxes.push_back(Rect(x1,?y1,?x2?-?x1,?y2?-?y1));
????????//?rectangle(src,?Rect(x1,?y1,?x2?-?x1,?y2?-?y1),?Scalar(255,?0,?255),?2,?8,?0);
????}
}非最大抑制的代碼如下:
vector<int>?indices;
NMSBoxes(boxes,?confidences,?0.25,?0.5,?indices);
for?(size_t?i?=?0;?i?{
????int?idx?=?indices[i];
????Rect?box?=?boxes[idx];
????rectangle(src,?box,?Scalar(140,?199,?0),?4,?8,?0);
}
float?fps?=?getTickFrequency()?/?(getTickCount()?-?start);
float?time?=?(getTickCount()?-?start)?/?getTickFrequency();官方的兩張測試圖像,測試結果如下:


加速,OpenVINO+YOLOv5 真的可以在一起?。–ORE i78th)。
交流群
歡迎加入公眾號讀者群一起和同行交流,目前有SLAM、三維視覺、傳感器、自動駕駛、計算攝影、檢測、分割、識別、醫(yī)學影像、GAN、算法競賽等微信群(以后會逐漸細分),請掃描下面微信號加群,備注:”昵稱+學校/公司+研究方向“,例如:”張三?+?上海交大?+?視覺SLAM“。請按照格式備注,否則不予通過。添加成功后會根據(jù)研究方向邀請進入相關微信群。請勿在群內發(fā)送廣告,否則會請出群,謝謝理解~

