当前位置:   article > 正文

手把手教你使用OpenVINO部署NanoDet模型

openvino_include_dirs

点击上方“3D视觉工坊”,选择“星标”

干货第一时间送达

本文为大家介绍了一个手把手使用OpenVINO部署NanoDet的教程,并开源部署的全部代码,在Intel i7-7700HQ CPU做到了6ms一帧的速度。

0x0. nanodet简介

NanoDet (https://github.com/RangiLyu/nanodet)是一个速度超快和轻量级的Anchor-free 目标检测模型。想了解算法本身的可以去搜一搜之前机器之心的介绍。

0x1. 环境配置

  • Ubuntu:18.04

  • OpenVINO:2020.4

  • OpenCV:3.4.2

  • OpenVINO和OpenCV安装包(编译好了,也可以自己从官网下载自己编译)可以从链接: https://pan.baidu.com/s/1zxtPKm-Q48Is5mzKbjGHeg 密码: gw5c下载

  • OpenVINO安装

  1. tar -xvzf l_openvino_toolkit_p_2020.4.287.tgz
  2. cd l_openvino_toolkit_p_2020.4.287
  3. sudo ./install_GUI.sh 一路next安装
  4. cd /opt/intel/openvino/install_dependencies
  5. sudo ./install_openvino_dependencies.sh
  6. vi ~/.bashrc
  • 把如下两行放置到bashrc文件尾

  1. source /opt/intel/openvino/bin/setupvars.sh
  2. source /opt/intel/openvino/opencv/setupvars.sh
  • source ~/.bashrc 激活环境

  • 模型优化配置步骤

  1. cd /opt/intel/openvino/deployment_tools/model_optimizer/install_prerequisites
  2. sudo ./install_prerequisites_onnx.sh(模型是从onnx转为IR文件,只需配置onnx依赖)
  • OpenCV配置

tar -xvzf opencv-3.4.2.zip 解压OpenCV到用户根目录即可,以便后续调用。(这是我编译好的版本,有需要可以自己编译)

0x2. NanoDet模型训练和转换ONNX

  • git clone https://github.com/Wulingtian/nanodet.git

  • cd nanodet

  • cd config 配置模型文件,训练模型

  • 定位到nanodet目录,进入tools目录,打开export.py文件,配置cfg_path model_path out_path三个参数

  • 定位到nanodet目录,运行 python tools/export.py 得到转换后的onnx模型

  • python /opt/intel/openvino/deployment_tools/model_optimizer/mo_onnx.py --input_model onnx模型 --output_dir 期望模型输出的路径。得到IR文件

0x3. NanoDet模型部署

  • sudo apt install cmake 安装cmake

  • git clone https://github.com/Wulingtian/nanodet_openvino.git (求star!)

  • cd nanodet_openvino 打开CMakeLists.txt文件,修改OpenCV_INCLUDE_DIRS和OpenCV_LIBS_DIR,之前已经把OpenCV解压到根目录了,所以按照你自己的路径指定

  • 定位到nanodet_openvino,cd models 把之前生成的IR模型(包括bin和xml文件)文件放到该目录下

  • 定位到nanodet_openvino, cd test_imgs 把需要测试的图片放到该目录下

  • 定位到nanodet_openvino,编辑main.cpp,xml_path参数修改为"../models/你的模型名称.xml"

  • 编辑 num_class 设置类别数,例如:我训练的模型是安全帽检测,只有1类,那么设置为1

  • 编辑 src 设置测试图片路径,src参数修改为"../test_imgs/你的测试图片"

  • 定位到nanodet_openvino

  • mkdir build; cd build; cmake .. ;make

  • ./detect_test 输出平均推理时间,以及保存预测图片到当前目录下,至此,部署完成!

0x4. 核心代码一览

  1. //主要对图片进行预处理,包括resize和归一化
  2. std::vector<float> Detector::prepareImage(cv::Mat &src_img){
  3.     std::vector<float> result(INPUT_W * INPUT_H * 3);
  4.     float *data = result.data();
  5.     float ratio = float(INPUT_W) / float(src_img.cols) < float(INPUT_H) / float(src_img.rows) ? float(INPUT_W) / float(src_img.cols) : float(INPUT_H) / float(src_img.rows);
  6.     cv::Mat flt_img = cv::Mat::zeros(cv::Size(INPUT_W, INPUT_H), CV_8UC3);
  7.     cv::Mat rsz_img = cv::Mat::zeros(cv::Size(src_img.cols*ratio, src_img.rows*ratio), CV_8UC3);
  8.     cv::resize(src_img, rsz_img, cv::Size(), ratio, ratio);
  9.     rsz_img.copyTo(flt_img(cv::Rect(00, rsz_img.cols, rsz_img.rows)));
  10.     flt_img.convertTo(flt_img, CV_32FC3);
  11.     int channelLength = INPUT_W * INPUT_H;
  12.     std::vector<cv::Mat> split_img = {
  13.             cv::Mat(INPUT_W, INPUT_H, CV_32FC1, data + channelLength * 2),
  14.             cv::Mat(INPUT_W, INPUT_H, CV_32FC1, data + channelLength),
  15.             cv::Mat(INPUT_W, INPUT_H, CV_32FC1, data)
  16.     };
  17.     cv::split(flt_img, split_img);
  18.     for (int i = 0; i < 3; i++) {
  19.         split_img[i] = (split_img[i] - img_mean[i]) / img_std[i];
  20.     }
  21.     return result;
  22. }
  23. //加载IR模型,初始化网络
  24. bool Detector::init(string xml_path,double cof_threshold,double nms_area_threshold,int input_w, int input_h, int num_class, int r_rows, int r_cols, std::vector<int> s, std::vector<float> i_mean,std::vector<float> i_std){
  25.     _xml_path = xml_path;
  26.     _cof_threshold = cof_threshold;
  27.     _nms_area_threshold = nms_area_threshold;
  28.     INPUT_W = input_w;
  29.     INPUT_H = input_h;
  30.     NUM_CLASS = num_class;
  31.     refer_rows = r_rows;
  32.     refer_cols = r_cols;
  33.     strides = s;
  34.     img_mean = i_mean;
  35.     img_std = i_std;
  36.     Core ie;
  37.     auto cnnNetwork = ie.ReadNetwork(_xml_path); 
  38.     InputsDataMap inputInfo(cnnNetwork.getInputsInfo());
  39.     InputInfo::Ptr& input = inputInfo.begin()->second;
  40.     _input_name = inputInfo.begin()->first;
  41.     input->setPrecision(Precision::FP32);
  42.     input->getInputData()->setLayout(Layout::NCHW);
  43.     ICNNNetwork::InputShapes inputShapes = cnnNetwork.getInputShapes();
  44.     SizeVector& inSizeVector = inputShapes.begin()->second;
  45.     cnnNetwork.reshape(inputShapes);
  46.     _outputinfo = OutputsDataMap(cnnNetwork.getOutputsInfo());
  47.     for (auto &output : _outputinfo) {
  48.         output.second->setPrecision(Precision::FP32);
  49.     }
  50.     _network =  ie.LoadNetwork(cnnNetwork, "CPU");
  51.     return true;
  52. }
  53. //模型推理及获取输出结果
  54. vector<Detector::Bbox> Detector::process_frame(Mat& inframe){
  55.     cv::Mat showImage = inframe.clone();
  56.     std::vector<float> pr_img = prepareImage(inframe);
  57.     InferRequest::Ptr infer_request = _network.CreateInferRequestPtr();
  58.     Blob::Ptr frameBlob = infer_request->GetBlob(_input_name);
  59.     InferenceEngine::LockedMemory<void> blobMapped = InferenceEngine::as<InferenceEngine::MemoryBlob>(frameBlob)->wmap();
  60.     float* blob_data = blobMapped.as<float*>();
  61.     memcpy(blob_data, pr_img.data(), 3 * INPUT_H * INPUT_W * sizeof(float));
  62.     infer_request->Infer();
  63.     vector<Rect> origin_rect;
  64.     vector<float> origin_rect_cof;
  65.     int i=0;
  66.     vector<Bbox> bboxes;
  67.     for (auto &output : _outputinfo) {
  68.         auto output_name = output.first;
  69.         Blob::Ptr blob = infer_request->GetBlob(output_name);
  70.         LockedMemory<const void> blobMapped = as<MemoryBlob>(blob)->rmap();
  71.         float *output_blob = blobMapped.as<float *>();
  72.         bboxes = postProcess(showImage,output_blob);
  73.         ++i;
  74.     }
  75.     return bboxes;
  76. }
  77. //对模型输出结果进行解码及nms
  78. std::vector<Detector::Bbox> Detector::postProcess(const cv::Mat &src_img,
  79.                               float *output) {
  80.     GenerateReferMatrix();
  81.     std::vector<Detector::Bbox> result;
  82.     float *out = output;
  83.     float ratio = std::max(float(src_img.cols) / float(INPUT_W), float(src_img.rows) / float(INPUT_H));
  84.     cv::Mat result_matrix = cv::Mat(refer_rows, NUM_CLASS + 4, CV_32FC1, out);
  85.     for (int row_num = 0; row_num < refer_rows; row_num++) {
  86.         Detector::Bbox box;
  87.         auto *row = result_matrix.ptr<float>(row_num);
  88.         auto max_pos = std::max_element(row + 4, row + NUM_CLASS + 4);
  89.         box.prob = row[max_pos - row];
  90.         if (box.prob < _cof_threshold)
  91.             continue;
  92.         box.classes = max_pos - row - 4;
  93.         auto *anchor = refer_matrix.ptr<float>(row_num);
  94.         box.x = (anchor[0] - row[0] * anchor[2] + anchor[0] + row[2] * anchor[2]) / 2 * ratio;
  95.         box.y = (anchor[1] - row[1] * anchor[2] + anchor[1] + row[3] * anchor[2]) / 2 * ratio;
  96.         box.w = (row[2] + row[0]) * anchor[2] * ratio;
  97.         box.h = (row[3] + row[1]) * anchor[2] * ratio;
  98.         result.push_back(box);
  99.     }
  100.     NmsDetect(result);
  101.     return result;
  102. }//主要对图片进行预处理,包括resize和归一化
  103. std::vector<float> Detector::prepareImage(cv::Mat &src_img){
  104.     std::vector<float> result(INPUT_W * INPUT_H * 3);
  105.     float *data = result.data();
  106.     float ratio = float(INPUT_W) / float(src_img.cols) < float(INPUT_H) / float(src_img.rows) ? float(INPUT_W) / float(src_img.cols) : float(INPUT_H) / float(src_img.rows);
  107.     cv::Mat flt_img = cv::Mat::zeros(cv::Size(INPUT_W, INPUT_H), CV_8UC3);
  108.     cv::Mat rsz_img = cv::Mat::zeros(cv::Size(src_img.cols*ratio, src_img.rows*ratio), CV_8UC3);
  109.     cv::resize(src_img, rsz_img, cv::Size(), ratio, ratio);
  110.     rsz_img.copyTo(flt_img(cv::Rect(00, rsz_img.cols, rsz_img.rows)));
  111.     flt_img.convertTo(flt_img, CV_32FC3);
  112.     int channelLength = INPUT_W * INPUT_H;
  113.     std::vector<cv::Mat> split_img = {
  114.             cv::Mat(INPUT_W, INPUT_H, CV_32FC1, data + channelLength * 2),
  115.             cv::Mat(INPUT_W, INPUT_H, CV_32FC1, data + channelLength),
  116.             cv::Mat(INPUT_W, INPUT_H, CV_32FC1, data)
  117.     };
  118.     cv::split(flt_img, split_img);
  119.     for (int i = 0; i < 3; i++) {
  120.         split_img[i] = (split_img[i] - img_mean[i]) / img_std[i];
  121.     }
  122.     return result;
  123. }
  124. //加载IR模型,初始化网络
  125. bool Detector::init(string xml_path,double cof_threshold,double nms_area_threshold,int input_w, int input_h, int num_class, int r_rows, int r_cols, std::vector<int> s, std::vector<float> i_mean,std::vector<float> i_std){
  126.     _xml_path = xml_path;
  127.     _cof_threshold = cof_threshold;
  128.     _nms_area_threshold = nms_area_threshold;
  129.     INPUT_W = input_w;
  130.     INPUT_H = input_h;
  131.     NUM_CLASS = num_class;
  132.     refer_rows = r_rows;
  133.     refer_cols = r_cols;
  134.     strides = s;
  135.     img_mean = i_mean;
  136.     img_std = i_std;
  137.     Core ie;
  138.     auto cnnNetwork = ie.ReadNetwork(_xml_path); 
  139.     InputsDataMap inputInfo(cnnNetwork.getInputsInfo());
  140.     InputInfo::Ptr& input = inputInfo.begin()->second;
  141.     _input_name = inputInfo.begin()->first;
  142.     input->setPrecision(Precision::FP32);
  143.     input->getInputData()->setLayout(Layout::NCHW);
  144.     ICNNNetwork::InputShapes inputShapes = cnnNetwork.getInputShapes();
  145.     SizeVector& inSizeVector = inputShapes.begin()->second;
  146.     cnnNetwork.reshape(inputShapes);
  147.     _outputinfo = OutputsDataMap(cnnNetwork.getOutputsInfo());
  148.     for (auto &output : _outputinfo) {
  149.         output.second->setPrecision(Precision::FP32);
  150.     }
  151.     _network =  ie.LoadNetwork(cnnNetwork, "CPU");
  152.     return true;
  153. }
  154. //模型推理及获取输出结果
  155. vector<Detector::Bbox> Detector::process_frame(Mat& inframe){
  156.     cv::Mat showImage = inframe.clone();
  157.     std::vector<float> pr_img = prepareImage(inframe);
  158.     InferRequest::Ptr infer_request = _network.CreateInferRequestPtr();
  159.     Blob::Ptr frameBlob = infer_request->GetBlob(_input_name);
  160.     InferenceEngine::LockedMemory<void> blobMapped = InferenceEngine::as<InferenceEngine::MemoryBlob>(frameBlob)->wmap();
  161.     float* blob_data = blobMapped.as<float*>();
  162.     memcpy(blob_data, pr_img.data(), 3 * INPUT_H * INPUT_W * sizeof(float));
  163.     infer_request->Infer();
  164.     vector<Rect> origin_rect;
  165.     vector<float> origin_rect_cof;
  166.     int i=0;
  167.     vector<Bbox> bboxes;
  168.     for (auto &output : _outputinfo) {
  169.         auto output_name = output.first;
  170.         Blob::Ptr blob = infer_request->GetBlob(output_name);
  171.         LockedMemory<const void> blobMapped = as<MemoryBlob>(blob)->rmap();
  172.         float *output_blob = blobMapped.as<float *>();
  173.         bboxes = postProcess(showImage,output_blob);
  174.         ++i;
  175.     }
  176.     return bboxes;
  177. }
  178. //对模型输出结果进行解码及nms
  179. std::vector<Detector::Bbox> Detector::postProcess(const cv::Mat &src_img,
  180.                               float *output) {
  181.     GenerateReferMatrix();
  182.     std::vector<Detector::Bbox> result;
  183.     float *out = output;
  184.     float ratio = std::max(float(src_img.cols) / float(INPUT_W), float(src_img.rows) / float(INPUT_H));
  185.     cv::Mat result_matrix = cv::Mat(refer_rows, NUM_CLASS + 4, CV_32FC1, out);
  186.     for (int row_num = 0; row_num < refer_rows; row_num++) {
  187.         Detector::Bbox box;
  188.         auto *row = result_matrix.ptr<float>(row_num);
  189.         auto max_pos = std::max_element(row + 4, row + NUM_CLASS + 4);
  190.         box.prob = row[max_pos - row];
  191.         if (box.prob < _cof_threshold)
  192.             continue;
  193.         box.classes = max_pos - row - 4;
  194.         auto *anchor = refer_matrix.ptr<float>(row_num);
  195.         box.x = (anchor[0] - row[0] * anchor[2] + anchor[0] + row[2] * anchor[2]) / 2 * ratio;
  196.         box.y = (anchor[1] - row[1] * anchor[2] + anchor[1] + row[3] * anchor[2]) / 2 * ratio;
  197.         box.w = (row[2] + row[0]) * anchor[2] * ratio;
  198.         box.h = (row[3] + row[1]) * anchor[2] * ratio;
  199.         result.push_back(box);
  200.     }
  201.     NmsDetect(result);
  202.     return result;
  203. }

0x5. 推理时间展示及预测结果展示

平均推理时间6ms左右,CPU下实时推理
安全帽检测结果

至此完成了NanoDet在X86 CPU上的部署,希望有帮助到大家。

本文仅做学术分享,如有侵权,请联系删文。

下载1

在「3D视觉工坊」公众号后台回复:3D视觉即可下载 3D视觉相关资料干货,涉及相机标定、三维重建、立体视觉、SLAM、深度学习、点云后处理、多视图几何等方向。

下载2

在「3D视觉工坊」公众号后台回复:3D视觉github资源汇总即可下载包括结构光、标定源码、缺陷检测源码、深度估计与深度补全源码、点云处理相关源码、立体匹配源码、单目、双目3D检测、基于点云的3D检测、6D姿态估计源码汇总等。

下载3

在「3D视觉工坊」公众号后台回复:相机标定即可下载独家相机标定学习课件与视频网址;后台回复:立体匹配即可下载独家立体匹配学习课件与视频网址。

重磅!3DCVer-学术论文写作投稿 交流群已成立

扫码添加小助手微信,可申请加入3D视觉工坊-学术论文写作与投稿 微信交流群,旨在交流顶会、顶刊、SCI、EI等写作与投稿事宜。

同时也可申请加入我们的细分方向交流群,目前主要有3D视觉CV&深度学习SLAM三维重建点云后处理自动驾驶、多传感器融合、CV入门、三维测量、VR/AR、3D人脸识别、医疗影像、缺陷检测、行人重识别、目标跟踪、视觉产品落地、视觉竞赛、车牌识别、硬件选型、学术交流、求职交流、ORB-SLAM系列源码交流、深度估计等微信群。

一定要备注:研究方向+学校/公司+昵称,例如:”3D视觉 + 上海交大 + 静静“。请按照格式备注,可快速被通过且邀请进群。原创投稿也请联系。

▲长按加微信群或投稿

▲长按关注公众号

3D视觉从入门到精通知识星球:针对3D视觉领域的知识点汇总、入门进阶学习路线、最新paper分享、疑问解答四个方面进行深耕,更有各类大厂的算法工程人员进行技术指导。与此同时,星球将联合知名企业发布3D视觉相关算法开发岗位以及项目对接信息,打造成集技术与就业为一体的铁杆粉丝聚集区,近2000星球成员为创造更好的AI世界共同进步,知识星球入口:

学习3D视觉核心技术,扫描查看介绍,3天内无条件退款

 圈里有高质量教程资料、可答疑解惑、助你高效解决问题

觉得有用,麻烦给个赞和在看~  

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/428890
推荐阅读
相关标签
  

闽ICP备14008679号