BiRefNet512*512onnx模型c++推理

BiRefNet主页:github图像前景分割

onnx模型下载:githu releases

前处理使用opencv,后处理使用libtorch

参考:onnxruntime c++ inference

           U_NET 网络onnx部署

           libtorch 输出tensor 到文件并用ImageJ 查看

环境:win11-wsl

依赖opencv,onnxruntime,libtorch

opencv编译参考:rk3588使用交叉编译opencv静态库,后面有wsl报openjp2错误解决方法

onnxruntime下载编译好的包解压:onnxruntime releases

libtorch下载编译好的包解压,参考:torch官方教程

目录结构:onnxruntime不一定要放在这里

main.cpp:

#include <vector>
#include <onnxruntime_cxx_api.h>
#include <opencv2/opencv.hpp>
#include <torch/torch.h>
#include <torch/script.h>
#include <fstream>

void tensor2txt(torch::Tensor &tsor, std::string path)
{
    std::ofstream outfile;
    outfile.open(path);
    assert(outfile && "failed to open the file!");    
    // tsor = tsor.reshape({-1});
    tsor.squeeze(0);
    for (int i=0; i<tsor.sizes()[0]; i++) {
      for (int j = 0; j < tsor.sizes()[1]; j++) {
        auto s = tsor[i][j].item<float>();
        outfile << s << ","; 
      }
        outfile << "\n"; 
    }
    outfile.close();
}

int main(int argc, char* argv[])
{
    printf("main start!!!");
    Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "test");
    Ort::SessionOptions session_options;
    session_options.SetInterOpNumThreads(1);
    session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);
    const char* model_path = "./models/BiRefNet-general-resolution_512x512-fp16-epoch_216.onnx";
    
    Ort::Session session(env, model_path, session_options);
    Ort::AllocatorWithDefaultOptions allocator;
    
    size_t num_input_nodes = session.GetInputCount();
    Ort::Value input_tensor{nullptr};
    std::array<int64_t, 4> input_shape{1, 3, 512, 512};
    
    Ort::Value output_tensor{nullptr};
    std::array<int64_t, 4> output_shape{1, 1, 512, 512};
    
    std::array<float, 512*512*3> input_image{};
    std::array<float, 512*512*1> output{};
    
    std::string imgPath = "./models/20250321111449.jpg";
    cv::Mat img = cv::imread(imgPath);
    cv::cvtColor(img, img, cv::COLOR_BGR2RGB);
    cv::resize(img, img, cv::Size(512, 512));
    cv::Mat img_float;
    img.convertTo(img_float, CV_32FC3, 1/255.0, 0);    
    std::cout<<"dim:"<< img_float.rows<<":"<<img_float.cols<<":"<<img_float.channels()<<std::endl;
    int index = 0;
    for (unsigned c = 0; c < 3; c++)
    {
        for (unsigned i = 0; i < img_float.rows; i++)
        {
            for (unsigned j = 0; j < img_float.cols; j++)
            {
                input_image[index++] = (static_cast<float>(img_float.at<cv::Vec3f>(i, j)[c]));
            }
        }
    }

    auto memory_info = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeDefault);
    input_tensor = Ort::Value::CreateTensor<float>(memory_info, input_image.data(), input_image.size(),input_shape.data(), input_shape.size());
    output_tensor = Ort::Value::CreateTensor<float>(memory_info, output.data(), output.size(), output_shape.data(), output_shape.size());
    const char* input_names[] = {"input_image"};
    const char* output_names[] = {"output_image"};

    session.Run(Ort::RunOptions{nullptr}, input_names, &input_tensor, 1, output_names, &output_tensor, 1);

    float* out = output_tensor.GetTensorMutableData<float>();
    torch::Tensor tensor = torch::from_blob((void*)out, {1, 1, 512, 512});
    torch::Tensor output2 = torch::sigmoid(tensor);
    output2.print();
    // 1*C*H*W -> H*W*C
    output2 = output2.squeeze(0).detach().permute({1, 2, 0});
    output2.print();
    tensor2txt(output2, "./output1.txt");
    output2 = output2.mul(255).clamp(0, 255).to(torch::kU8);
    output2.print();
    tensor2txt(output2, "./output2.txt");
    output2 = output2.cpu();
    cv::Mat resultImg(512, 512, CV_8UC1, output2.data_ptr());
    cv::imwrite("result.jpg", resultImg);

    // for(int i = 0; i < 10; i++)
    // {
    //     std::cout<< out[i]<<":";
    // }

    return 0;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.18)

project(onnx_example LANGUAGES CXX)

find_package(Torch REQUIRED)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORCH_CXX_FLAGS}")

# 替换成自己的路径
set(ONNXRUNTIME_DIR "xxxx/3rdparty/onnxruntime-linux-x64-1.21.0")
set(OpenCV_DIR "xxxx/opencv/install/x64/lib/cmake/opencv4")
include_directories(${ONNXRUNTIME_DIR}/include)
find_package(Iconv)
find_package(OpenCV REQUIRED )

include_directories(
    ${OpenCV_INCLUDE_DIRS}/include
    )


add_executable(onnx_example main.cpp)
target_link_libraries(onnx_example PRIVATE ${ONNXRUNTIME_DIR}/lib/libonnxruntime.so ${OpenCV_LIBS} ${TORCH_LIBRARIES})

install(TARGETS ${PROJECT_NAME} DESTINATION .)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../models/20250321111449.jpg DESTINATION models)
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/../models/BiRefNet-general-resolution_512x512-fp16-epoch_216.onnx DESTINATION models)

进入build编译:替换成自己的libtorch路径

cmake ../ -DCMAKE_INSTALL_PREFIX=../lib -DCMAKE_PREFIX_PATH=xxx/depends/libtorch

make

make install

可执行文件在lib路径下

添加libtorch和onnxruntime的环境变量

export ONNXRUNTIME_DIR="xxx/depends/onnxruntime-linux-x64-1.21.0"

export LIBTORCH_DIR="xxx/depends/libtorch"
export LD_LIBRARY_PATH=$ONNXRUNTIME_DIR/lib:$LD_LIBRARY_PATH

export LD_LIBRARY_PATH=$LIBTORCH_DIR/lib:$LD_LIBRARY_PATH

运行:./onnx_example

<think>好的,我现在需要帮助用户在树莓派上使用C++实现ONNX模型推理。首先,我需要回忆一下相关的步骤和可能遇到的问题。用户之前提到的YOLOv8和AlexNet的ONNX模型部署,可能可以参考这些例子,但现在是C++环境,所以需要调整工具链。 首先,用户需要在树莓派上安装ONNX Runtime的C++库。树莓派是ARM架构,可能需要从源码编译ONNX Runtime,或者找预编译的版本。我记得ONNX Runtime有针对Linux ARM的版本,可能需要确认是否支持树莓派的OS版本,比如Raspbian。如果官方没有提供预编译包,可能需要自己编译,这可能会比较麻烦,特别是对于树莓派这种资源有限的设备。 接下来,用户需要配置C++项目,包含ONNX Runtime的头文件和链接库。可能需要使用CMake来管理项目,确保编译器能找到相关的依赖项。同时,树莓派的内存和处理能力有限,需要优化模型推理代码,避免内存溢出或性能问题。 然后,编写推理代码。参考Python的例子,C++代码的结构应该类似:创建InferenceSession,准备输入数据,运行推理,处理输出。需要注意的是输入输出的数据类型和形状,确保与模型匹配。用户可能需要使用OpenCV或其他库来预处理图像数据,转换成模型需要的格式。 此外,树莓派上可能需要处理硬件加速的问题,比如使用ARM的NEON指令集,或者是否支持GPU加速。不过大多数树莓派没有独立的GPU,所以可能只能依赖CPU。这会限制推理速度,特别是对于较大的模型。 还要考虑模型转换是否正确。用户需要确保他们的ONNX模型是正确导出的,没有不支持的算子。可能需要使用ONNX的检查工具验证模型,类似Python中的onnx.checker.check_model。 可能的错误点包括:安装依赖库缺失,如libprotobuf;编译ONNX Runtime时的配置选项不正确;输入数据格式错误导致推理失败;内存不足导致运行崩溃等。需要指导用户逐步排查这些问题。 总结步骤应该是:安装依赖,编译或安装ONNX Runtime C++库,配置项目,编写代码,处理数据,运行推理。同时,提供示例代码片段,帮助用户理解如何加载模型、创建会话、处理输入输出。 另外,用户可能需要测试模型性能,比如推理时间,内存占用,以及可能的优化方法,比如模型量化,减少输入尺寸等。这些优化在资源受限的树莓派上尤其重要。 最后,需要验证整个流程是否可行,可能建议用户从一个简单的模型开始,比如MNIST,确保基础流程正确,再迁移到他们自己的模型上。</think>### 在树莓派上使用C++实现ONNX模型推理的步骤 #### 1. 安装ONNX Runtime C++库 树莓派需要安装支持ARM架构的ONNX Runtime。由于官方未直接提供预编译包,需从源码编译: ```bash # 安装依赖 sudo apt-get install cmake g++ python3-dev libprotobuf-dev protobuf-compiler # 下载ONNX Runtime源码 git clone --recursive https://github.com/microsoft/onnxruntime cd onnxruntime # 编译(需调整编译参数以适配树莓派) ./build.sh --config MinSizeRel --arm --update --build --parallel 4 ``` 编译完成后,库文件位于`build/Linux/MinSizeRel`目录,头文件在`include`目录[^1]。 #### 2. 配置C++项目 使用CMake链接ONNX Runtime库: ```cmake cmake_minimum_required(VERSION 3.10) project(ONNXInference) set(CMAKE_CXX_STANDARD 17) # 指定ONNX Runtime路径 set(ONNXRUNTIME_ROOT "/path/to/onnxruntime") include_directories(${ONNXRUNTIME_ROOT}/include) link_directories(${ONNXRUNTIME_ROOT}/lib) add_executable(main main.cpp) target_link_libraries(main onnxruntime) ``` #### 3. 编写推理代码 ```cpp #include <onnxruntime_cxx_api.h> #include <vector> #include <iostream> int main() { // 初始化ONNX环境 Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "ONNXInference"); Ort::SessionOptions session_options; // 加载模型 Ort::Session session(env, "model.onnx", session_options); // 获取输入输出信息 Ort::AllocatorWithDefaultOptions allocator; auto input_name = session.GetInputName(0, allocator); auto output_name = session.GetOutputName(0, allocator); // 准备输入数据(假设输入为1x3x224x224的float数组) std::vector<int64_t> input_shape = {1, 3, 224, 224}; std::vector<float> input_data(1*3*224*224, 0.5f); // 示例数据 // 创建输入Tensor Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu( OrtAllocatorType::OrtDeviceAllocator, OrtMemType::OrtMemTypeDefault); Ort::Value input_tensor = Ort::Value::CreateTensor<float>( memory_info, input_data.data(), input_data.size(), input_shape.data(), input_shape.size()); // 执行推理 const char* input_names[] = {input_name}; const char* output_names[] = {output_name}; auto output_tensors = session.Run(Ort::RunOptions{nullptr}, input_names, &input_tensor, 1, output_names, 1); // 解析输出 float* output_data = output_tensors[0].GetTensorMutableData<float>(); std::cout << "Output[0]: " << output_data[0] << std::endl; return 0; } ``` #### 4. 关键注意事项 1. **数据预处理**:需将输入数据转换为与模型匹配的格式(如归一化、调整尺寸) 2. **内存管理**:树莓派内存有限,建议: - 使用`MinSizeRel`编译模式 - 避免多次加载模型 - 优化输入尺寸 3. **性能优化**: ```cpp // 启用多线程(需树莓派支持) session_options.SetIntraOpNumThreads(4); session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL); ``` #### 5. 验证流程 1. 使用Netron可视化模型结构,确认输入输出维度 2. 用简单测试数据验证推理结果一致性 3. 监控内存使用:`free -m`
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值