OpenVINO2021.4+YOLOX目标检测模型部署测试

举报
风吹稻花香 发表于 2021/07/27 01:15:16 2021/07/27
【摘要】 前面写过一篇文章介绍了YOLOX目标检测模型,知道它是基于Pytroch而且类似与YOLOv5目标检测模型,文章链接: 比YOLOv5还厉害的YOLOX来了,官方支持OpenVINO推理 本文基于YOLOX的ONNX模型分别测试了YOLOX-Small与YOLOX-Tiny版本的模型。硬件配置与软件版本: Win10 64位 CPU CORE i7 8thVS20...

前面写过一篇文章介绍了YOLOX目标检测模型,知道它是基于Pytroch而且类似与YOLOv5目标检测模型,文章链接:

比YOLOv5还厉害的YOLOX来了,官方支持OpenVINO推理

本文基于YOLOX的ONNX模型分别测试了YOLOX-Small与YOLOX-Tiny版本的模型。硬件配置与软件版本:

Win10 64位

CPU CORE i7 8thVS2017OpenVINO2021.4
 

模型说明

两个模型的输入与输出格式分别如下:

图片

以YOLOX small版本为例,解释输出的内容是什么,看模型的输出图示如下:

图片

图片

有三个输出层,分别是8倍、16倍、32倍的降采样,输出的8400计算方法为:

80x80+40x40+20x20 = 6400+1600+400=8400
 

分别对应640的8倍、16倍、32倍的降采样大小。85的前四个是cx、cy、w、h大小,第五个是object预测得分,后面80个是COCO类别。

看到这里就知道它跟YOLOv5的解析极其类似。然后它对图象的预测要求如下:

输入通道顺序:RGB、类型浮点数0~1之间输入的均值:0.485f, 0.456f, 0.406f输入的归一化方差:0.229f, 0.224f, 0.225f
 

代码实现部分

首先需要加载模型,从github上下载好对应的模型ONNX格式文件之后,首先通过IECore来加载YOLOX模型,代码如下:


  
  1. std::cout << "YOLOX Demo" << std::endl;
  2. Core ie;
  3. std::vector<std::string> availableDevices = ie.GetAvailableDevices();
  4. for (int i = 0; i < availableDevices.size(); i++) {
  5.     printf("supported device name : %s \n", availableDevices[i].c_str());
  6. }
  7. //  加载检测模型
  8. auto network = ie.ReadNetwork("D:/yolox.onnx");

设置模型的输入与输出,这里需要注意,输入设置为FP32,读取输入与输出层名称,代码如下:


  
  1. // 请求网络输入与输出信息
  2. InferenceEngine::InputsDataMap input_info(network.getInputsInfo());
  3. InferenceEngine::OutputsDataMap output_info(network.getOutputsInfo());
  4. // 设置输入格式
  5. std::string input_name = "";
  6. for (auto &item : input_info) {
  7.     auto input_data = item.second;
  8.     input_name = item.first;
  9.     input_data->setPrecision(Precision::FP32);
  10.     input_data->setLayout(Layout::NCHW);
  11. }
  12. printf("get it \n");
  13. // 设置输出格式
  14. std::string output_name = "";
  15. for (auto &item : output_info) {
  16.     auto output_data = item.second;
  17.     output_name = item.first;
  18.     std::cout <<"output name: "<< item.first << std::endl;
  19.     output_data->setPrecision(Precision::FP32);
  20. }

下面就是生成三个输出层的grid,每个grid上的每个点的坐标信息,后面解析输出数据的时候需要根据index来取每个grid对应的数据


  
  1. // 生成三个输出层的grid与anchor信息
  2. std::vector<int> strides = { 81632 };
  3. std::vector<GridAndStride> grid_strides;
  4. generate_grids_and_stride(IMG_W, strides, grid_strides);

其中generate_grids_and_stride是我借鉴了官方的代码,这部分我感觉是可以省去的,可以从index中直接计算的,也许这样会更快点,暂时我就借用了,该方法的代码如下:


  
  1. const float IMG_W = 640.0f;
  2. struct GridAndStride
  3. {
  4.     int gh;
  5.     int gw;
  6.     int stride;
  7. };
  8. void generate_grids_and_stride(int target_size, std::vector<int>& strides, std::vector<GridAndStride>& grid_strides)
  9. {
  10.     for (auto stride : strides)
  11.     {
  12.         int num_grid = target_size / stride;
  13.         for (int g1 = 0; g1 < num_grid; g1++)
  14.         {
  15.             for (int g0 = 0; g0 < num_grid; g0++)
  16.             {
  17.                 GridAndStride gs;
  18.                 gs.gh = g0;
  19.                 gs.gw = g1;
  20.                 gs.stride = stride;
  21.                 grid_strides.push_back(gs);
  22.             }
  23.         }
  24.     }
  25. }

下面就很容易啦,创建推理请求,开始执行推理,推理的解析部分,代码如下:


  
  1. // 开始推理处理 - 支持图象与视频
  2. cv::Mat image = cv::imread("D:/zidane.jpg");
  3. inferAndOutput(image, grid_strides, input_name, output_name, infer_request);

其中inferAndOutput是我的推理与解析输出结果的方法,该方法首先得到输出,然后根据index来从grid_strides里面查询对应grid的对应位置信息,原来官方的方法比较比较啰嗦,代码不够简洁,我稍微改动了一下,借助OpenVINO中OpenCV自带的NMS函数功能,重新整理一下,改成现在的方法,发现可以降低代码量,提升可读性,该方法的代码如下:


  
  1. void inferAndOutput(cv::Mat &image, std::vector<GridAndStride> &grid_strides, std::string &input_name, std::string &output_name, InferRequest &infer_request) {
  2.     int64 start = cv::getTickCount();
  3.     Blob::Ptr imgBlob = infer_request.GetBlob(input_name);
  4.     float sx = static_cast<float>(image.cols) / IMG_W;
  5.     float sy = static_cast<float>(image.rows) / IMG_W;
  6.     // 推理
  7.     blobFromImage(image, imgBlob);
  8.     infer_request.Infer();
  9.     const Blob::Ptr output_blob = infer_request.GetBlob(output_name);
  10.     const float* outblob = static_cast<PrecisionTrait<Precision::FP32>::value_type*>(output_blob->buffer());
  11.     const SizeVector outputDims = output_blob->getTensorDesc().getDims();
  12.     const int num_anchors = grid_strides.size();
  13.     const int num_class = 80;
  14.     // 处理解析输出结果
  15.     std::vector<cv::Rect> boxes;
  16.     std::vector<int> classIds;
  17.     std::vector<float> confidences;
  18.     for (int anchor_idx = 0; anchor_idx < num_anchors; anchor_idx++)
  19.     {
  20.         const int grid0 = grid_strides[anchor_idx].gh; // H
  21.         const int grid1 = grid_strides[anchor_idx].gw; // W
  22.         const int stride = grid_strides[anchor_idx].stride; // stride
  23.         const int basic_pos = anchor_idx * 85;
  24.         float x_center = (outblob[basic_pos + 0] + grid0) * stride * sx;
  25.         float y_center = (outblob[basic_pos + 1] + grid1) * stride * sy;
  26.         float w = exp(outblob[basic_pos + 2]) * stride * sx;
  27.         float h = exp(outblob[basic_pos + 3]) * stride * sy;
  28.         float x0 = x_center - w * 0.5f;
  29.         float y0 = y_center - h * 0.5f;
  30.         float box_objectness = outblob[basic_pos + 4];
  31.         for (int class_idx = 0; class_idx < num_class; class_idx++)
  32.         {
  33.             float box_cls_score = outblob[basic_pos + 5 + class_idx];
  34.             float box_prob = box_objectness * box_cls_score;
  35.             if (box_prob > 0.25)
  36.             {
  37.                 cv::Rect rect;
  38.                 rect.x = x0;
  39.                 rect.y = y0;
  40.                 rect.width = w;
  41.                 rect.height = h;
  42.                 classIds.push_back(class_idx);
  43.                 confidences.push_back((float)box_prob);
  44.                 boxes.push_back(rect);
  45.             }
  46.         } // class loop
  47.     }
  48.     std::vector<int> indices;
  49.     cv::dnn::NMSBoxes(boxes, confidences, 0.250.5, indices);
  50.     for (size_t i = 0; i < indices.size(); ++i)
  51.     {
  52.         int idx = indices[i];
  53.         cv::Rect box = boxes[idx];
  54.         rectangle(image, box, cv::Scalar(1401990), 480);
  55.     }
  56.     float fps = cv::getTickFrequency() / (cv::getTickCount() - start);
  57.     float time = (cv::getTickCount() - start) / cv::getTickFrequency();
  58.     std::ostringstream ss;
  59.     ss << "FPS : " << fps << " detection time: " << time * 1000 << " ms";
  60.     cv::putText(image, ss.str(), cv::Point(2050), 01.0, cv::Scalar(00255), 2);
  61.     cv::imshow("OpenVINO2021.4+YOLOX Demo@JiaZhiGang", image);
  62. }

运行与测试

首先用YOLOv5的一张测试图象测试一下,基于YOLOX的samll版本模型运行结果如下:

图片

跟YOLOV5 small版本测试结果完成一致,毫无违和感!

视频测试(YOLOX Small版本模型)运行结果如下:

图片

感觉没有YOLOv5的small版本推理速度快(在我的机器上)!还需进一步优化输出解析代码。

视频测试(YOLOX Tiny版本模型)运行结果如下:

图片

CPU果然可以30+ FPS的。

文章来源: blog.csdn.net,作者:AI视觉网奇,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/jacke121/article/details/119114550

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。