大数跨境
0
0

实践教程|基于OpenVINO 在C++中部署YOLOv5模型

实践教程|基于OpenVINO 在C++中部署YOLOv5模型 极市平台
2023-02-06
0
↑ 点击蓝字 关注极市平台
作者丨王一凡 英特尔物联网行业创新大使
编辑丨极市平台

极市导读

 

本文详细介绍了如何在C++中使用OpenVINO工具包部署YOLOv5模型的步骤。>>加入极市CV技术交流群,走在计算机视觉的最前沿

目录

1.1 配置OpenVINO C++开发环境
1.2 下载并转换YOLOv5预训练模型
1.3 使用OpenVINO Runtime C++ API编写推理程序
1.3.1 采集图像&图像解码
1.3.2 YOLOv5的图像预处理
1.3.3 执行AI推理计算
1.3.4 推理结果进行后处理
1.4 总结

本文主要介绍在C++中使用OpenVINO工具包部署YOLOv5模型,主要步骤有:

  1. 配置OpenVINO C++开发环境
  2. 下载并转换YOLOv5预训练模型
  3. 使用OpenVINO Runtime C++ API编写推理程序

下面,本文将依次详述

1.1 配置OpenVINO C++开发环境

配置OpenVINO C++开发环境的详细步骤,请参考《在Windows中基于Visual Studio配置OpenVINO C++开发环境》

1.2 下载并转换YOLOv5预训练模型

下载并转换YOLOv5预训练模型的详细步骤,请参考:《基于OpenVINO™2022.2和蝰蛇峡谷优化并部署YOLOv5模型》,本文所使用的OpenVINO是2022.3 LTS版。

完成上述步骤后,可以获得YOLOv5的IR模型文件:yolov5s.xml 和 yolov5s.bin,如下图所示:

图 1-1  YOLOv5 IR模型文件

1.3 使用OpenVINO Runtime C++ API编写推理程序

一个端到端的AI推理程序,主要包含五个典型的处理流程:

  1. 采集图像&图像解码
  2. 图像数据预处理
  3. AI推理计算
  4. 对推理结果进行后处理
  5. 将处理后的结果集成到业务流程

图 1-2  端到端的AI推理程序处理流程

1.3.1采集图像&图像解码

OpenCV提供imread()函数将图像文件载入内存,

Mat cv::imread (const String &filename, int flags=IMREAD_COLOR)

若是从视频流(例如,视频文件、网络摄像头、3D摄像头(Realsense)等)中,一帧一帧读取图像数据到内存,则使用cv::VideoCapture类,对应范例代码请参考OpenCV官方范例代码:https://github.com/opencv/opencv/tree/4.x/samples/cpp。

图 1-3 从视频流读取图像帧范例

1.3.2 YOLOv5的图像预处理

图像数据输入YOLOv5模型前需要做预处理,其主要工作有:使用Letterbox算法对图像进行非变形放缩,然后完成转换颜色通道、归一化数据、更改数据布局和数值精度。

直接调用OpenCV的cv::resize()函数将原始图像按照模型输入要求的尺寸进行放缩,虽然实现起来简单,但会导致图像中的被检测对象变形。Letterbox算法一种不会导致被检测对象变形的缩放,主要步骤为:

  1. 计算宽高缩放比例,选择较小那个缩放系数
  2. 计算缩放后的尺寸,原始图片的长宽都乘以较小的缩放系数
  3. 计算短边需要填充的灰边数,将短边的两边各自填充一半的灰行

参考YOLOv5的Letterbox算法实现方式,本文的Letterbox函数实现如下所示:

cv::Mat letterbox(cv::Mat& img, std::vector<int> new_shape = {640, 640}){
    // Get current image shape [height, width]
    // Refer to https://github.com/ultralytics/yolov5/blob/master/utils/augmentations.py#L111
    int img_h = img.rows; 
    int img_w = img.cols;
    // Compute scale ratio(new / old) and target resized shape
    float scale = std::min(new_shape[1] * 1.0 / img_h, new_shape[0] * 1.0 / img_w);
    int resize_h = int(round(img_h * scale));
    int resize_w = int(round(img_w * scale));
    // Compute padding
    int pad_h = new_shape[1] - resize_h;
    int pad_w = new_shape[0] - resize_w;
    // Resize and pad image while meeting stride-multiple constraints
    cv::Mat resized_img;
    cv::resize(img, resized_img, cv::Size(resize_w, resize_h));
    // divide padding into 2 sides
    float half_h = pad_h * 1.0 / 2;
    float half_w = pad_w * 1.0 / 2;
    // Compute padding boarder
    int top = int(round(half_h - 0.1));
    int bottom = int(round(half_h + 0.1));
    int left = int(round(half_w - 0.1));
    int right = int(round(half_w + 0.1));
    // Add border
    cv::copyMakeBorder(resized_img, resized_img, top, bottom, left, right, 0, cv::Scalar(114, 114, 114));
    return resized_img;
}

letterbox函数的运行结果如下图所示:

图 1-4 letterbox放缩图片的效果

转换颜色通道、归一化数据、更改数据布局和数值精度的操作可以由OpenCV提供的 Mat cv::dnn::blobFromImage()函数实现,或者由OpenVINO的预处理API实现。为了简洁范例代码,本文选择调用cv::dnn::blobFromImage()函数。

1.3.3执行AI推理计算

基于OpenVINO Runtime C++ API实现AI推理计算主要有两种方式:一种是同步推理方式,一种是异步推理方式,本文主要介绍同步推理方式。

主要步骤有:

  1. 初始化Core类
  2. 编译模型
  3. 创建推理请求infer_request
  4. 读取图像数据并做预处理
  5. 将预处理后的blob数据传入模型输入节点
  6. 调用infer()方法执行推理计算
  7. 获得推理结果

基于OpenVINO Runtime C++API的同步推理代码如下所示:

    // -------- Step 1. Initialize OpenVINO Runtime Core --------
    ov::Core core;
    // -------- Step 2. Compile the Model --------
    auto compiled_model = core.compile_model(model_file, "CPU"); //GPU.1 is dGPU A770
    // -------- Step 3. Create an Inference Request --------
    ov::InferRequest infer_request = compiled_model.create_infer_request();
    // -------- Step 4. Read a picture file and do the preprocess --------
    cv::Mat img = cv::imread(image_file); //Load a picture into memory
    std::vector<float> paddings(3);       //scale, half_h, half_w
    cv::Mat resized_img = letterbox(img, paddings); //resize to (640,640) by letterbox
    // BGR->RGB, u8(0-255)->f32(0.0-1.0), HWC->NCHW
    cv::Mat blob = cv::dnn::blobFromImage(resized_img, 1 / 255.0, cv::Size(640, 640), cv::Scalar(0, 0, 0), true);
    // -------- Step 5. Feed the blob into the input node of YOLOv5 -------
    // Get input port for model with one input
    auto input_port = compiled_model.input();
    // Create tensor from external memory
    ov::Tensor input_tensor(input_port.get_element_type(), input_port.get_shape(), blob.ptr(0));
    // Set input tensor for model with one input
    infer_request.set_input_tensor(input_tensor);
    // -------- Step 6. Start inference --------
    infer_request.infer();
    // -------- Step 7. Get the inference result --------
    auto output = infer_request.get_output_tensor(0);
    auto output_shape = output.get_shape();
    std::cout << "The shape of output tensor:"<<output_shape << std::endl;

1.3.4 推理结果进行后处理

对于目标检测应用,后处理主要是执行NMS(非极大值抑制)算法去除多余的检测框,然后剩余的检测框中提取出检测框坐标(box)、置信度(confidence)和类别(class_id)。NMS算法本文直接使用了cv::dnn::NMSBoxes()。

经过后处理,获得了经过NMS过滤后的检测框坐标(box)、置信度(confidence)和类别(class_id)后,就可以将这些信息显示在图像上了。

完整的代码实现,请下载:https://gitee.com/ppov-nuc/yolov5_infer/blob/main/yolov5_openvino_sync_dGPU.cpp

1.4 总结

配置OpenVINO C++开发环境后,可以直接编译运行yolov5_openvino_sync_dGPU.cpp,结果如下图所示。使用OpenVINO Runtime C++ API函数开发YOLOv5推理程序,简单方便,并可以任意部署在英特尔CPU、集成显卡和独立显卡上。

图 1-5 运行结果

公众号后台回复“知识蒸馏”获取知识蒸馏研究综述PDF

极市干货

技术干货损失函数技术总结及Pytorch使用示例深度学习有哪些trick?目标检测正负样本区分策略和平衡策略总结

实操教程GPU多卡并行训练总结(以pytorch为例)CUDA WarpReduce 学习笔记卷积神经网络压缩方法总结

极市原创作者激励计划 #


极市平台深耕CV开发者领域近5年,拥有一大批优质CV开发者受众,覆盖微信、知乎、B站、微博等多个渠道。通过极市平台,您的文章的观点和看法能分享至更多CV开发者,既能体现文章的价值,又能让文章在视觉圈内得到更大程度上的推广,并且极市还将给予优质的作者可观的稿酬!

我们欢迎领域内的各位来进行投稿或者是宣传自己/团队的工作,让知识成为最为流通的干货!

对于优质内容开发者,极市可推荐至国内优秀出版社合作出书,同时为开发者引荐行业大牛,组织个人分享交流会,推荐名企就业机会等。


投稿须知:
1.作者保证投稿作品为自己的原创作品。
2.极市平台尊重原作者署名权,并支付相应稿费。文章发布后,版权仍属于原作者。
3.原作者可以将文章发在其他平台的个人账号,但需要在文章顶部标明首发于极市平台

投稿方式:
添加小编微信Fengcall(微信号:fengcall19),备注:姓名-投稿

点击阅读原文进入CV社区

收获更多技术干货

【声明】内容源于网络
0
0
极市平台
为计算机视觉开发者提供全流程算法开发训练平台,以及大咖技术分享、社区交流、竞赛实践等丰富的内容与服务。
内容 8155
粉丝 0
极市平台 为计算机视觉开发者提供全流程算法开发训练平台,以及大咖技术分享、社区交流、竞赛实践等丰富的内容与服务。
总阅读9.6k
粉丝0
内容8.2k