赞
踩
上一篇博文将yolov5利用tensorrt部署的环境和操作流程进行了大概介绍,由于在工业应用现场更多的是通过ui界面调用后台检测模型实现最终目标检测功能,因此,为了更方便的将yolov5经加速后的模型部署到现场,这个博文记录一下自己将yolov5编译为dll动态库与ui界面之间调用的过程。
利用gen_wts.py文件将yolov5s.pt文件转换为yolov5s.wts文件。转换命令如下:
python gen_wts.py -w yolov5s.pt -o yolov5s.wts
然后利用trnsorrt生成的yolov5.exe将生成的yolov5s.wts文件转换为yolov5s.engine文件。命令如下:
yolov5 -s yolov5s.wts yolov5s.engine s
在生成.engine文件后,便可以使用yolov5.exe调用进行图片推理。推理命令如下:
yolov5 -d yolov5s.engine ./images
上述命令中,yolov5.exe与.engine文件在同一文件夹下,同时images是该文件夹下的一个文件夹,里面为测试图像。在实际应用中,可以根据实际需求将路径进行修改。推理过程下图所示
上述过程在上一个博文已经详细介绍过,因此,这里仅仅是介绍一下最后推理的相关过程。
其实归根结底的说,生成yolov5.dll文件是在上述生成yolov5.exe基础上进行。在利用cmake生成yolov5.exe时,项目下最重要的文件为yolov5.cpp文件,该cpp文件内详细封装好了利用wts文件生成engine文件的方法,以及yolov5的整个网络架构和预处理过程(包括图像的resize和通道转换),当然其中也封装好了cuda现存释放和predict的方法。因此,生成yolov5.dll其实就是在此基础上进行修改。
为了能够更好的区分和代码维护,这里新建了一个头文件,与项目中其他相关文件区分。Yolov5TRTContext.h头文件内容
- #pragma once
- #include <iostream>
- #include <chrono>
- #include <cmath>
- #include "cuda_utils.h"
- #include "logging.h"
- #include "common.hpp"
- #include "utils.h"
- #include "calibrator.h"
- #include "preprocess.h"
- #include "macros.h"
- class Yolov5TRTContext {
-
- public:
- float* data;
- float* prob;
- IRuntime* runtime;
- ICudaEngine* engine;
- IExecutionContext* context;
- void* buffers[2];
- cudaStream_t stream;
- int inputIndex;
- int outputIndex;
- };
- extern "C" API void* Init(char* model_path);
- extern "C" API void Detect(void* h, int rows, int cols, unsigned char* src_data, float(*res_array)[6]);
- extern "C" API void cuda_free(void* h);
-
该头文件建立后放置的位置与yolov5.cpp文件夹为同一个目录下。
在 建立好上述头文件后,开始对yolov5.cpp文件进行修改。头文件内声明了3个以标准C格式的接口外放的方法。init为模型初始化方法,detect为模型推理方法,cuda_free为现存释放的方法。因此需要在yolov5.cpp内将上述3个方法的方法体进行实现。可以直接复制下面代码至yolov5.cpp中,也可以自己根据不同点进行修改。yolov5.cpp内容如下:
- #include <iostream>
- #include <chrono>
- #include <cmath>
- #include "cuda_utils.h"
- #include "logging.h"
- #include "common.hpp"
- #include "utils.h"
- #include "calibrator.h"
- #include "preprocess.h"
- #include "macros.h"
-
- #include"Yolov5TRTContext.h" //这里新建立的头文件
- /*
- 下面是头文件对应的3个方法的方法体
- */
- void* Init(char* model_path)
- {
- cudaSetDevice(DEVICE);
- // create a model using the API directly and serialize it to a stream
- char* trtModelStream{ nullptr };
- size_t size_e{ 0 };
- std::string engine_name = model_path;
- std::ifstream file(engine_name, std::ios::binary);
- Yolov5TRTContext* trt = new Yolov5TRTContext();
- if (file.good()) {
- file.seekg(0, file.end);
- size_e = file.tellg();
- file.seekg(0, file.beg);
- trtModelStream = new char[size_e];
- assert(trtModelStream);
- file.read(trtModelStream, size_e);
- file.close();
- }
-
- trt->runtime = createInferRuntime(gLogger);
- assert(trt->runtime != nullptr);
- trt->engine = trt->runtime->deserializeCudaEngine(trtModelStream, size_e);
- assert(trt->engine != nullptr);
- trt->context = trt->engine->createExecutionContext();
- assert(trt->context != nullptr);
- //delete[] trtModelStream;
- assert(trt->engine->getNbBindings() == 2);
- trt->data = new float[BATCH_SIZE * 3 * INPUT_H * INPUT_W];
- trt->prob = new float[BATCH_SIZE * OUTPUT_SIZE];
- trt->inputIndex = trt->engine->getBindingIndex(INPUT_BLOB_NAME);
- trt->outputIndex = trt->engine->getBindingIndex(OUTPUT_BLOB_NAME);
- assert(trt->inputIndex == 0);
- assert(trt->outputIndex == 1);
- // Create GPU buffers on device
- CUDA_CHECK(cudaMalloc(&trt->buffers[trt->inputIndex], BATCH_SIZE * 3 * INPUT_H * INPUT_W * sizeof(float)));
- CUDA_CHECK(cudaMalloc(&trt->buffers[trt->outputIndex], BATCH_SIZE * OUTPUT_SIZE * sizeof(float)));
- // Create stream
- CUDA_CHECK(cudaStreamCreate(&trt->stream));
-
- // In order to bind the buffers, we need to know the names of the input and output tensors.
- // Note that indices are guaranteed to be less than IEngine::getNbBindings()
- return (void*)trt;
- }
-
-
- void Detect(void* h, int rows, int cols, unsigned char* src_data, float(*res_array)[6])
- {
- Yolov5TRTContext* trt = (Yolov5TRTContext*)h;
- cv::Mat img = cv::Mat(rows, cols, CV_8UC3, src_data);
- // prepare input data ---------------------------
- cv::Mat pr_img = preprocess_img(img, INPUT_W, INPUT_H); // letterbox BGR to RGB
- int i = 0;
- for (int row = 0; row < INPUT_H; ++row) {
- uchar* uc_pixel = pr_img.data + row * pr_img.step;
- for (int col = 0; col < INPUT_W; ++col)
- {
- trt->data[0 * 3 * INPUT_H * INPUT_W + i] = (float)uc_pixel[2] / 255.0;
- trt->data[0 * 3 * INPUT_H * INPUT_W + i + INPUT_H * INPUT_W] = (float)uc_pixel[1] / 255.0;
- trt->data[0 * 3 * INPUT_H * INPUT_W + i + 2 * INPUT_H * INPUT_W] = (float)uc_pixel[0] / 255.0;
- uc_pixel += 3;
- ++i;
- }
- }
-
- // Run inference
- doInference(*trt->context, trt->stream, trt->buffers, trt->data, trt->prob, BATCH_SIZE);
-
- std::vector<std::vector<Yolo::Detection>> batch_res(1);
- auto& res = batch_res[0];
- nms(res, &trt->prob[0 * OUTPUT_SIZE], CONF_THRESH, NMS_THRESH);
- int len = res.size();
- for (size_t j = 0; j < res.size(); j++) {
- cv::Rect r = get_rect(img, res[j].bbox);
- res_array[j][0] = r.x;
- res_array[j][1] = r.y;
- res_array[j][2] = r.width;
- res_array[j][3] = r.height;
- res_array[j][4] = res[j].class_id;
- res_array[j][5] = res[j].conf;
- }
- }
-
- void cuda_free(void* h) {
- Yolov5TRTContext* trt = (Yolov5TRTContext*)h;
- cudaStreamDestroy(trt->stream);
- CUDA_CHECK(cudaFree(trt->buffers[trt->inputIndex]));
- CUDA_CHECK(cudaFree(trt->buffers[trt->outputIndex]));
- trt->context->destroy();
- trt->engine->destroy();
- trt->runtime->destroy();
- }
-
-
-
修改编译器生成dll文件。在tensorrtx-yolov5-v6.0\yolov5\build目录中通过Visual studio打开yolov5s.sln,设置yolov5模块-》鼠标右键-》属性-》高级-》目标扩展名-》输入**.dll**;同时设置 常规-》配置类型:动态库(.dll)
设置完成后,进行编译。
在Release目录下查看生成的dll文件
至此,yolov5.dll文件就生成成功。
在上个博客中介绍的,yolov5编译好的yolov5_tensorrt文件夹下有一个名为python_trt.py文件,将该文件放置在生成yolov5.dll文件夹下。放置好如下
放置完成后修改python_trt.py文件中的相关路径。
- from ctypes import *
- import cv2
- import numpy as np
- import numpy.ctypeslib as npct
-
- class Detector():
- def __init__(self,model_path,dll_path):
- #self.yolov5 = CDLL(dll_path)
- self.yolov5 = CDLL(dll_path, winmode=0)#python3.8版本加载dll失败时用
- self.yolov5.Detect.argtypes = [c_void_p,c_int,c_int,POINTER(c_ubyte),npct.ndpointer(dtype = np.float32, ndim = 2, shape = (50, 6), flags="C_CONTIGUOUS")]
- self.yolov5.Init.restype = c_void_p
- self.yolov5.Init.argtypes = [c_void_p]
- self.yolov5.cuda_free.argtypes = [c_void_p]
- self.c_point = self.yolov5.Init(model_path)
-
- def predict(self,img):
- rows, cols = img.shape[0], img.shape[1]
- res_arr = np.zeros((50,6),dtype=np.float32)
- print("res_Arr===",res_arr)
- self.yolov5.Detect(self.c_point,c_int(rows), c_int(cols), img.ctypes.data_as(POINTER(c_ubyte)),res_arr)
- print("res_Arr===",res_arr)
- self.bbox_array = res_arr[~(res_arr==0).all(1)]
- print("bbox===",self.bbox_array)
- return self.bbox_array
-
- def free(self):
- self.yolov5.cuda_free(self.c_point)
-
- def visualize(img,bbox_array):
- for temp in bbox_array:
- bbox = [temp[0],temp[1],temp[2],temp[3]] #xywh
- clas = int(temp[4])
- score = temp[5]
- cv2.rectangle(img,(int(temp[0]),int(temp[1])),(int(temp[0]+temp[2]),int(temp[1]+temp[3])), (105, 237, 249), 2)
- img = cv2.putText(img, "class:"+str(clas)+" "+str(round(score,2)), (int(temp[0]),int(temp[1])-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (105, 237, 249), 1)
- return img
- # 修改模型路径
- det = Detector(model_path=b"./yolov5s.engine",dll_path="./yolov5.dll") # b'' is needed
- # 待检测图像路径
- img = cv2.imread("./images/zidane.jpg")
- result = det.predict(img)
- img = visualize(img,result)
- cv2.imshow("img",img)
- cv2.waitKey(0)
- det.free()
- cv2.destroyAllWindows()
这里要注意的是,yolov5.dll文件加载处,python>3.7时,python对于dll库的调用进行了更为严格的语法更改,因此需要更改一下调用时的参数。更改完成进行代码执行。
python python_trt.py
执行结果如下
创建空项目testYoloDll项目,创建源文件testYolov5Dll.cpp
将以下文件替换成自己的文件目录即可。
VC++目录-》包含目录:
- D:\Space\VisualStudioSpace\tensorrtx-yolov5-v6.0\yolov5
- D:\AITool\TensorRT\TensorRT-8.4.1.5\include
- D:\AITool\CUDA\CUDA_Development\include
- D:\Tool\opencv4.5.1\opencv\build\include\opencv2
- D:\Tool\opencv4.5.1\opencv\build\include
VC++目录-》库目录:
- D:\Tool\opencv4.5.1\opencv\build\x64\vc15\lib
- D:\Space\VisualStudioSpace\tensorrtx-yolov5-v6.0\yolov5\build\Release
- D:\AITool\TensorRT\TensorRT-8.4.1.5\lib
- D:\AITool\CUDA\CUDA_Development\lib\x64
链接器->输入:
- opencv_world451d.lib
- cudart.lib
- cudart_static.lib
- yolov5.lib
- nvinfer.lib
- nvinfer_plugin.lib
- nvonnxparser.lib
- nvparsers.lib
- #pragma once
- #include <iostream>
- #include <opencv2/opencv.hpp>
- #include <opencv2/dnn.hpp>
- #include <opencv2/highgui/highgui.hpp>
- #include <Windows.h>
- #include <iostream>
- #include <string>
- #include <tchar.h>
- #include <time.h>
-
- using namespace std;
- using namespace cv;
-
- void display(Mat dst,vector<vector<float>> list);//显示boundingbox
- //根据自己模型定义 类别
- const static int class_num = 80;
- const static string classes[class_num] = { "person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light",
- "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow",
- "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee",
- "skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard",
- "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
- "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch",
- "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone",
- "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear",
- "hair drier", "toothbrush" };
-
- int main(){
- HMODULE module = LoadLibrary(_T("yolov5.dll"));//显示加载dll
- if (module == NULL)
- {
- cout << "加载yolov5.dll动态库失败" << endl;
- return -1;
- }
- else {
- cout << "加载成功!!!" << endl;
- }
-
- typedef void * (*InitFuc)(char* ); // 定义函数指针类型
- typedef void (*DetectFuc)(void* , int , int , unsigned char* , float(*)[6]); // 定义函数指针类型
- typedef void (*cuda_freeFuc)(void*);
-
- //从dll中加载Init、Detect、cuda_free
- InitFuc Init;
- Init = (InitFuc)GetProcAddress(module,"Init");
- //推理
- DetectFuc Detect;
-
- Detect = (DetectFuc)GetProcAddress(module, "Detect");
- //free
- cuda_freeFuc cuda_free;
- cuda_free = (cuda_freeFuc)GetProcAddress(module, "cuda_free");
-
- char model[] = "yolov5s.engine";//yolov5s.engine 位置
- char* model_path = model;
- float res_arr[50][6] = { 0.0f };//50个anchor 6(x,y,w,h,class,confidence)
- Mat img, dst;
- void* trt = (void*)Init(model_path);//初始化模型
- const char* image_path = "./images/bus.jpg";//图片路径
- img = cv::imread(image_path);//读取图片
- dst = img;
- Detect(trt, img.rows, img.cols, img.data, res_arr);//推理
- vector<vector<float>> list;
- for (int i = 0; i < 50; i++) {
- if (res_arr[i][0] != 0) {
- vector<float> temp;
- temp.push_back(res_arr[i][0]);//x
- temp.push_back(res_arr[i][1]);//y
- temp.push_back(res_arr[i][2]);//w
- temp.push_back(res_arr[i][3]);//h
- temp.push_back(res_arr[i][4]);//class
- temp.push_back(res_arr[i][5]);//confidence
- list.push_back(temp);
- }
- }
- display(img, list);
- waitKey(0);
- img.release();dst.release();
- cuda_free(trt);
- return 0;
- }
- void display(Mat dst, vector<vector<float>> list) {
- //初始化m
- //遍历list
- Scalar scalar(0, 255, 0);//BGR(Green)
- vector<float> temp;
- for (int i = 0; i < list.size(); i++) {
-
- temp = list.at(i);
- float x = temp.at(0);
- float y = temp.at(1);
- float w = temp.at(2);
- float h = temp.at(3);
- int c = (int)temp.at(4);
- float confidence = temp.at(5);
-
- // 在dst上面作图
- //cout << "x=" << x << ",y=" << y << ",w" << w << ",h" << h << ",class=" << c << ",confidence=" << confidence << endl;
- Rect rect(x, y, w, h);//绘制矩形
- rectangle(dst, rect, scalar, 2, LINE_8, 0);
-
- //在dst上添加class confidence
- string text = classes[c] + format(",%0.3f", confidence);
- putText(dst, text, Point2f(x, y + 10), FONT_HERSHEY_SIMPLEX, 0.5, scalar);
- temp.clear();
- }
- namedWindow("yolov5-6.0", WINDOW_AUTOSIZE); //WINDOW_NORMAL
- imshow("yolov5-6.0", dst);
- }
-
-
至此,yolov5.dll文件的生成和利用c++和python调用就完成了。下面博客还会介绍自己利用C#调用yolov5.dll的过程,同时结合一些图像处理库软件。希望大家点赞支持。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。