tensorrt+yolov5dll部署

上一篇博文将yolov5利用tensorrt部署的环境和操作流程进行了大概介绍,由于在工业应用现场更多的是通过ui界面调用后台检测模型实现最终目标检测功能,因此,为了更方便的将yolov5经加速后的模型部署到现场,这个博文记录一下自己将yolov5编译为dll动态库与ui界面之间调用的过程。

1、使用yolov5s.engine加速

利用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是该文件夹下的一个文件夹,里面为测试图像。在实际应用中,可以根据实际需求将路径进行修改。推理过程下图所示

上述过程在上一个博文已经详细介绍过,因此,这里仅仅是介绍一下最后推理的相关过程。

2、生成yolov5的dll文件

其实归根结底的说,生成yolov5.dll文件是在上述生成yolov5.exe基础上进行。在利用cmake生成yolov5.exe时,项目下最重要的文件为yolov5.cpp文件,该cpp文件内详细封装好了利用wts文件生成engine文件的方法,以及yolov5的整个网络架构和预处理过程(包括图像的resize和通道转换),当然其中也封装好了cuda现存释放和predict的方法。因此,生成yolov5.dll其实就是在此基础上进行修改。

2.1、生成Yolov5TRTContext.h文件

为了能够更好的区分和代码维护,这里新建了一个头文件,与项目中其他相关文件区分。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文件夹为同一个目录下。

2.2、修改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();
}



2.3、编译器模式修改

修改编译器生成dll文件。在tensorrtx-yolov5-v6.0\yolov5\build目录中通过Visual studio打开yolov5s.sln,设置yolov5模块-》鼠标右键-》属性-》高级-》目标扩展名-》输入**.dll**;同时设置 常规-》配置类型:动态库(.dll)

设置完成后,进行编译。

在Release目录下查看生成的dll文件

至此,yolov5.dll文件就生成成功。

3、Python调用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

执行结果如下

4、C++调用dll文件

创建空项目testYoloDll项目,创建源文件testYolov5Dll.cpp

  • 项目导入Yolov5TRTContext.h;
  • 将yolov5.dll、yolov5.exp、yolov5.lib、yolov5s.engine拷贝至项目目录下

4.1、项目配置

将以下文件替换成自己的文件目录即可。

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

4.2、testYolov5Dll.cpp内容

#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);
}


4.3、运行结果

结语

至此,yolov5.dll文件的生成和利用c++和python调用就完成了。下面博客还会介绍自己利用C#调用yolov5.dll的过程,同时结合一些图像处理库软件。希望大家点赞支持。

  • 20
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: YOLOv5是一种用于目标检测的深度学习算法,它使用了一种称为DLL(Dynamic Link Library)和LIB(Library)的文件格式来扩展其功能。 DLL是一种可以被多个程序共享的文件,它包含了一些被程序调用的函数和数据。在YOLOv5中,DLL文件可以包含一些辅助函数和模型相关的代码,用于加速计算和提高性能。 LIB文件是库文件,它包含了一些可重用代码的二进制形式。在YOLOv5中,LIB文件可以包含一些对于目标检测任务非常重要的函数和数据结构,用于实现算法的核心逻辑和运算。 通过使用DLL和LIB文件,YOLOv5可以更方便地扩展和调用其他的功能和库。例如,可以将YOLOv5与图像处理库、加速库或者其他深度学习库进行结合,以实现更复杂的图像处理任务或者提高目标检测的速度和精度。 总而言之,YOLOv5DLL和LIB文件扩展了算法的功能和性能,可以通过与其他库的结合来实现更多的功能和优化。这些文件为YOLOv5提供了更大的灵活性和可扩展性,使得它成为一种高效和强大的目标检测算法。 ### 回答2: YOLOv5是一种目标检测算法,它使用了DLL和LIB文件来支持其功能。 DLL文件(动态链接库)包含了算法的函数和方法,可以在运行时被程序调用。YOLOv5DLL文件中包含了检测目标的核心算法代码,以及与硬件交互的功能,如GPU加速等。通过使用DLL文件,我们可以在自己的程序中调用YOLOv5的函数,实现目标检测的功能。 LIB文件则用于链接DLL文件。它包含了DLL文件中函数的地址等信息,以便在程序编译时可以引用和使用这些函数。通过将LIB文件与我们的程序进行链接,我们可以直接调用DLL文件中的函数,而无需自己实现算法的细节。 使用YOLOv5DLL和LIB文件,我们可以在自己的程序中轻松集成目标检测功能。只需将DLL和LIB文件与我们的程序一起发布,然后在程序中调用相应的函数即可实现目标检测。这使得我们可以快速地在自己的应用中添加强大的目标检测能力,无需从头开始开发算法。 总之,YOLOv5DLL和LIB文件是支持该算法的关键文件,它们提供了目标检测的核心算法和与硬件交互的功能。通过使用这些文件,我们可以方便地将目标检测功能集成到自己的程序中。 ### 回答3: YOLOv5是一种目标检测算法,其dll lib是指用于支持YOLOv5动态链接库和静态链接库。 dll(lib)是Windows操作系统中可执行代码和数据的分发和共享机制。在YOLOv5中,dll(lib)的作用是提供算法实现的核心函数和数据结构,使得开发者可以在自己的应用程序中引用这些dll(lib),从而使用YOLOv5目标检测功能。 具体而言,dll(lib)包含了YOLOv5算法的实现代码和必要的依赖库。它提供了一个接口,开发者可以使用该接口来调用YOLOv5算法进行图像或视频中的目标检测。通过这个接口,开发者可以传入图像或视频数据,然后获得检测结果,包括目标的边界框和类别标签等信息。 使用YOLOv5dll(lib),可以帮助开发者快速搭建自己的目标检测应用。开发者只需将dll(lib)文件导入项目,然后编写相应的代码调用dll(lib)中提供的函数即可完成目标检测的流程。这样,开发者无需从零开始实现目标检测算法,大大提高了开发效率。 总而言之,YOLOv5dll(lib)提供了一个方便易用的接口,支持开发者在自己的应用程序中集成和使用YOLOv5目标检测算法。它可以减少开发者的工作量,同时提升目标检测的准确性和效率。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值