yolo-inference多后端+多任务+多算法+多精度模型 框架开发记录(cpp版)

本文主要是介绍yolo-inference多后端+多任务+多算法+多精度模型 框架开发记录(cpp版),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

先贴出github地址,欢迎大家批评指正:https://github.com/taifyang/yolo-inference
不知不觉LZ已经快工作两年了,由于之前的工作内容主要和模型部署相关,想着利用闲暇时间写一些推理方面的经验总结,于是有了这个工程。其实本来也是自己写了玩的,不过已经陆续迭代半年多了,期间也通过借签优秀代码吸收了经验,索性总结一下心得~

1.0 初始版本
1.1 支持多精度模型
1.2 支持tensorrt的cuda前后处理
1.3 支持onnxruntime的int8推理
1.4 onnxruntime推理代码采用cpp风格接口
1.5 采用抽象工厂和单例模式重构代码
1.6 增加cmake编译支持
1.7 增加Linux系统编译支持
2.0 增加yolov8检测器支持
2.1 增加cmake条件编译选项和自动化测试脚本
3.0 增加分类和分割算法支持
3.1 重构代码结构和缺陷修复

最初版的接口头文件部分主要如下:

#pragma once#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>...enum Device_Type
{CPU,GPU,
};class YOLOv5
{
public:void infer(const std::string image_path){m_image = cv::imread(image_path);m_result = m_image.clone();pre_process();process();post_process();cv::imwrite("result.jpg", m_result);cv::imshow("result", m_result);cv::waitKey(0);}cv::Mat m_image;cv::Mat m_result;private:virtual void pre_process() = 0;virtual void process() = 0;virtual void post_process() = 0;
};

该接口类的思路很简单,即一个名为YOLOv5的基类,定义了抽象业务接口如前处理pre_process()、模型推理process()和后处理post_process()需要在派生类中进行具体实现。由基类YOLO根据后端推理框架种类派生出五个子类YOLO_Libtorch 、YOLO_ONNXRuntime、YOLO_OpenCV、YOLO_OpenVINO和YOLO_TensorRT。

#pragma once#include "yolov5.h"
#include <torch/script.h>
#include <torch/torch.h>class YOLOv5_Libtorch : public YOLOv5
{	
public:YOLOv5_Libtorch(std::string model_path, Device_Type device_type);~YOLOv5_Libtorch();private:void pre_process();void process();void post_process();torch::DeviceType m_device;torch::jit::script::Module module;std::vector<torch::jit::IValue> m_inputs;torch::jit::IValue m_outputs;
};

调用时初始化传入模型路径和Device_Type,并指定图片路径即可推理,demo如下:

#include "yolov5_libtorch.h"int main(int argc, char* argv[])
{YOLOv5* yolov5 = new YOLOv5_Libtorch("yolov5n_cpu.torchscript", CPU);yolov5->infer("bus.jpg");return 0;
}

后续扩充了支持不同的模型Model_Type,并使用抽象工厂和单例模式时对外接口在被调用时表现形式更统一:

#pragma once#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>...enum Algo_Type
{Libtorch,ONNXRuntime,OpenCV,OpenVINO,TensorRT,
};enum Device_Type
{CPU,GPU,
};enum Model_Type
{FP32,FP16,INT8,
};class YOLOv5
{
public:virtual void init(const std::string model_path, const Device_Type device_type, Model_Type model_type) = 0;void infer(const std::string image_path);virtual void release() {};protected:virtual void pre_process() = 0;virtual void process() = 0;virtual void post_process();cv::Mat m_image;cv::Mat m_result;float* m_outputs_host;
};class  AlgoFactory
{
public:typedef std::unique_ptr<YOLOv5>(*CreateFunction)();static AlgoFactory& instance();void register_algo(const Algo_Type& algo_type, CreateFunction create_function);std::unique_ptr<YOLOv5> create(const Algo_Type& algo_type);private:AlgoFactory();std::map<Algo_Type, CreateFunction> m_algo_registry;
};

AlgoFactory类中m_algo_registry用来储存算法的唯一全局注册表,register_algo接口用来注册算法,create接口用来返回抽象工厂创建的算法,具体实现如下:

AlgoFactory& AlgoFactory::instance()
{static AlgoFactory algo_factory;return algo_factory;
}void AlgoFactory::register_algo(const Algo_Type& algo_type, CreateFunction create_function)
{m_algo_registry[algo_type] = create_function;
}std::unique_ptr<YOLOv5> AlgoFactory::create(const Algo_Type& algo_type)
{assert(("algo type not exists!", m_algo_registry.find(algo_type) != m_algo_registry.end()));return m_algo_registry[algo_type]();
}AlgoFactory::AlgoFactory()
{register_algo(Algo_Type::Libtorch, []() -> std::unique_ptr<YOLOv5> { return std::make_unique<YOLOv5_Libtorch>(); });register_algo(Algo_Type::ONNXRuntime, []() -> std::unique_ptr<YOLOv5> { return std::make_unique<YOLOv5_ONNXRuntime>(); });register_algo(Algo_Type::OpenCV, []() -> std::unique_ptr<YOLOv5> { return std::make_unique<YOLOv5_OpenCV>(); });register_algo(Algo_Type::OpenVINO, []() -> std::unique_ptr<YOLOv5> { return std::make_unique<YOLOv5_OpenVINO>(); });register_algo(Algo_Type::TensorRT, []() -> std::unique_ptr<YOLOv5> { return std::make_unique<YOLOv5_TensorRT>(); });
}

此时调用时需要先创建算法实例,并依次调用init()、infer()和release()接口,demo的表现形式如下:

#include "yolov5.h"int main(int argc, char* argv[])
{std::unique_ptr<YOLOv5> yolov5 = AlgoFactory::instance().create(Algo_Type::Libtorch);yolov5->init("yolov5n_cpu_fp32.torchscript", CPU, FP32);yolov5->infer("test.mp4");yolov5->release();return 0;
}

2.x版本中主要增加了对yolov8检测器的支持,接口头文件除了增加Algo_Type枚举类型用来表示不同算法:

enum Algo_Type
{YOLOv5,YOLOv8,
};

3.x版本主要增加了对分类、分割算法的支持,头文件增加了Task_Type枚举类型,由于维度的扩充将算法注册表替换为二维向量来储存:

#pragma once#include <iostream>
#include <fstream>
#include <opencv2/opencv.hpp>enum Backend_Type
{Libtorch,ONNXRuntime,OpenCV,OpenVINO,TensorRT,
};enum Task_Type
{Classification,Detection,Segmentation,
};enum Algo_Type
{YOLOv5,YOLOv8,
};enum Device_Type
{CPU,GPU,
};enum Model_Type
{FP32,FP16,INT8,
};class YOLO
{
public:virtual ~YOLO() {};	//不加此句会导致虚拟继承内存泄漏virtual void init(const Algo_Type algo_type, const Device_Type device_type, const Model_Type model_type, const std::string model_path) = 0;void infer(const std::string file_path, char* argv[], bool save_result = true, bool show_result = true);virtual void release() {};protected:virtual void pre_process() = 0;virtual void process() = 0;virtual void post_process() = 0;cv::Mat m_image;cv::Mat m_result;int m_input_width = 640;int m_input_height = 640;int m_input_numel = 1 * 3 * m_input_width * m_input_height;
};class  CreateFactory
{
public:typedef std::unique_ptr<YOLO>(*CreateFunction)();static CreateFactory& instance();void register_class(const Backend_Type& backend_type, const Task_Type& task_type, CreateFunction create_function);std::unique_ptr<YOLO> create(const Backend_Type& backend_type, const Task_Type& task_type);private:CreateFactory();std::vector<std::vector<CreateFunction>> m_create_registry;
};
CreateFactory& CreateFactory::instance()
{static CreateFactory create_factory;return create_factory;
}void CreateFactory::register_class(const Backend_Type& backend_type, const Task_Type& task_type, CreateFunction create_function)
{m_create_registry[backend_type][task_type] = create_function;
}std::unique_ptr<YOLO> CreateFactory::create(const Backend_Type& backend_type, const Task_Type& task_type)
{if (backend_type >= m_create_registry.size()){std::cout << "unsupported backend type!" << std::endl;std::exit(-1);}if (task_type >= m_create_registry[task_type].size()){std::cout << "unsupported task type!" << std::endl;std::exit(-1);}return m_create_registry[backend_type][task_type]();
}CreateFactory::CreateFactory()
{m_create_registry.resize(5, std::vector<CreateFunction>(3));#ifdef _YOLO_LIBTORCHregister_class(Backend_Type::Libtorch, Task_Type::Classification, []() -> std::unique_ptr<YOLO> { return std::make_unique<YOLO_Libtorch_Classification>(); });register_class(Backend_Type::Libtorch, Task_Type::Detection, []() -> std::unique_ptr<YOLO> { return std::make_unique<YOLO_Libtorch_Detection>(); });register_class(Backend_Type::Libtorch, Task_Type::Segmentation, []() -> std::unique_ptr<YOLO> { return std::make_unique<YOLO_Libtorch_Segmentation>(); });
#endif // _YOLO_Libtorch#ifdef _YOLO_ONNXRUNTIMEregister_class(Backend_Type::ONNXRuntime, Task_Type::Classification, []() -> std::unique_ptr<YOLO> { return std::make_unique<YOLO_ONNXRuntime_Classification>(); });register_class(Backend_Type::ONNXRuntime, Task_Type::Detection, []() -> std::unique_ptr<YOLO> { return std::make_unique<YOLO_ONNXRuntime_Detection>(); });register_class(Backend_Type::ONNXRuntime, Task_Type::Segmentation, []() -> std::unique_ptr<YOLO> { return std::make_unique<YOLO_ONNXRuntime_Segmentation>(); });
#endif // _YOLO_ONNXRuntime#ifdef _YOLO_OPENCVregister_class(Backend_Type::OpenCV, Task_Type::Classification, []() -> std::unique_ptr<YOLO> { return std::make_unique<YOLO_OpenCV_Classification>(); });register_class(Backend_Type::OpenCV, Task_Type::Detection, []() -> std::unique_ptr<YOLO> { return std::make_unique<YOLO_OpenCV_Detection>(); });register_class(Backend_Type::OpenCV, Task_Type::Segmentation, []() -> std::unique_ptr<YOLO> { return std::make_unique<YOLO_OpenCV_Segmentation>(); });
#endif // _YOLO_OpenCV#ifdef _YOLO_OPENVINOregister_class(Backend_Type::OpenVINO, Task_Type::Classification, []() -> std::unique_ptr<YOLO> { return std::make_unique<YOLO_OpenVINO_Classification>(); });register_class(Backend_Type::OpenVINO, Task_Type::Detection,[]() -> std::unique_ptr<YOLO> { return std::make_unique<YOLO_OpenVINO_Detection>(); });register_class(Backend_Type::OpenVINO, Task_Type::Segmentation, []() -> std::unique_ptr<YOLO> { return std::make_unique<YOLO_OpenVINO_Segmentation>(); });
#endif // _YOLO_OpenVINO#ifdef _YOLO_TENSORRTregister_class(Backend_Type::TensorRT, Task_Type::Classification, []() -> std::unique_ptr<YOLO> { return std::make_unique<YOLO_TensorRT_Classification>(); });register_class(Backend_Type::TensorRT, Task_Type::Detection, []() -> std::unique_ptr<YOLO> { return std::make_unique<YOLO_TensorRT_Detection>(); });register_class(Backend_Type::TensorRT, Task_Type::Segmentation, []() -> std::unique_ptr<YOLO> { return std::make_unique<YOLO_TensorRT_Segmentation>(); });
#endif // _YOLO_TensorRT
}

通过基类YOLO根据任务类型派生出三个子类YOLO_Classification、YOLO_Detection ,并由YOLO_Detection 派生出YOLO_Segmentation:

#pragma once#include "yolo.h"
#include "utils.h"class YOLO_Classification : virtual public YOLO
{
protected:void draw_result(std::string label);int class_num = 1000;
};
#pragma once#include "yolo.h"
#include "utils.h"class YOLO_Detection : virtual public YOLO
{
protected:void LetterBox(cv::Mat& input_image, cv::Mat& output_image, cv::Vec4d& params, cv::Size shape = cv::Size(640, 640), cv::Scalar color = cv::Scalar(114, 114, 114));void nms(std::vector<cv::Rect> & boxes, std::vector<float> & scores, float score_threshold, float nms_threshold, std::vector<int> & indices);void scale_box(cv::Rect& box, cv::Size size);void draw_result(std::string label, cv::Rect box);int class_num = 80;float score_threshold = 0.2;float nms_threshold = 0.5;float confidence_threshold = 0.2;cv::Vec4d m_params;int m_output_numprob;int m_output_numbox;int m_output_numdet;
};
#pragma once#include "yolo_detection.h"//网络输出相关参数
struct OutputSeg
{int id;             //结果类别idfloat confidence;   //结果置信度cv::Rect box;       //矩形框cv::Mat boxMask;    //矩形框内mask,节省内存空间和加快速度
};//掩膜相关参数
struct MaskParams
{int segChannels = 32;int segWidth = 160;int segHeight = 160;int netWidth = 640;int netHeight = 640;float maskThreshold = 0.5;cv::Size srcImgShape;cv::Vec4d params;
};class YOLO_Segmentation : public YOLO_Detection
{
protected:void GetMask(const cv::Mat& maskProposals, const cv::Mat& mask_protos, OutputSeg& output, const MaskParams& maskParams);void draw_result(std::vector<OutputSeg> result);MaskParams m_mask_params;int m_output_numseg;
};

另一方面和之前版本类似,由基类YOLO根据后端推理框架种类派生出五个子类YOLO_Libtorch 、YOLO_ONNXRuntime、YOLO_OpenCV、YOLO_OpenVINO和YOLO_TensorRT。最终的具体实现子类需要派生自任务种类的父类和推理框架种类的父类,如下所示:

class YOLO_Libtorch : virtual public YOLO
{	
public:void init(const Algo_Type algo_type, const Device_Type device_type, const Model_Type model_type, const std::string model_path);protected:Algo_Type m_algo;torch::DeviceType m_device;Model_Type m_model;torch::jit::script::Module module;std::vector<torch::jit::IValue> m_input;torch::jit::IValue m_output;
};class YOLO_Libtorch_Classification : public YOLO_Libtorch, public YOLO_Classification
{
public:void init(const Algo_Type algo_type, const Device_Type device_type, const Model_Type model_type, const std::string model_path);private:void pre_process();void process();void post_process();float* m_output_host;
};

这篇关于yolo-inference多后端+多任务+多算法+多精度模型 框架开发记录(cpp版)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1056836

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof