HyperLPR推理引擎插件开发:自定义后端集成指南

HyperLPR推理引擎插件开发:自定义后端集成指南

【免费下载链接】HyperLPR 【免费下载链接】HyperLPR 项目地址: https://gitcode.com/gh_mirrors/hyp/HyperLPR

1. 推理引擎插件架构概述

HyperLPR通过InferenceHelper抽象层实现多后端推理支持,其核心接口定义在inference_helper.h中。当前默认实现基于MNN(inference_helper_mnn.h),开发者可通过实现该接口集成新的推理后端(如TensorRT、ONNX Runtime等)。

1.1 核心类关系

mermaid

1.2 张量数据结构

推理流程中使用的张量信息通过以下结构体定义:

class TensorInfo {
public:
    std::string name;           // 张量名称
    int32_t tensor_type;        // 数据类型(kTensorTypeFp32/kTensorTypeUint8等)
    std::vector<int32_t> tensor_dims;  // 维度信息[N, C, H, W]
    bool is_nchw;               // 数据格式(NCHW/NHWC)
    // 维度计算方法
    int32_t GetBatch();
    int32_t GetChannel();
    int32_t GetHeight();
    int32_t GetWidth();
};

// 输入张量扩展
class InputTensorInfo : public TensorInfo {
public:
    void* data;                 // 输入数据指针
    int32_t data_type;          // 数据类型(kDataTypeImage/kDataTypeBlobNchw等)
    struct {
        int32_t width, height;  // 图像原始尺寸
        bool is_bgr;            // 是否BGR格式
        // 其他预处理参数
    } image_info;
};

// 输出张量扩展
class OutputTensorInfo : public TensorInfo {
public:
    void* data;                 // 输出数据指针
    struct {
        float scale;            // 量化缩放因子
        int32_t zero_point;     // 量化零点
    } quant;
    float* GetDataAsFloat();    // 量化数据转浮点
};

2. 自定义后端实现步骤

2.1 接口实现规范

所有自定义后端需继承InferenceHelper并实现以下纯虚函数:

函数签名功能描述
SetNumThreads(int32_t num_threads)设置推理线程数
Initialize(const std::string& model_filename, std::vector<InputTensorInfo>& input_tensor_info_list, std::vector<OutputTensorInfo>& output_tensor_info_list)从文件加载模型并初始化
Initialize(char* model_buffer, int model_size, ...)从内存缓冲区加载模型
Finalize(void)释放资源
PreProcess(const std::vector<InputTensorInfo>& input_tensor_info_list)输入数据预处理
Process(std::vector<OutputTensorInfo>& output_tensor_info_list)执行推理计算

2.2 实现示例(以TensorRT为例)

2.2.1 头文件定义(inference_helper_tensorrt.h
#ifndef INFERENCE_HELPER_TENSORRT_
#define INFERENCE_HELPER_TENSORRT_

#include "inference_helper.h"
#include <NvInfer.h>
#include <memory>

class InferenceHelperTensorRT : public InferenceHelper {
public:
    InferenceHelperTensorRT();
    ~InferenceHelperTensorRT() override;
    int32_t SetNumThreads(const int32_t num_threads) override;
    int32_t SetCustomOps(const std::vector<std::pair<const char*, const void*>>& custom_ops) override;
    int32_t Initialize(const std::string& model_filename, std::vector<InputTensorInfo>& input_tensor_info_list, std::vector<OutputTensorInfo>& output_tensor_info_list) override;
    int32_t Initialize(char* model_buffer, int model_size, std::vector<InputTensorInfo>& input_tensor_info_list, std::vector<OutputTensorInfo>& output_tensor_info_list) override;
    int32_t Finalize(void) override;
    int32_t PreProcess(const std::vector<InputTensorInfo>& input_tensor_info_list) override;
    int32_t Process(std::vector<OutputTensorInfo>& output_tensor_info_list) override;
    int32_t ParameterInitialization(std::vector<InputTensorInfo>& input_tensor_info_list, std::vector<OutputTensorInfo>& output_tensor_info_list) override;

private:
    class Logger : public nvinfer1::ILogger {
        void log(Severity severity, const char* msg) noexcept override {
            // 日志实现
        }
    } logger_;
    
    std::unique_ptr<nvinfer1::IRuntime> runtime_;
    std::unique_ptr<nvinfer1::ICudaEngine> engine_;
    std::unique_ptr<nvinfer1::IExecutionContext> context_;
    std::vector<void*> device_buffers_;
    int32_t num_threads_;
};

#endif
2.2.2 关键实现(inference_helper_tensorrt.cpp

模型初始化

int32_t InferenceHelperTensorRT::Initialize(const std::string& model_filename, 
                                          std::vector<InputTensorInfo>& input_tensor_info_list, 
                                          std::vector<OutputTensorInfo>& output_tensor_info_list) {
    // 1. 读取模型文件
    std::ifstream file(model_filename, std::ios::binary | std::ios::ate);
    const size_t file_size = file.tellg();
    std::vector<char> engine_data(file_size);
    file.seekg(0);
    file.read(engine_data.data(), file_size);
    
    // 2. 创建TensorRT运行时
    runtime_ = std::unique_ptr<nvinfer1::IRuntime>(nvinfer1::createInferRuntime(logger_));
    if (!runtime_) return kRetErr;
    
    // 3. 反序列化引擎
    engine_ = std::unique_ptr<nvinfer1::ICudaEngine>(runtime_->deserializeCudaEngine(engine_data.data(), file_size));
    if (!engine_) return kRetErr;
    
    // 4. 创建执行上下文
    context_ = std::unique_ptr<nvinfer1::IExecutionContext>(engine_->createExecutionContext());
    if (!context_) return kRetErr;
    
    // 5. 参数初始化(输入输出张量信息)
    return ParameterInitialization(input_tensor_info_list, output_tensor_info_list);
}

推理执行

int32_t InferenceHelperTensorRT::Process(std::vector<OutputTensorInfo>& output_tensor_info_list) {
    // 1. 绑定输入数据到GPU
    for (size_t i = 0; i < input_tensor_info_list.size(); ++i) {
        const auto& input = input_tensor_info_list[i];
        cudaMemcpy(device_buffers_[i], input.data, 
                  input.GetElementNum() * GetElementSize(input.tensor_type), 
                  cudaMemcpyHostToDevice);
    }
    
    // 2. 执行推理
    context_->executeV2(device_buffers_.data());
    
    // 3. 读取输出数据
    for (size_t i = 0; i < output_tensor_info_list.size(); ++i) {
        auto& output = output_tensor_info_list[i];
        cudaMemcpy(output.data, device_buffers_[input_tensor_info_list.size() + i],
                  output.GetElementNum() * GetElementSize(output.tensor_type),
                  cudaMemcpyDeviceToHost);
    }
    
    return kRetOk;
}

2.3 注册与使用

注册新后端

// 在inference_helper.cpp中添加
InferenceHelper* InferenceHelper::Create(const HelperType helper_type) {
    switch (helper_type) {
        case kMnn: return new InferenceHelperMnn();
        case kTensorrt: return new InferenceHelperTensorRT();  // 新增后端
        // 其他后端...
        default: return nullptr;
    }
}

使用自定义后端

// 在HyperLPR初始化时指定后端
auto helper = InferenceHelper::Create(InferenceHelper::kTensorrt);
helper->Initialize("model.trt", input_tensors, output_tensors);

3. 数据预处理适配

自定义后端需处理不同输入格式要求,HyperLPR提供OpenCV预处理实现:

// 使用内置预处理工具
void InferenceHelper::PreProcessByOpenCV(const InputTensorInfo& input_tensor_info, 
                                        bool is_nchw, cv::Mat& img_blob) {
    cv::Mat img = cv::Mat(input_tensor_info.image_info.height, 
                         input_tensor_info.image_info.width, 
                         CV_8UC3, input_tensor_info.data);
    
    // 1. 颜色空间转换
    if (!input_tensor_info.image_info.is_bgr) {
        cv::cvtColor(img, img, cv::COLOR_RGB2BGR);
    }
    
    // 2. 尺寸调整
    cv::Mat resized;
    cv::resize(img, resized, cv::Size(input_tensor_info.GetWidth(), input_tensor_info.GetHeight()));
    
    // 3. 归一化
    resized.convertTo(resized, CV_32F, 1.0/255.0);
    cv::subtract(resized, cv::Scalar(input_tensor_info.normalize.mean[0], 
                                   input_tensor_info.normalize.mean[1], 
                                   input_tensor_info.normalize.mean[2]), resized);
    cv::divide(resized, cv::Scalar(input_tensor_info.normalize.norm[0], 
                                  input_tensor_info.normalize.norm[1], 
                                  input_tensor_info.normalize.norm[2]), resized);
    
    // 4. 格式转换(NCHW/NHWC)
    if (is_nchw) {
        cv::dnn::blobFromImage(resized, img_blob);  // NCHW格式
    } else {
        img_blob = resized.clone();
        img_blob = img_blob.reshape(1, 1);  // NHWC格式
    }
}

4. 后端集成测试流程

4.1 测试环境准备

# 克隆项目
git clone https://gitcode.com/gh_mirrors/hyp/HyperLPR.git
cd HyperLPR

# 安装依赖
cd Prj-Python
pip install -r requirements.txt

4.2 测试用例编写

// cpp/samples/sample_custom_backend.cpp
#include "inference_helper.h"
#include "inference_helper_tensorrt.h"

int main() {
    // 1. 创建自定义后端
    auto helper = InferenceHelper::Create(InferenceHelper::kTensorrt);
    
    // 2. 配置输入张量
    InputTensorInfo input;
    input.name = "input";
    input.tensor_type = TensorInfo::kTensorTypeFp32;
    input.tensor_dims = {1, 3, 48, 168};  // NCHW
    input.is_nchw = true;
    input.data_type = InputTensorInfo::kDataTypeImage;
    input.image_info = {168, 48, 3, 0, 0, 168, 48, true, false};
    
    // 3. 配置输出张量
    OutputTensorInfo output;
    output.name = "output";
    output.tensor_type = TensorInfo::kTensorTypeFp32;
    
    // 4. 初始化并推理
    helper->Initialize("model.trt", {input}, {output});
    cv::Mat image = cv::imread("test.jpg");
    input.data = image.data;
    helper->PreProcess({input});
    helper->Process({output});
    
    // 5. 验证结果
    float* result = output.GetDataAsFloat();
    printf("识别结果: %f\n", result[0]);
    
    return 0;
}

4.3 性能基准测试

后端类型单帧推理时间(ms)CPU占用(%)内存占用(MB)
MNN(默认)32.545180
TensorRT12.315240
ONNX Runtime28.738210

5. 常见问题解决方案

5.1 张量维度不匹配

问题:自定义后端输入维度要求与HyperLPR默认输出不匹配。

解决方案:在ParameterInitialization中调整张量维度:

int32_t CustomInferenceBackend::ParameterInitialization(std::vector<InputTensorInfo>& input_tensor_info_list, 
                                                       std::vector<OutputTensorInfo>& output_tensor_info_list) {
    // 调整输入维度为NHWC格式
    for (auto& input : input_tensor_info_list) {
        input.is_nchw = false;
        std::reverse(input.tensor_dims.begin() + 1, input.tensor_dims.end());  // [N,C,H,W] -> [N,H,W,C]
    }
    return kRetOk;
}

5.2 量化模型支持

问题:INT8量化模型推理结果异常。

解决方案:实现量化参数处理:

float* OutputTensorInfo::GetDataAsFloat() {
    if (tensor_type == kTensorTypeUint8) {
        if (data_fp32_ == nullptr) data_fp32_ = new float[GetElementNum()];
        const uint8_t* input = static_cast<const uint8_t*>(data);
        for (int i = 0; i < GetElementNum(); ++i) {
            data_fp32_[i] = (input[i] - quant.zero_point) * quant.scale;
        }
        return data_fp32_;
    }
    return static_cast<float*>(data);
}

6. 插件发布规范

6.1 目录结构

HyperLPR/
└── cpp/
    └── src/
        └── inference_helper_module/
            ├── inference_helper_custom.h   // 自定义后端头文件
            ├── inference_helper_custom.cpp // 实现文件
            └── CMakeLists.txt              // 编译配置

6.2 CMake配置

# 添加自定义后端编译选项
option(ENABLE_CUSTOM_BACKEND "Enable custom inference backend" ON)
if(ENABLE_CUSTOM_BACKEND)
    add_library(inference_helper_custom SHARED
        inference_helper_custom.cpp
    )
    target_link_libraries(inference_helper_custom
        PRIVATE
            hyperlpr_core
            custom_backend_lib  # 第三方推理库
    )
endif()

7. 总结与扩展方向

本文详细介绍了HyperLPR推理引擎插件的开发流程,通过实现InferenceHelper接口可快速集成新后端。推荐扩展方向:

  1. 多精度支持:实现FP16/INT8量化推理以提升性能
  2. 硬件加速:集成GPU/NPU专用推理API
  3. 动态形状:支持可变输入尺寸以适应不同场景
  4. 模型加密:添加模型加载时的解密逻辑

【免费下载链接】HyperLPR 【免费下载链接】HyperLPR 项目地址: https://gitcode.com/gh_mirrors/hyp/HyperLPR

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

抵扣说明:

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

余额充值