【CV学习笔记】 yolov5-6.0之tensorRT 部署

1、前言

原yolov5 github地址为:https://github.com/ultralytics/yolov5

原tensorrtx github地址为:https://github.com/wang-xinyu/tensorrtx

本项目github 地址为:https://github.com/Rex-LK/tensorrt_learning/tree/main/sideline_learn/yolo5-6.0-ros/src/yolov5-6.0,有需要的小伙伴请给个star,感谢!

​ 由于最近工作转到ros上进行开发,同时需要将识别框架进行加速优化,于是需要对yolov5进行加速。本文将简单介绍构造引擎的方法,并对 tensortx 的代码进行封装,以便有需要的小伙伴直接调用,尤其是对于c++不熟且需要用到的小伙伴,再次感谢上面两位大佬提供的开源项目,此文章用于记录日常学习笔记,以及给不太熟悉tensorrt的小伙伴一些帮助,如有错误的地方,还请指正。
欢迎正在学习或者想学的CV的同学进群一起讨论与学习,v:Rex1586662742,q群:468713665

2、构造引擎

​ yolov5到目前为止有6个版本,tensorrtx项目中不同的tag对应yolov5的版本,请务必下载 tensorrtx和yolov5对应的版本,才能运行成功。

​ 首先请下载本项目,然后进入到yolov5-6.0文件夹中,将训练好权重放入weights文件夹中,本文以官方自带的yolov5s 为例。

​ 在终端中输入 python gen_wts.py --w weight/yolov5s.pt --o ./

​ 其中 --w 为训练好的模型路径 --o 为输出的.wts文件,等待一段时间,在yolov5-6.0文件中将会产生一个 yolov5s.wts的中间文件,这个文件保存了模型的所有参数。然后将yolov5s.wts文件移动到tensorrtx/engine文件夹中。

​ 接下来,就要将产生的wts文件转化为engine文件了,首先根据模型的类别数量,修改yololayer.h中的num_classes 的数量,本文所使用的类别数量为80.

然后修改CMakeLists.txt

//cuda 路径
include_directories(/usr/local/cuda/include)
link_directories(/usr/local/cuda/lib64)
//tensorrt 路径
include_directories(/usr/local/TensorRT/include/)
link_directories(/usr/local/TensorRT/targets/x86_64-linux-gnu/lib/)

​ 上述修改完毕之后,在tensorrtx文件夹下新建build文件夹 ,输入 cmake … 以及cmake -j ,在build文件夹下将会出现yolov5 以及detect两个执行文件,如果没报错,那就十分nice了。

​ 在这里先说yolov5,这个可执行文件包含了两个功能,构造引擎以及对文件夹中的图片进行简单预测。那么就开始进行转化吧,在终端输入

​ ./yolov5 -s …/engine/yolov5s.wts …/engine/yolov5s.engine s

​ 其中 -s 为构造引擎的选项,最后的s为模型的大小,可根据模型的大小进行替换。等待一会,在engine文件夹中就出现了 yolov5s.engine 文件,如果出现错误,可能的原因为 tensorrtx和yolov5 的版本没对应,或者tensorrt的版本和cuda、cudnn版本没有对应。

​ engine生成后,可以使用yolov5这个可执行文件简单的看一下加速后的效果,在终端中执行

​ ./yolov5s -d ./engine/yolov5s.engine …/sample

​ 其中 -d 选项为预测选项,…/samlpe为图片路基,这里简单看一下效果。

​ 构造完引擎之后,发现,如果小伙伴需要对视频流进行实时检测,该怎么对原作者的代码进行修改呢,接下来,本文将对源码进行封装,并且添加部分函数,极大简化了engine的调用。

3、yolov5_detect.h

​ 为了更加简单的调用engine文件,本项目对源码进行了封装,在yolov5_detect.h文件中声明了yolov5 的检测类,在构造函数中加载引擎,在成员函数中进行预测。接下来直接看代码。

class yolov5
{
public:
    yolov5(std::string engine_name)
    {
        this->engine_name = engine_name;
        std::ifstream file(engine_name, std::ios::binary);
        size_t size = 0;
        file.seekg(0, file.end);
        size = file.tellg();
        file.seekg(0, file.beg);
        this->trtModelStream = new char[size];
        assert(this->trtModelStream);
        file.read(this->trtModelStream, size);
        file.close();
        std::vector<std::string> file_names;

        this->runtime = createInferRuntime(gLogger);
        assert(runtime != nullptr);
        this->engine = runtime->deserializeCudaEngine(trtModelStream, size);
        assert(engine != nullptr);
        this->context = engine->createExecutionContext();
        assert(context != nullptr);
        this->m_InputBindingIndex = engine->getBindingIndex(INPUT_BLOB_NAME);
        delete[] trtModelStream;

        CUDA_CHECK(cudaMalloc(&buffers[0], 3 * INPUT_H * INPUT_W * sizeof(float)));
        CUDA_CHECK(cudaMalloc(&buffers[1], OUTPUT_SIZE * sizeof(float)));

        CUDA_CHECK(cudaMalloc((void **)&d_resized_img, 3 * INPUT_H * INPUT_H * sizeof(unsigned char)));
        CUDA_CHECK(cudaMalloc((void **)&d_norm_img, sizeof(float) * INPUT_H * INPUT_H * 3));
    }
    ~yolov5()
    {
        context->destroy();
        engine->destroy();
        runtime->destroy();

        CUDA_CHECK(cudaFree(buffers[0]));
        CUDA_CHECK(cudaFree(buffers[1]));
    }
    bool detect(unsigned char *d_roi_image, int roi_w, int roi_h,cv::Mat &img)
    {
        float image_ratio = roi_w > roi_h ? float(INPUT_W) / float(roi_w) : float(INPUT_H) / float(roi_h);
        int width_out = roi_w > roi_h ? INPUT_W : (int)(roi_w * image_ratio);
        int height_out = roi_w < roi_h ? INPUT_H : (int)(roi_h * image_ratio);
        cudaMemset(d_resized_img, 0, sizeof(unsigned char) * INPUT_H * INPUT_W * 3);
        PicResize(d_roi_image, d_resized_img, roi_w, roi_h, width_out,
                   height_out);
        PicNormalize(d_resized_img, d_norm_img, INPUT_W, INPUT_H);

        cudaStream_t stream;
        CUDA_CHECK(cudaStreamCreate(&stream));
        buffers[0] = d_norm_img;
        this->context->enqueue(1, this->buffers, stream, nullptr);
        CUDA_CHECK(cudaMemcpyAsync(this->out_put, this->buffers[1], OUTPUT_SIZE * sizeof(float), cudaMemcpyDeviceToHost, stream));
        cudaStreamSynchronize(stream);
        cudaStreamDestroy(stream);
        std::vector<std::vector<Yolo::Detection>> batch_res(1);
        std::vector<Yolo::Detection> &res = batch_res[0];
        nms(res, &out_put[0], CONF_THRESH, NMS_THRESH);

        for (size_t j = 0; j < res.size(); j++)
        {
            cv::Rect r = get_rect(roi_w, roi_h, res[j].bbox);
            cv::rectangle(img, r, cv::Scalar(0x27, 0xC1, 0x36), 1);
            cv::putText(img, std::to_string((int)res[j].class_id), cv::Point(r.x, r.y + 5), cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar(0xFF, 0xFF, 0xFF), 2);
        }
        if(res.size()>0)
            return true;
        else
            return false;
    };

private:
    std::string engine_name;
    IRuntime *runtime;
    ICudaEngine *engine;
    IExecutionContext *context;
    char *trtModelStream = nullptr;
    cudaStream_t m_CudaStream;
    int m_InputBindingIndex;
    std::vector<void *> m_DeviceBuffers;
    float out_put[OUTPUT_SIZE];
    void *buffers[2];

    unsigned char *d_resized_img;
    float *d_norm_img;
};

至此,yolov5-6,0-tensorrt 的检测接口已经检测全部完成了,那么如何进行调用呢?接下来通过一个简单的例子来展示yolov5检测接口的使用方法。

int main(int argc, char **argv)
{
    cudaSetDevice(0);

    std::string engine_name = "yolov5s.engine";
    yolov5 *det = new yolov5(engine_name);

    cv::Mat img = cv::imread("1.jpg");
    int w = img.cols;
    int h = img.rows;
    unsigned char *d_image;
    cudaMalloc((void **)&d_image, sizeof(unsigned char) * w * h * 3);
    cudaMemcpy(d_image, img.data, w * h * 3 * sizeof(unsigned char),cudaMemcpyHostToDevice);

    bool flag = det->detect(d_image, w, h,img);

    cudaFree(d_image);
    return 0;
}

预测结果展示

CMakeLists.txt 文件修改 这里也可以 将yolov5-detect.h修改成动态库的形式 ,完整请见项目中的CMakelists

cuda_add_library(myplugins SHARED src/yololayer.cu)
target_link_libraries(myplugins nvinfer cudart)

cuda_add_library(mybasic SHARED src/basic_transform.cu)
target_link_libraries(mybasic nvinfer cudart)


#构造引擎
cuda_add_executable(build_engine src/calibrator.cpp src/build_engine.cpp src/preprocess.cu)
target_link_libraries(build_engine nvinfer cudart  myplugins  ${OpenCV_LIBS})

#预测demo
cuda_add_executable(detect  src/detect.cpp src/preprocess.cu)
target_link_libraries(detect nvinfer cudart  myplugins mybasic  ${OpenCV_LIBS})

if(UNIX)
add_definitions(-O2 -pthread)
endif(UNIX)

3、总结

​ 本项目通过封装源代码,构造了一个 yolov5-tensorrt 的检测接口,极大简化了tenosrrt的使用步骤,同时在项目过程中,在本项目中,接触到c++ 、CUDA编程、cmake编写规则,希望能够帮助到有需要的小伙伴,欢迎star。

  • 11
    点赞
  • 84
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 22
    评论
是的,部署YOLOv7到TensorRT的代码可以在GitHub上找到。以下是链接:https://github.com/WongKinYiu/yolov7-tensorrt 该仓库提供了YOLOv7的PyTorch实现,同时也提供了将YOLOv7模型转换为TensorRT格式的代码。在该仓库中,你可以找到以下文件: - `yolov7.py`:YOLOv7的PyTorch实现代码 - `tensorrt.py`:将YOLOv7模型转换为TensorRT格式的代码 - `infer.py`:使用TensorRT格式的模型进行推理的Python脚本 你可以按照以下步骤来使用这些代码: 1. 克隆该仓库并安装相关依赖。 ``` git clone https://github.com/WongKinYiu/yolov7-tensorrt.git cd yolov7-tensorrt pip install -r requirements.txt ``` 2. 下载YOLOv7 PyTorch模型的预训练权重,并将其转换为ONNX格式。 ``` python3 yolov7.py --weights yolov7.pt --cfg cfg/yolov7.yaml --img-size 640 --onnx ``` 3. 将ONNX模型转换为TensorRT格式。 ``` python3 tensorrt.py --onnx yolov7.onnx --engine yolov7.trt --batch 1 ``` 4. 使用TensorRT格式的模型进行推理。 ``` python3 infer.py --engine yolov7.trt --image ./data/samples/zidane.jpg ``` 以上步骤中,`--weights`参数指定了YOLOv7模型的预训练权重,`--cfg`参数指定了YOLOv7模型的配置文件,`--img-size`参数指定了输入图片的大小,`--onnx`参数将模型转换为ONNX格式。`--onnx`参数会生成一个名为`yolov7.onnx`的文件。`--engine`参数将ONNX模型转换为TensorRT格式,并生成一个名为`yolov7.trt`的文件。`--batch`参数指定了推理时的批次大小。`--image`参数指定了要进行推理的图片路径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rex久居

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值