神经网络语义分割模型C++部署(VS2019+ONNXRuntime+OpenCV)

  • Post author:
  • Post category:其他


C++的API跑神经网络模型相比于Python可以得到数倍的提升

同时类似TensorRT会提供非常多的模型优化,比如int8推理

而且实际项目中一般考虑性能等各种因素也不会直接使用Python

而是会选择C++或者Java等

这次我在做项目中遇到了必须要用C++来做模型部署的问题,这里做个记录

我这次选择了ONNXRuntime这个框架做CPU推理

ONNXRuntime的GPU推理和TensorRT将在未来有空的时候进行跟进

另外本来我是想直接用Tensorflow的C++api,但是坑爹的是windows10版的源码缺头文件,无法通过编译,所以就搁置了



ONNXRuntime

onnx作为一个非常优秀的跨平台的深度学习工具,其他框架训练的模型均可在上进行使用部署,作为cpu平台部署的利器,因为不像gpu平台,英伟达提供了tensorrt进行加速部署.这里是在win10借助vs2019上面进行安装编译这个平台



安装ONNXRuntime

(1)去下载安装包https://www.nuget.org/

在这里插入图片描述

在这里插入图片描述

(2)将安装包拷贝到win上的磁盘上,指定一个目录存放

在这里插入图片描述

(3)打开vs2019,工具->NuGet程序包管理器->程序包管理控制平台

这是一个命令行平台

接下来安装即可

PM> Install-Package Microsoft.ML.OnnxRuntime -Source C:\Users\pc\Desktop\Cache
正在尝试收集与目标为“native,Version=v0.0”的项目“onnxDemo”有关的包“Microsoft.ML.OnnxRuntime.1.6.0”的依赖项信息
收集依赖项信息花费时间 20 ms
正在尝试解析程序包“Microsoft.ML.OnnxRuntime.1.6.0”的依赖项,DependencyBehavior 为“Lowest”
解析依赖项信息花费时间 0 ms
正在解析操作以安装程序包“Microsoft.ML.OnnxRuntime.1.6.0”
已解析操作以安装程序包“Microsoft.ML.OnnxRuntime.1.6.0”
从“C:\Users\Administrator\AppData\Local\NuGet\Cache”检索包“Microsoft.ML.OnnxRuntime 1.6.0” 
正在将程序包“Microsoft.ML.OnnxRuntime.1.6.0”添加到文件夹“C:\Users\Administrator\source\repos\onnxDemo\packages”
已将程序包“Microsoft.ML.OnnxRuntime.1.6.0”添加到文件夹“C:\Users\Administrator\source\repos\onnxDemo\packages”
已将程序包“Microsoft.ML.OnnxRuntime.1.6.0”添加到“packages.config”
已将“Microsoft.ML.OnnxRuntime 1.6.0”成功安装到 onnxDemo
执行 nuget 操作花费时间 7.18 sec
已用时间: 00:00:07.4252338
PM> Install-Package Microsoft.ML.OnnxRuntime.mklml -Source C:\Users\Administrator\AppData\Local\NuGet\Cache
 
 
正在尝试收集与目标为“native,Version=v0.0”的项目“onnxDemo”有关的包“Microsoft.ML.OnnxRuntime.mklml.1.6.0”的依赖项信息
收集依赖项信息花费时间 2 ms
正在尝试解析程序包“Microsoft.ML.OnnxRuntime.mklml.1.6.0”的依赖项,DependencyBehavior 为“Lowest”
解析依赖项信息花费时间 0 ms
正在解析操作以安装程序包“Microsoft.ML.OnnxRuntime.mklml.1.6.0”
已解析操作以安装程序包“Microsoft.ML.OnnxRuntime.mklml.1.6.0”
从“C:\Users\Administrator\AppData\Local\NuGet\Cache”检索包“Microsoft.ML.OnnxRuntime.MKLML 1.6.0” 
正在将程序包“Microsoft.ML.OnnxRuntime.MKLML.1.6.0”添加到文件夹“C:\Users\Administrator\source\repos\onnxDemo\packages”
已将程序包“Microsoft.ML.OnnxRuntime.MKLML.1.6.0”添加到文件夹“C:\Users\Administrator\source\repos\onnxDemo\packages”
已将程序包“Microsoft.ML.OnnxRuntime.MKLML.1.6.0”添加到“packages.config”
已将“Microsoft.ML.OnnxRuntime.MKLML 1.6.0”成功安装到 onnxDemo
执行 nuget 操作花费时间 7.63 sec
已用时间: 00:00:07.6510274
PM> 

(3)写代码,ONNXRuntime写代码十分简洁

大概分为三部分

1.初始化环境,会话等

2.会话中加载模型,得到模型的输入和输出节点

3.调用API得到模型的返回值

这里以语义分割模型U2Net为例

#include <assert.h>
#include <vector>
#include<ctime>
#include <onnxruntime_cxx_api.h>
int main(int argc, char* argv[]) 
{
    //记录程序运行时间
    auto start_time = clock();
    //初始化环境,每个进程一个环境
    //环境保留了线程池和其他状态信息
    Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "test");
    //初始化Session选项
    Ort::SessionOptions session_options;
    session_options.SetIntraOpNumThreads(1);
    // Available levels are
    // ORT_DISABLE_ALL -> To disable all optimizations
    // ORT_ENABLE_BASIC -> To enable basic optimizations (Such as redundant node removals)
    // ORT_ENABLE_EXTENDED -> To enable extended optimizations (Includes level 1 + more complex optimizations like node fusions)
    // ORT_ENABLE_ALL -> To Enable All possible opitmizations
    session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_EXTENDED);

    //*************************************************************************
    // 创建Session并把模型加载到内存中
    const wchar_t* model_path = L"u2net.onnx";

    printf("Using Onnxruntime C++ API\n");
    Ort::Session session(env, model_path, session_options);

    //*************************************************************************
    //打印模型的输入层(node names, types, shape etc.)
    Ort::AllocatorWithDefaultOptions allocator;

    //输出模型输入节点的数量
    size_t num_input_nodes = session.GetInputCount();
    size_t num_output_nodes = session.GetOutputCount();
    std::vector<const char*> input_node_names(num_input_nodes);
    std::vector<const char*> output_node_names(num_output_nodes);
    std::vector<int64_t> input_node_dims;  // simplify... this model has only 1 input node {1, 3, 224, 224}.
                                           // Otherwise need vector<vector<>>

    printf("Number of inputs = %zu\n", num_input_nodes);
    //迭代所有的输入节点
    for (int i = 0; i < num_input_nodes; i++) {
         //输出输入节点的名称
        char* input_name = session.GetInputName(i, allocator);
        printf("Input %d : name=%s\n", i, input_name);
        input_node_names[i] = input_name;

        // 输出输入节点的类型
        Ort::TypeInfo type_info = session.GetInputTypeInfo(i);
        auto tensor_info = type_info.GetTensorTypeAndShapeInfo();

        ONNXTensorElementDataType type = tensor_info.GetElementType();
        printf("Input %d : type=%d\n", i, type);

        input_node_dims = tensor_info.GetShape();
        //输入节点的打印维度
          printf("Input %d : num_dims=%zu\n", i, input_node_dims.size());
        //打印各个维度的大小
          for (int j = 0; j < input_node_dims.size(); j++)
             printf("Input %d : dim %d=%jd\n", i, j, input_node_dims[j]);
        //batch_size=1
        input_node_dims[0] = 1;
    }
    //打印输出节点信息,方法类似
    for (int i = 0; i < num_output_nodes; i++)
    {
        char* output_name = session.GetOutputName(i, allocator);
        printf("Output: %d name=%s\n", i, output_name);
        output_node_names[i] = output_name;
        Ort::TypeInfo type_info = session.GetOutputTypeInfo(i);
        auto tensor_info = type_info.GetTensorTypeAndShapeInfo();
        ONNXTensorElementDataType type = tensor_info.GetElementType();
        printf("Output %d : type=%d\n", i, type);
        auto output_node_dims = tensor_info.GetShape();
        printf("Output %d : num_dims=%zu\n", i, output_node_dims.size());
        for (int j = 0; j < input_node_dims.size(); j++)
            printf("Output %d : dim %d=%jd\n", i, j, output_node_dims[j]);
    }
    
    //*************************************************************************
    // 使用样本数据对模型进行评分,并检验出入值的合法性
    size_t input_tensor_size = 3 * 320 * 320;  // simplify ... using known dim values to calculate size
                                               // use OrtGetTensorShapeElementCount() to get official size!
    
    std::vector<float> input_tensor_values(input_tensor_size);

    // 初始化一个数据(演示用,这里实际应该传入归一化的数据)
    for (unsigned int i = 0; i < input_tensor_size; i++)
        input_tensor_values[i] = (float)i / (input_tensor_size + 1);
    
    // 为输入数据创建一个Tensor对象
    try
    {
        auto memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
        Ort::Value input_tensor = Ort::Value::CreateTensor<float>(memory_info, input_tensor_values.data(), input_tensor_size, input_node_dims.data(), 4);
        //assert(input_tensor.IsTensor());
    
	    // 推理得到结果
	    auto output_tensors = session.Run(Ort::RunOptions{ nullptr }, input_node_names.data(), &input_tensor, 1, output_node_names.data(), 1);
	    assert(output_tensors.size() == 1 && output_tensors.front().IsTensor());
	
	    // Get pointer to output tensor float values
	    float* floatarr = output_tensors.front().GetTensorMutableData<float>(); 
	    printf("Number of outputs = %d\n", output_tensors.size());
    }
    catch (Ort::Exception& e)
    {
        printf(e.what());
    }
    auto end_time = clock();
    printf("Proceed exit after %.2f seconds\n", static_cast<float>(end_time - start_time) / CLOCKS_PER_SEC);
    printf("Done!\n");
    return 0;
}

输出:

在这里插入图片描述

然后我们进一步进行一定程度的封装,方便我们后续使用

class U2NetModel
{
public:
    U2NetModel(const wchar_t* onnx_model_path);
    float* predict(std::vector<float>input_data,int batch_size=1);
private:
    Ort::Env env;
    Ort::Session session;
    Ort::AllocatorWithDefaultOptions allocator;
    std::vector<const char*>input_node_names;
    std::vector<const char*>output_node_names;
    std::vector<int64_t> input_node_dims;
};
U2NetModel::U2NetModel(const wchar_t* onnx_model_path):session(nullptr),env(nullptr)
{
    //初始化环境,每个进程一个环境,环境保留了线程池和其他状态信息
    this->env=Ort::Env(ORT_LOGGING_LEVEL_WARNING, "u2net");
    //初始化Session选项
    Ort::SessionOptions session_options;
    session_options.SetInterOpNumThreads(1);
    session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);
    // 创建Session并把模型加载到内存中
    this->session=Ort::Session(env, onnx_model_path,session_options);
    //输入输出节点数量和名称
    size_t num_input_nodes = session.GetInputCount();
    size_t num_output_nodes = session.GetOutputCount();
    for (int i = 0; i < num_input_nodes; i++)
    {
        auto input_node_name = session.GetInputName(i, allocator);
        this->input_node_names.push_back(input_node_name);
        Ort::TypeInfo type_info = session.GetInputTypeInfo(i);
        auto tensor_info = type_info.GetTensorTypeAndShapeInfo();
        ONNXTensorElementDataType type = tensor_info.GetElementType();
        this->input_node_dims = tensor_info.GetShape();
    }
    for (int i = 0; i < num_output_nodes; i++)
    {
        auto output_node_name = session.GetOutputName(i, allocator);
        this->output_node_names.push_back(output_node_name);
    }
}
float* U2NetModel::predict(std::vector<float>input_tensor_values,int batch_size)
{
    this->input_node_dims[0] = batch_size;
    auto input_tensor_size = input_tensor_values.size();
    auto memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
    Ort::Value input_tensor = Ort::Value::CreateTensor<float>(memory_info, input_tensor_values.data(), input_tensor_size, input_node_dims.data(), 4);
    auto output_tensors = session.Run(Ort::RunOptions{ nullptr }, input_node_names.data(), &input_tensor, 1, output_node_names.data(), 1);
    assert(output_tensors.size() == 1 && output_tensors.front().IsTensor());
    float* floatarr = output_tensors.front().GetTensorMutableData<float>();
    return floatarr;
}

然后初始化一个示例并调用接口

int main(int argc, char* argv[]) 
{
    auto start_time = std::clock();
    U2NetModel u2net(L"u2net.onnx");
    size_t input_tensor_size = 3 * 320 * 320;
    std::vector<float> input_tensor_values(input_tensor_size);

    //初始化一个数据(演示用)
    for (unsigned int i = 0; i < input_tensor_size; i++)
    {
        input_tensor_values[i] = (float)i / (input_tensor_size + 1);
    }
    float* results = nullptr;
    try
    {
        results = u2net.predict(input_tensor_values);
    }
    catch (Ort::Exception& e)
    {
        delete results;
        printf("%s\n", e.what());
    }
    auto end_time = std::clock();
    printf("Proceed exits after %.2f seconds", static_cast<float>(end_time - start_time) / 1000);
    printf("Done!\n");
    return 0;
}

现在模型部分结束了,但问题是我们其实并没法得知我们的模型运行情况

所以我们还需要读入图片和显示图片,也就是

1.读入一张图片

2.模型推理

3.屏幕打印图片

现在我们仅仅完成了第二部

所以接下来我们需要安装OpenCV



OpenCV

待续…



下载OpenCV

opencv 下载:https://github.com/opencv/opencv/releases



文件复制

打开你的 vs目录 D:\Softs\Microsoft Visual Studio2019Community\VC\Tools\MSVC\14.23.28105\include 直接把opencv目录下该文件夹复制过去

在这里插入图片描述

打开你的 vs目录D:\Softs\Microsoft Visual Studio2019Community\VC\Tools\MSVC\14.23.28105\lib\x64 把这两个文件复制过去

在这里插入图片描述

打开 C:\Windows\System32和C:\Windows\SysWOW64这两个目录 把所有dll复制到这两个目录

在这里插入图片描述

在这里插入图片描述



设置VS2019

注意点 这两地方都选 x64

在这里插入图片描述

附加依赖项选自己的opencv版本

在这里插入图片描述



设置环境变量

win+R打开命令行,输入 SystemPropertiesAdvanced.exe,进行环境变量设置

在这里插入图片描述



写代码测试一下

#include <assert.h>
#include <vector>
#include <ctime>
#include <iostream>
#include <onnxruntime_cxx_api.h>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/videoio.hpp>
int main(int argc, char* argv[]) 
{
    cv::Mat image = cv::imread("horse.jpg");
    cv::resize(image, image, { 320, 320 },0.0,0.0, cv::INTER_CUBIC);
    cv::imshow("test", image);
    cv::waitKey(0);//不加这个会闪退
}

在这里插入图片描述

不过控制台上好像输出了一下动态链接失败的信息,但好像没影响到我现在的使用,就先暂时不管了hhh



借助OpenCV进行模型推理

首先是重载一个新的

predict

函数来支持

cv::Mat

数据

当然这里我新写的版本已经不直接返回

float*

,而是

std::vector<float>

class U2NetModel
{
public:
    ...
    std::vector<float> predict(std::vector<float>& input_data,int batch_size=1,int index=0);
    cv::Mat predict(cv::Mat& input_tensor, int batch_size = 1, int index = 0);
    ...
}

代码实现,增加了对于

cv::Mat

的处理:

std::vector<float> U2NetModel::predict(std::vector<float>& input_tensor_values,int batch_size,int index)
{
    this->input_node_dims[0] = batch_size;
    this->output_node_dims[0] = batch_size;
    float* floatarr = nullptr;
    try
    {
        std::vector<const char*>output_node_names;
        if (index != -1)
        {
            output_node_names = { this->output_node_names[index] };
        }
        else
        {
            output_node_names = this->output_node_names;
        }
        this->input_node_dims[0] = batch_size;
        auto input_tensor_size = input_tensor_values.size();
        auto memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
        Ort::Value input_tensor = Ort::Value::CreateTensor<float>(memory_info, input_tensor_values.data(), input_tensor_size, input_node_dims.data(), 4);
        auto output_tensors = session.Run(Ort::RunOptions{ nullptr }, input_node_names.data(), &input_tensor, 1, output_node_names.data(), 1);
        assert(output_tensors.size() == 1 && output_tensors.front().IsTensor());
        floatarr = output_tensors.front().GetTensorMutableData<float>();
    }
    catch (Ort::Exception&e)
    {
        throw e;
    }
    int64_t output_tensor_size = 1;
    for (auto& it : this->output_node_dims)
    {
        output_tensor_size *= it;
    }
    std::vector<float>results(output_tensor_size);
    for (unsigned i = 0;i < output_tensor_size; i++)
    {
        results[i] = floatarr[i];
    }
    return results;
}
cv::Mat U2NetModel::predict(cv::Mat& input_tensor, int batch_size, int index)
{
    int input_tensor_size = input_tensor.cols * input_tensor.rows * 3;
    std::size_t counter = 0;//std::vector空间一次性分配完成,避免过多的数据copy
    std::vector<float>input_data(input_tensor_size);
    std::vector<float>output_data;
    try
    {
        for (unsigned k = 0; k < 3; k++)
        {
            for (unsigned i = 0; i < input_tensor.rows; i++)
            {
                for (unsigned j = 0; j < input_tensor.cols; j++)
                {
                    input_data[counter++]=static_cast<float>(input_tensor.at<cv::Vec3b>(i, j)[k]) / 255.0;
                }
            }
        }
    }
    catch (cv::Exception& e)
    {
        printf(e.what());
    }
    try
    {
        output_data = this->predict(input_data);
    }
    catch (Ort::Exception& e)
    {
        throw e;
    }
    cv::Mat output_tensor(output_data);
    output_tensor=output_tensor.reshape(1, { 320,320 })*255.0;
    std::cout << output_tensor.rows << " " << output_tensor.cols << "fuck" << std::endl;
    return output_tensor;
}
int main(int argc, char* argv[]) 
{
    U2NetModel model(L"u2net.onnx");
    cv::Mat image = cv::imread("horse.jpg");
    cv::resize(image, image, { 320, 320 },0.0,0.0, cv::INTER_CUBIC);//调整大小到320*320
    cv::imshow("image", image);                                     //打印原图片
    cv::cvtColor(image, image, cv::COLOR_BGR2RGB);                  //BRG格式转化为RGB格式
    auto result=model.predict(image);                               //模型预测
    cv::imshow("result", result);                                   //打印结果
    cv::waitKey(0);
}

在这里插入图片描述

到这里模型的部署和结果的展示就OKK了

不过直接把模型输出转化为图片显然结果并不是非常理想

所以现在还需要对数据进行后处理,对图片进行二值化处理

得到一个Mask掩码矩阵

    cv::Mat output_tensor(output_data);
    output_tensor=255.0-output_tensor.reshape(1, { 320,320 })*255.0;
    cv::threshold(output_tensor, output_tensor, 220, 255, cv::THRESH_BINARY_INV);
    return output_tensor;

在这里插入图片描述

现在结果就已经十分的理想了



完整测试代码

#include <assert.h>
#include <vector>
#include <ctime>
#include <iostream>
#include <onnxruntime_cxx_api.h>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/videoio.hpp>
class U2NetModel
{
public:
    U2NetModel(const wchar_t* onnx_model_path);
    std::vector<float> predict(std::vector<float>& input_data,int batch_size=1,int index=0);
    cv::Mat predict(cv::Mat& input_tensor, int batch_size = 1, int index = 0);
private:
    Ort::Env env;
    Ort::Session session;
    Ort::AllocatorWithDefaultOptions allocator;
    std::vector<const char*>input_node_names;
    std::vector<const char*>output_node_names;
    std::vector<int64_t> input_node_dims;
    std::vector<int64_t> output_node_dims;
};
U2NetModel::U2NetModel(const wchar_t* onnx_model_path):session(nullptr),env(nullptr)
{
    //初始化环境,每个进程一个环境,环境保留了线程池和其他状态信息
    this->env=Ort::Env(ORT_LOGGING_LEVEL_WARNING, "u2net");
    //初始化Session选项
    Ort::SessionOptions session_options;
    session_options.SetInterOpNumThreads(4);
    session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_ALL);
    // 创建Session并把模型加载到内存中
    this->session=Ort::Session(env, onnx_model_path,session_options);
    //输入输出节点数量和名称
    size_t num_input_nodes = session.GetInputCount();
    size_t num_output_nodes = session.GetOutputCount();
    for (int i = 0; i < num_input_nodes; i++)
    {
        auto input_node_name = session.GetInputName(i, allocator);
        this->input_node_names.push_back(input_node_name);
        Ort::TypeInfo type_info = session.GetInputTypeInfo(i);
        auto tensor_info = type_info.GetTensorTypeAndShapeInfo();
        ONNXTensorElementDataType type = tensor_info.GetElementType();
        this->input_node_dims = tensor_info.GetShape();
    }
    for (int i = 0; i < num_output_nodes; i++)
    {
        auto output_node_name = session.GetOutputName(i, allocator);
        this->output_node_names.push_back(output_node_name);
        Ort::TypeInfo type_info = session.GetOutputTypeInfo(i);
        auto tensor_info = type_info.GetTensorTypeAndShapeInfo();
        this->output_node_dims = tensor_info.GetShape();
    }
}
std::vector<float> U2NetModel::predict(std::vector<float>& input_tensor_values,int batch_size,int index)
{
    this->input_node_dims[0] = batch_size;
    this->output_node_dims[0] = batch_size;
    float* floatarr = nullptr;
    try
    {
        std::vector<const char*>output_node_names;
        if (index != -1)
        {
            output_node_names = { this->output_node_names[index] };
        }
        else
        {
            output_node_names = this->output_node_names;
        }
        this->input_node_dims[0] = batch_size;
        auto input_tensor_size = input_tensor_values.size();
        auto memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
        Ort::Value input_tensor = Ort::Value::CreateTensor<float>(memory_info, input_tensor_values.data(), input_tensor_size, input_node_dims.data(), 4);
        auto output_tensors = session.Run(Ort::RunOptions{ nullptr }, input_node_names.data(), &input_tensor, 1, output_node_names.data(), 1);
        assert(output_tensors.size() == 1 && output_tensors.front().IsTensor());
        floatarr = output_tensors.front().GetTensorMutableData<float>();
    }
    catch (Ort::Exception&e)
    {
        throw e;
    }
    int64_t output_tensor_size = 1;
    for (auto& it : this->output_node_dims)
    {
        output_tensor_size *= it;
    }
    std::vector<float>results(output_tensor_size);
    for (unsigned i = 0;i < output_tensor_size; i++)
    {
        results[i] = floatarr[i];
    }
    return results;
}
cv::Mat U2NetModel::predict(cv::Mat& input_tensor, int batch_size, int index)
{
    int input_tensor_size = input_tensor.cols * input_tensor.rows * 3;
    std::size_t counter = 0;//std::vector空间一次性分配完成,避免过多的数据copy
    std::vector<float>input_data(input_tensor_size);
    std::vector<float>output_data;
    try
    {
        for (unsigned k = 0; k < 3; k++)
        {
            for (unsigned i = 0; i < input_tensor.rows; i++)
            {
                for (unsigned j = 0; j < input_tensor.cols; j++)
                {
                    input_data[counter++]=static_cast<float>(input_tensor.at<cv::Vec3b>(i, j)[k]) / 255.0;
                }
            }
        }
    }
    catch (cv::Exception& e)
    {
        printf(e.what());
    }
    try
    {
        output_data = this->predict(input_data);
    }
    catch (Ort::Exception& e)
    {
        throw e;
    }
    cv::Mat output_tensor(output_data);
    output_tensor=255.0-output_tensor.reshape(1, { 320,320 })*255.0;
    cv::threshold(output_tensor, output_tensor, 220, 255, cv::THRESH_BINARY_INV);
    
    return output_tensor;
}
int main(int argc, char* argv[]) 
{
    U2NetModel model(L"u2net.onnx");
    cv::Mat image = cv::imread("horse.jpg");
    cv::resize(image, image, { 320, 320 },0.0,0.0, cv::INTER_CUBIC);//调整大小到320*320
    cv::imshow("image", image);                                     //打印原图片
    cv::cvtColor(image, image, cv::COLOR_BGR2RGB);                  //BRG格式转化为RGB格式
    auto result=model.predict(image);                               //模型预测
    cv::imshow("result", result);                                   //打印结果
    cv::waitKey(0);
}



版权声明:本文为qq_19865329原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。