• 【深度学习】【Opencv】Python/C++调用onnx模型【基础】


    【深度学习】【Opencv】python/C++调用onnx模型【基础】

    提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论


    前言

    OpenCV是一个基于BSD许可发行的跨平台计算机视觉和机器学习软件库(开源),可以运行在Linux、Windows、Android和Mac OS操作系统上。可以将pytorch中训练好的模型使用ONNX导出,再使用opencv中的dnn模块直接进行加载使用。
    系列学习目录:
    【CPU】Pytorch模型转ONNX模型流程详解
    【GPU】Pytorch模型转ONNX格式流程详解
    【ONNX模型】快速部署
    【ONNX模型】多线程快速部署
    【ONNX模型】opencv_cpu调用onnx
    【ONNX模型】opencv_gpu调用onnx


    Python版本OpenCV

    Windows平台安装OpenCV

    博主在win10环境下装anaconda环境,而后搭建onnx模型运行所需的openCV环境。

    # 搭建opencv环境
    conda create -n opencv_onnx_cpu python=3.10 -y
    # 激活环境
    activate opencv_onnx_cpu
    # 安装opencv
    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple opencv-python
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    查看opencv版本

    import cv2
    cv2.__version__
    
    • 1
    • 2

    opencv调用onnx模型

    现在代码抛开任何pytorch相关的依赖,也抛开了onnx相关的依赖,只使用opencv完成了整个推理流程。

    import cv2
    import numpy as np
    
    def normalizeImage(image,mean,std):
        normalized = image.astype(np.float32)
        normalized = normalized / 255.0 - mean
        normalized = normalized / std
        return normalized
    
    def main():
        # 读取图片
        image = cv2.imread(r"./animal-1.jpg")
        # 将BGR图像转换为RGB格式
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        # 获取图像的大小
        ori_w, ori_h, = image.shape[0], image.shape[1]
    
        # 指定调整后的大小
        new_width = 416
        new_height = 416
        # 图片尺寸缩放
        resized_img = cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_AREA)
    
        # 定义每个通道的归一化参数
        mean = np.array([0.485, 0.456, 0.406]).astype(np.float32)  # 均值
        std = np.array([0.229, 0.224, 0.225]).astype(np.float32)  # 标准差
        # 图片归一化
        normalized = normalizeImage(resized_img, mean, std)
    
        # 加载ONNX模型
        net = cv2.dnn.readNetFromONNX("PFNet.onnx")  # 加载训练好的识别模型
        # onnx是多输出,每个输出都会对应一个name,因此需要获取所有输出的name
        output_layer_names = net.getUnconnectedOutLayersNames()
        blob = cv2.dnn.blobFromImage(normalized)  # 由图片加载数据 这里还可以进行缩放、归一化等预处理
        # 将Blob设置为模型的输入
        net.setInput(blob)
        # 运行前向传播,将所有输出name作为参数传递
        out = net.forward(output_layer_names)
        out = np.squeeze(out[3]) * 255.0
        output = cv2.resize(out, (ori_h, ori_w), interpolation=cv2.INTER_AREA)
    
        # 保存图像
        cv2.imwrite('saved_opencv_python_image.png', output)
    
    if __name__ == '__main__':
        main()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46


    C++版本OpenCV

    Windows平台安装OpenCV

    官网下载安装文件地址,博主使用opencv-4.8.0-windows.exe版本

    双击运行解压后即可,博主重命名为opencv_cpu:

    打开VS 2019:新建新项目---->空项目---->配置项目---->项目路径以及勾选“将解决方案和项目放在同一目录中---->点击创建。
    在解决方案–>源文件–>右键添加新建项。这里暂时可以默认空着不做处理。

    设置OpenCV路径:项目---->属性。假设没有新建cpp文件,空项目的属性页就不会存在C/C++这一项目。

    添加附加包含目录:Release | x64---->C/C+±—>常规---->附加包含目录。

    D:\C++_demo\opencv_cpu\build\x64\vc16\bin
    D:\C++_demo\opencv_cpu\build\bin
    D:\C++_demo\opencv_cpu\build\include
    D:\C++_demo\opencv_cpu\build\include\opencv2
    
    • 1
    • 2
    • 3
    • 4

    链接器:Release | x64---->链接器---->常规---->附加包含目录。

    D:\C++_demo\opencv_cpu\build\x64\vc16\lib
    
    • 1

    链接器:Release | x64---->链接器---->输入---->附加依赖项。

    在D:\C++_demo\opencv_cpu\build\x64\vc16\lib下找到附加依赖项的文件。

    opencv_world480.lib
    
    • 1

    在Release x64模式下测试,将opencv_world480.dll文件复制到自己项目的Release下。

    没有Release目录时,需要在Release | x64模式下运行一遍代码,代码部分在下一节提供,读者可以先行新建文件复制代码。

    D:\C++_demo\opencv_cpu\build\x64\vc16\bin
    ===>
    D:\C++_demo\opencv_onnx_cpu\x64\Releas
    
    • 1
    • 2
    • 3


    这里博主为了方便安装的是release版本的,读者可以安装debug版本的,流程基本一致,只需要将属性的Release | x64变成Debug | x64,然后附加依赖项由opencv_world480.lib变成opencv_world480d.lib,再将opencv_world480d.dll文件复制到自己项目的Release下。

    opencv调用onnx模型

    将PFNet.onnx拷贝到项目路径下。

    简单使用

    这里简单验证一下opencv是否安装成功,适用于包括博主在内的许多对c++不熟悉的人来说,代码完成了简单的图像的读取与显示。

    #include 
    using namespace cv;
    using namespace std;
    int main(int argc, char** argv) {
    	Mat src = imread("./animal-1.jpg");
    	//没有图像输入
    	if (src.empty()) {
    		printf("....\n");
    		return -1;
    	}
    	//namedWindow("输入窗口", WINDOW_FREERATIO);
    	imshow("输入窗口", src);
    	waitKey(0);
    	destroyAllWindows();
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    调用onnx模型

    将python版本的opencv转化成对应的c++版本的,发现输出的效果完全一致,onnx模型可以作为c++的接口来供其他应用调用。

    #include 
    #include 
    #include 
    #include
    #include 
    using namespace std;
    cv::Mat normalizeImage(const cv::Mat& image, const cv::Scalar& mean, const cv::Scalar& std) {
        cv::Mat normalized;
        image.convertTo(normalized, CV_32F);
        cv::subtract(normalized / 255.0, mean, normalized);
        cv::divide(normalized, std, normalized);
        return normalized;
    }
    int main()
    {   
        // 读取图片
        cv::Mat bgrImage = cv::imread("./animal-1.jpg", cv::IMREAD_COLOR);
        // 图片格式转化bgr-->rgb
        cv::Mat rgbImage;
        cv::cvtColor(bgrImage, rgbImage, cv::COLOR_BGR2RGB);
        // 获取图像的大小
        cv::Size originalSize(rgbImage.cols, rgbImage.rows);
        cv::Mat resizedImage;
    
        // 定义目标图像大小
        cv::Size targetSize(416, 416);
        //图片尺寸缩放
        cv::resize(rgbImage, resizedImage, targetSize, 0, 0, cv::INTER_AREA);
    
        // 定义每个通道的归一化参数
        cv::Scalar mean(0.485, 0.456, 0.406); // 均值
        cv::Scalar std(0.229, 0.224, 0.225);  // 标准差
        // 图片归一化
        cv::Mat normalized = normalizeImage(resizedImage, mean, std);
        
        // 加载ONNX模型
        cv::dnn::Net net = cv::dnn::readNetFromONNX("D:/C++_demo/opencv_onnx_cpu/PFNet.onnx");
        cv::Mat blob = cv::dnn::blobFromImage(normalized);
        // 将Blob设置为模型的输入
        net.setInput(blob);
        // 运行前向传播
        std::vector<cv::Mat> output_probs;
        // 获取多输出对应的名称
        std::vector<cv::String> output_layer_names = net.getUnconnectedOutLayersNames();
        net.forward(output_probs, output_layer_names);
        cv::Mat prediction = output_probs[3];
        cv::Mat mask;
        cv::resize(prediction.reshape(1, 416) * 255.0, mask, originalSize, 0, 0, cv::INTER_AREA);
        cv::imwrite("saved_opencv_c++_image.png", mask);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51


    总结

    尽可能简单、详细的介绍Python和C++下Opencv_CPU调用ONNX模型的流程。

  • 相关阅读:
    基于SpringMVC+Hibernate开发学生成绩管理系统
    Websocket的基本认识、使用与封装
    SpringMVC识 乱码过滤+ajax请求+SpringMVC四种跳转方式和默认的参数类型以及日期处理
    关于数字化转型的know how
    Java字节码增强技术(NoClassDefFoundError)问题
    数据链路层
    yolov5训练可视化指标的含义
    Mysql 查询患某种疾病的患者
    挑战字节软件测试岗,原来这么轻松...
    关于Linux安装OpenLDAP说明
  • 原文地址:https://blog.csdn.net/yangyu0515/article/details/132894953