c++平台下利用openvino对YOLOv5进行加速

此文为毕设内容的一部分,分为两个大块:(1)YOLO模块 (2) openvino模块
下面分别介绍两个模块的安装流程。

一、YOLO

1、环境配置

此次任务我采用的是yolov5算法,代码地址如下:https://github.com/ultralytics/yolov5
首先要把环境配置好,在本机终端输入pip install -r requirements.txt
若在colab则 添加代码块为:

git clone https://github.com/ultralytics/yolov5  
cd yolov5
pip install -r requirements.txt  # install

执行即可,至此环境配置完成。
2、数据集构建
(1)在 yolov5目录下 新建文件夹 VOCData(可以自定义命名)
、92c++平台下利用openvino对YOLOv5进行加速_第1张图片
(2)在VOCData下新建两个文件夹 Annotations 以及 images
images:用于存放要标注的图片(jpg格式)
Annotations :用于存放标注图片后产生的内容(这里采用XML格式)
然后使用labellmg进行数据集标注,标注完后会生成XML文件。
(3)在VOCData目录下创建程序 split_train_val.py 并运行

# coding:utf-8

import os
import random
import argparse

parser = argparse.ArgumentParser()
#xml文件的地址,根据自己的数据进行修改 xml一般存放在Annotations下
parser.add_argument('--xml_path', default='Annotations', type=str, help='input xml label path')
#数据集的划分,地址选择自己数据下的ImageSets/Main
parser.add_argument('--txt_path', default='ImageSets/Main', type=str, help='output txt label path')
opt = parser.parse_args()

trainval_percent = 1.0  # 训练集和验证集所占比例。 这里没有划分测试集
train_percent = 0.8    # 训练集所占比例,可自己进行调整
xmlfilepath = opt.xml_path
txtsavepath = opt.txt_path
total_xml = os.listdir(xmlfilepath)
if not os.path.exists(txtsavepath):
    os.makedirs(txtsavepath)

num = len(total_xml)
list_index = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list_index, tv)
train = random.sample(trainval, tr)

file_trainval = open(txtsavepath + '/trainval.txt', 'w')
file_test = open(txtsavepath + '/test.txt', 'w')
file_train = open(txtsavepath + '/train.txt', 'w')
file_val = open(txtsavepath + '/val.txt', 'w')

for i in list_index:
    name = total_xml[i][:-4] + '\n'
    if i in trainval:
        file_trainval.write(name)
        if i in train:
            file_train.write(name)
        else:
            file_val.write(name)
    else:
        file_test.write(name)

file_trainval.close()
file_train.close()
file_val.close()
file_test.close()

运行完毕后 会生成 ImagesSets\Main 文件夹,且在其下生成 测试集、训练集、验证集,存放图片的名字(无后缀.jpg)由于没有分配测试集,所以测试集为空。若要分配,更改第 14、15 行代码,更改所在比例即可。c++平台下利用openvino对YOLOv5进行加速_第2张图片

(4)在VOCData目录下创建程序 text_to_yolo.py 并运行
需要将第 7 行改成自己所标注的类别 以及修改各绝对路径

# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
import os
from os import getcwd

sets = ['train', 'val', 'test']
classes = ["light", "post"]   # 改成自己的类别
abs_path = os.getcwd()
print(abs_path)

def convert(size, box):
    dw = 1. / (size[0])
    dh = 1. / (size[1])
    x = (box[0] + box[1]) / 2.0 - 1
    y = (box[2] + box[3]) / 2.0 - 1
    w = box[1] - box[0]
    h = box[3] - box[2]
    x = x * dw
    w = w * dw
    y = y * dh
    h = h * dh
    return x, y, w, h

def convert_annotation(image_id):
    in_file = open('D:/Yolov5/yolov5/VOCData/Annotations/%s.xml' % (image_id), encoding='UTF-8')
    out_file = open('D:/Yolov5/yolov5/VOCData/labels/%s.txt' % (image_id), 'w')
    tree = ET.parse(in_file)
    root = tree.getroot()
    size = root.find('size')
    w = int(size.find('width').text)
    h = int(size.find('height').text)
    for obj in root.iter('object'):
        difficult = obj.find('difficult').text
        #difficult = obj.find('Difficult').text
        cls = obj.find('name').text
        if cls not in classes or int(difficult) == 1:
            continue
        cls_id = classes.index(cls)
        xmlbox = obj.find('bndbox')
        b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
             float(xmlbox.find('ymax').text))
        b1, b2, b3, b4 = b
        # 标注越界修正
        if b2 > w:
            b2 = w
        if b4 > h:
            b4 = h
        b = (b1, b2, b3, b4)
        bb = convert((w, h), b)
        out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')

wd = getcwd()
for image_set in sets:
    if not os.path.exists('D:/Yolov5/yolov5/VOCData/labels/'):
        os.makedirs('D:/Yolov5/yolov5/VOCData/labels/')
    image_ids = open('D:/Yolov5/yolov5/VOCData/ImageSets/Main/%s.txt' % (image_set)).read().strip().split()
   
    if not os.path.exists('D:/Yolov5/yolov5/VOCData/dataSet_path/'):
        os.makedirs('D:/Yolov5/yolov5/VOCData/dataSet_path/')
     
    list_file = open('dataSet_path/%s.txt' % (image_set), 'w')
    # 这行路径不需更改,这是相对路径
    for image_id in image_ids:
        list_file.write('D:/Yolov5/yolov5/VOCData/images/%s.jpg\n' % (image_id))
        convert_annotation(image_id)
    list_file.close()

运行后会生成如下 labels 文件夹和 dataSet_path 文件夹。

其中 labels 中为不同图像的标注文件。每个图像对应一个txt文件,文件每一行为一个目标的信息,包括class, x_center, y_center, width, height格式,这种即为 yolo_txt格式

c++平台下利用openvino对YOLOv5进行加速_第3张图片
dataSet_path文件夹包含三个数据集的txt文件,train.txt等txt文件为划分后图像所在位置的路径,如train.txt就含有所有训练集图像的路径。
c++平台下利用openvino对YOLOv5进行加速_第4张图片
3、配置yaml文件
在 yolov5 目录下的 data 文件夹下 新建一个 myvoc.yaml文件(可以自定义命名),用记事本打开。

内容是:

训练集以及验证集(train.txt和val.txt)的路径(可以改为相对路径)

以及 目标的类别数目和类别名称。
c++平台下利用openvino对YOLOv5进行加速_第5张图片
还需要在model文件夹中,选择一个自己需要的模型,我这里选择的是yolov5s,把nc改成自己所标注的类别 nc = 1
c++平台下利用openvino对YOLOv5进行加速_第6张图片
4、训练
然后运行train.py即可。注意train.py中的参数。

weights:权重文件路径
cfg:存储模型结构的配置文件.
data:存储训练、测试数据的文件
epochs:指的就是训练过程中整个数据集将被迭代(训练)了多少次
batch-size:训练完多少张图片才进行权重更新,显卡不行就调小点。
img-size:输入图片宽高,显卡不行就调小点。
device:cuda device, i.e. 0 or 0,1,2,3 or cpu。选择使用GPU还是CPU
workers:线程数。默认是8。

修改自己参数和文件路径后在运行即可。
训练后得到best.pt模型,采用以下命令运行

python detect.py --weights runs/train/exp/weights/best.pt --source …/data/0001.png

source后接需要检测的图片、文件夹、视频等。我们在此还可以先把pt模型转换成onnx模型,直接运行export.py文件,需要改路径如下c++平台下利用openvino对YOLOv5进行加速_第7张图片
选择所需要的模型onnx 后面openvino会用到c++平台下利用openvino对YOLOv5进行加速_第8张图片
转换完成后会有best.onnx模型文件。至此yolo可以告一段落。

二、基于Visual Studio配置OpenVINO C++开发环境

主要步骤有:

  1. 下载并安装 Visual Studio Community 版
  2. 下载并解压 OpenVINO Runtime
  3. 下载并解压 OpenCV
  4. 在 Visual Studio 中配置项目属性
  5. 运行 OpenVINO C++ 范例程序,测试开发环境
    2.1、下载并安装 Visual Studio Community 版
    Microsoft Visual Studio(简称VS)是 Windows 平台上非常好用的集成开发环境(IDE),其 Community 版本免费供学生、开放源代码参与者和个人使用。从下方地址下载 Visual Studio 2022 Community 版:
https://visualstudio.microsoft.com/zh-hans/downloads/

2.2下载并解压 OpenVINO Runtime

OpenVINO™ Runtime 2022.3 以压缩包(OpenVINO Archives)的形式提供,里面包含了开发 OpenVINO C++ 推理程序所必需的文件首先,打开下面的链接,下载 OpenVINO™ Runtime 2022.3 压缩包:
https://www.intel.com/content/www/us/en/developer/tools/openvino-toolkit/download.html
然后,手动新建 C:\Program Files (x86)\Intel\openvino_2022.3.0 文件夹,并把压缩包解压后的文件拷贝到 openvino_2022.3.0 文件夹中,完成OpenVINO Runtime 压缩包的下载和解压工作,如图

c++平台下利用openvino对YOLOv5进行加速_第9张图片
2.3下载并解压 OpenCV
OpenCV 也是以压缩包的形式提供,从下方地址下载 Windows 平台上的压缩包:
https://opencv.org/releases/
首先,手动新建 C:\opencv 文件夹;然后,把 OpenCV 解压后的 build 文件夹全部拷贝到 C:\opencv 文件夹,如图 所示,完成 OpenCV 的下载并解压c++平台下利用openvino对YOLOv5进行加速_第10张图片2.4在 Visual Studio 中配置项目属性
在 Visual Studio 中配置项目属性,主要是告诉 C++ 编译器和链接器,OpenVINO™ 和 OpenCV 的头文件以及库文件在哪里。
第一步,打开 Visual Studio,在“文件(F)”菜单中选择“新建(N)→项目§”,新建一个 C++ 空项目。
c++平台下利用openvino对YOLOv5进行加速_第11张图片
第二步,在“文件(F)菜单”中选择“新建(N)→文件(F)”,新建一个 main.cpp 文件,并添加到项目管理器的源文件文件夹中。
c++平台下利用openvino对YOLOv5进行加速_第12张图片
第三步,在“解决方案资源管理器”中,右键单击项目名称,在右键菜单中选择“属性®”,启动“属性页”对话框。
在“配置”栏选择“所有配置”,在“平台§”栏选择“所有平台”;
在“输出目录”输入
“$(SolutionDir)bin$(Platform)$(Configuration)\”;
在“中间目录”输入
“$(SolutionDir)Intermediate$(Platform)$(Configuration)\”
c++平台下利用openvino对YOLOv5进行加速_第13张图片
第四步,在“属性页”对话框中选中“C/C++→常规”;在“平台§”栏选择“x64”;在“附加包含目录”中输入:
C:\Program Files (x86)\Intel\openvino_2022.3.0\runtime\include
C:\opencv\build\include
c++平台下利用openvino对YOLOv5进行加速_第14张图片
第五步,在“属性页”对话框中选中“链接器→常规”;在“平台§”栏选择“x64”;在“附加库目录”中输入:

C:\Program Files (x86)\Intel\openvino_2022.3.0\runtime\lib\intel64$(Configuration)
C:\opencv\build\x64\vc16\lib
此处路径要具体看自己的解压位置和bin位置,并不唯一,文中写的只是我的路径。c++平台下利用openvino对YOLOv5进行加速_第15张图片
第六步,在“属性页”对话框中选中“链接器→输入”,在“配置©”栏选择“Debug”,在“平台§”栏选择“x64”,然后在“附加依赖项”中添加:
openvinod.lib;opencv_world470d.lib;
将“配置”栏改为“Release”,然后在“附加依赖项”中添加:
openvino.lib;opencv_world470.lib;
c++平台下利用openvino对YOLOv5进行加速_第16张图片
此处也要看自己的C:\opencv\build\x64\vc16\lib(也有可能是x64\vc15\lib)路径下的文件名,不可一股脑复制 比如别人此路径下就为opencv_world3415 所以附加项依赖要改成3415.lib。
到此,完成在 Visual Studio 中配置 OpenVINO C++ 推理计算项目属性。

2.51.5 运行 OpenVINO C++ 范例程序
注意此代码中有图片路径需修改

#include 
#include 
#include  //openvino header file
#include     //opencv header file
int main(int argc, char* argv[]) {
    // -------- Get OpenVINO runtime version --------
    std::cout << ov::get_openvino_version().description << ':' << ov::get_openvino_version().buildNumber << std::endl;
    // -------- Step 1. Initialize OpenVINO Runtime Core --------
    ov::Core core;
    // -------- Step 2. Get list of available devices --------
    std::vector<std::string> availableDevices = core.get_available_devices();
    // -------- Step 3. Query and print supported metrics and config keys --------
    std::cout << "Available devices: " << std::endl;
    for (auto&& device : availableDevices) {
        std::cout << device << std::endl;
    }
    // -------- Step 4. Read a picture file and show by OpenCV --------
    cv::Mat img = cv::imread("zidane.jpg"); //Load a picture into memory
    cv::imshow("Test OpenVINO & OpenCV IDE", img);
    std::cout << "Image width: " << img.cols << " height: " << img.rows << std::endl;
    cv::waitKey(0);
    cv::destroyAllWindows();
    return 0;
}

运行后得到c++平台下利用openvino对YOLOv5进行加速_第17张图片
表示环境配置完成。

yolo+openvino

在这里直接给出代码

#include 
#include 

using namespace std;
using namespace ov;
using namespace cv;

// 数据集的标签
vector<string> class_names = { "bad" };

//模型文件路径
string model_file = "D:/yolov5_master/best.onnx";
//测试图片路径
string image_file = "D:/yolov5_master/00036.png";

int main(int argc, char** argv) {
	//1.创建OpenVINO Runtime Core对象
	Core core;

	//2.载入并编译模型
	CompiledModel compiled_model = core.compile_model(model_file, "GPU");

	//3.创建推理请求
	InferRequest infer_request = compiled_model.create_infer_request();

	//4.设置模型输入
	//4.1 获取模型输入节点形状
	Tensor input_node = infer_request.get_input_tensor();
	Shape tensor_shape = input_node.get_shape();

	//4.2读取图片并按照模型输入要求进行预处理
	Mat frame = imread(image_file, IMREAD_COLOR);
	//Lettterbox resize is the default resize method in YOLOv5.
	int w = frame.cols;
	int h = frame.rows;
	int _max = max(h, w);
	Mat image = Mat::zeros(Size(_max, _max), CV_8UC3);
	Rect roi(0, 0, w, h);
	frame.copyTo(image(roi));
	//交换RB通道
	cvtColor(image, image, COLOR_BGR2RGB);
	//计算缩放因子
	size_t num_channels = tensor_shape[1];
	size_t height = tensor_shape[2];
	size_t width = tensor_shape[3];
	float x_factor = image.cols / width;
	float y_factor = image.rows / height;

	int64 start = cv::getTickCount();
	//缩放图片并归一化
	Mat blob_image;
	resize(image, blob_image, cv::Size(width, height));
	blob_image.convertTo(blob_image, CV_32F);
	blob_image = blob_image / 255.0;

	// 4.3 将图像数据填入input tensor
	Tensor input_tensor = infer_request.get_input_tensor();
	// 获取指向模型输入节点数据块的指针
	float* input_tensor_data = input_tensor.data<float>();
	// 将图片数据填充到模型输入节点中
	// 原有图片数据为 HWC格式,模型输入节点要求的为 CHW 格式
	for (size_t c = 0; c < num_channels; c++) {
		for (size_t h = 0; h < height; h++) {
			for (size_t w = 0; w < width; w++) {
				input_tensor_data[c * width * height + h * width + w] = blob_image.at<Vec<float, 3>>(h, w)[c];
			}
		}
	}

	// 5.执行推理计算
	infer_request.infer();

	// 6.处理推理计算结果
	// 6.1 获得推理结果
	const ov::Tensor& output = infer_request.get_tensor("output0");
	const float* output_buffer = output.data<const float>();

	// 6.2 解析推理结果,YOLOv5 output format: cx,cy,w,h,score
	int out_rows = output.get_shape()[1]; //获得"output"节点的rows
	int out_cols = output.get_shape()[2]; //获得"output"节点的cols
	Mat det_output(out_rows, out_cols, CV_32F, (float*)output_buffer);

	vector<cv::Rect> boxes;
	vector<int> classIds;
	vector<float> confidences;

	for (int i = 0; i < det_output.rows; i++) {
		float confidence = det_output.at<float>(i, 4);
		if (confidence < 0.5) {
			continue;
		}
		Mat classes_scores = det_output.row(i).colRange(0, 2);
		Point classIdPoint;
		double score;
		minMaxLoc(classes_scores, 0, &score, 0, &classIdPoint);

		// 置信度 0~1之间
		if (score > 0.5)
		{
			float cx = det_output.at<float>(i, 0);
			float cy = det_output.at<float>(i, 1);
			float ow = det_output.at<float>(i, 2);
			float oh = det_output.at<float>(i, 3);
			int x = static_cast<int>((cx - 0.5 * ow) * x_factor);
			int y = static_cast<int>((cy - 0.5 * oh) * y_factor);
			int width = static_cast<int>(ow * x_factor);
			int height = static_cast<int>(oh * y_factor);
			Rect box;
			box.x = x;
			box.y = y;
			box.width = width;
			box.height = height;

			boxes.push_back(box);
			classIds.push_back(classIdPoint.x);
			confidences.push_back(score);
		}
	}
	// NMS
	vector<int> indexes;
	dnn::NMSBoxes(boxes, confidences, 0.25, 0.45, indexes);
	for (size_t i = 0; i < indexes.size(); i++) {
		int index = indexes[i];
		int idx = classIds[index];
		rectangle(frame, boxes[index], Scalar(0, 0, 255), 2, 8);
		rectangle(frame, Point(boxes[index].tl().x, boxes[index].tl().y - 20),
			Point(boxes[index].br().x, boxes[index].tl().y), Scalar(0, 255, 255), -1);
		putText(frame, class_names[idx], Point(boxes[index].tl().x, boxes[index].tl().y - 10), FONT_HERSHEY_SIMPLEX, .5, Scalar(0, 0, 0));
	}

	// 计算FPS
	float t = (getTickCount() - start) / static_cast<float>(getTickFrequency());
	cout << "Infer time(ms): " << t * 1000 << "ms; Detections: " << indexes.size() << endl;
	putText(frame, format("FPS: %.2f", 1.0 / t), Point(20, 40), FONT_HERSHEY_PLAIN, 2.0, Scalar(255, 0, 0), 2, 8);
	imshow("YOLOv5-6.1 + OpenVINO 2022.1 C++ Demo", frame);

	waitKey(0);
	destroyAllWindows();
	return 0;
}

运行后得到结果
c++平台下利用openvino对YOLOv5进行加速_第18张图片

onnx模型经过剪枝后可得到IR模型,因笔者能力有限,IR模型暂未调试成功,希望后续会取得进步。

你可能感兴趣的:(python,c++,openvino)