• (二)ndarray(Python)与Mat(C++)数据的传输



    该系列文章:
    (一)python调用c++代码《从C++共享链接库编译到python调用指南》
    (二)ndarray(Python)与Mat(C++)数据的传输
    (三)C++结构体与python的传输

    1、前言

    (一)python调用c++代码《从C++共享链接库编译到python调用指南》中,我们已经实现了在python中调用C++代码的效果。但是新的问题产生了,python中opencv的图像数据是ndarray格式,C++中opencv的图像数据是Mat格式,在C++中定义的test函数入参是Mat数据,在python中调用是不能直接将ndarray数据作为test函数的参数。

    2、ndarray和Mat类型转换

    该部分我们将设计c++函数接收并返回图像数据,你可以看到图像数据是如何在python的ndarray和c++的Mat直接流转的。

    2.1 c++代码

    在这里我们定义一个MatSo类,该类有两个函数,一个函数接收uchar类型(图像数据),并返回uchar类型(图像数据);另一个函数接收uchar*类型(图像数据),并返回buffer类型(图像数据)。

    // Dll2.cpp: 定义 DLL 应用程序的导出函数。
    #define EXPORT __declspec(dllexport)
    #include <opencv2/opencv.hpp>
    #include <iostream>
    
    using namespace cv;
    using namespace std;
    
    // MatSO声明
    class MatSO
    {
        public:
            // 该函数接收uchar数据,转为Mat数据,显示,然后再返回uchar数据
            uchar *get_mat_and_return_uchar(uchar *matrix, int rows, int cols, int channels);
    
            // 该函数接收uchar数据,转为Mat数据,显示,然后再返回buffle数据
            uchar *get_mat_and_return_buffer(uchar *matrix, int rows, int cols, int channels);
    };
    
    
    // mat数据接收并以uchar返回
    uchar *MatSO::get_mat_and_return_uchar(uchar *matrix, int rows, int cols, int channels)
    {
        // 将接收的uchar转为Mat
        cout << "rows=" << rows << "\ncols=" << cols << "\nchannels=" << channels;
        Mat input_mat = Mat(Size(cols, rows), CV_8UC3, Scalar(255, 255, 255));
        input_mat.data = matrix;
    
        // 显示Mat
        imshow("input_mat", input_mat);
        cv::waitKey(0);
    
        // 将Mat转为uchar类型并返回
        // 注意:要记住这里Mat的rol、row和channels,在python中才能正确接收
        uchar *s = input_mat.data; // Mat转ucahr*
        return s;
    }
    
    
    // mat数据接收并以buffer返回
    uchar *MatSO::get_mat_and_return_buffer(uchar *matrix, int rows, int cols, int channels)
    {
        // 将接收的uchar转为Mat
        cout << "rows=" << rows << "\ncols=" << cols << "\nchannels=" << channels;
        Mat input_mat = Mat(Size(cols, rows), CV_8UC3, Scalar(255, 255, 255));
        input_mat.data = matrix;
    
        // 显示Mat
        imshow("input_mat", input_mat);
        cv::waitKey(0);
    
        // 将Mat转为buffer并返回
        // 注意:要记住这里Mat的rol、row和channels,在python中才能正确接收
        int height = input_mat.cols;
        int width = input_mat.rows;
        uchar *buffer = (uchar *)malloc(sizeof(uchar) * height * width * 3);
        memcpy(buffer, input_mat.data, height * width * 3);
        return buffer;
    }
    
    extern "C"
    {
        MatSO td; // 包装MatSO,使之可以在so文件外调用
    
        uchar *get_mat_and_return_uchar(uchar *matrix, int rows, int cols, int channels)
        {
            return td.get_mat_and_return_uchar(matrix, rows, cols, channels);
        }
    
        uchar *get_mat_and_return_buffer(uchar *matrix, int rows, int cols, int channels)
        {
            return td.get_mat_and_return_buffer(matrix, rows, cols, channels);
        }
    }
    
    
    • 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
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    顺便给出CMakeLists.txt配置:

    cmake_minimum_required(VERSION 3.0.0)
    project(hbp VERSION 0.1.0)  # 项目名称
    
    set(CMAKE_CXX_FLAGS "-std=c++11")   # 添加c++11标准
    
    find_package(OpenCV REQUIRED)
    include_directories(${OpenCV_INCLUDE_DIRS}) # 头文件目录
    
    add_library(MatSo SHARED MatSo.cpp)  # 设置输出的库的类型
    target_link_libraries(MatSo ${OpenCV_LIBS})
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2.2 python中调用MatSo::get_mat_and_return_uchar()

    在这里我们要完成cv2读取一张图片,并调用MatSo类中的函数,得到返回的数据并显示。

    主要步骤为:

    1. 加载so共享链接库
    2. 定义要使用函数的入参和出参类型
    3. 对入参数据进行转换为目的类型
    4. 调用
    5. 对返回数据进行类型转换
    import cv2
    import ctypes
    from ctypes import *
    import numpy as np
    
    
    # 加载共享链接库
    matso = ctypes.cdll.LoadLibrary("build/libMatSo.so")
    
    # matso中有两个函数我们会使用到
    # 现在对这两个函数定义入参和出参的类型
    # 参考:https://blog.csdn.net/qq_40047008/article/details/107785856
    matso.get_mat_and_return_uchar.argtypes = (POINTER(c_ubyte), c_int, c_int, c_int)
    matso.get_mat_and_return_uchar.restype = POINTER(c_ubyte)
    
    img = cv2.imread("face.jpeg")
    rows, cols, channels = img.shape
    
    img = img.ctypes.data_as(POINTER(c_ubyte))  # 将ndarray转为c++的uchar类型
    
    return_uchar_data = matso.get_mat_and_return_uchar(img, rows, cols, channels)   # 调用链接库函数,得到uchar*数据
    
    # 注意这里的rows、rows和channels是C++函数中返回时的Mat尺寸,与上面中不是同一个意思
    # 但是因为上面传入函数并返回过程中没有改变图像的shape,所以在数值上是一样的
    np_canny = np.array(np.fromiter(return_uchar_data, dtype=np.uint8, count=cols * rows * channels))
    np_canny = np_canny.reshape((rows, cols, 3))
    
    cv2.imshow("q", np_canny)
    cv2.waitKey(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

    在这里插入图片描述

    2.3 python中调用MatSo::get_mat_and_return_buffer()

    get_mat_and_return_uchar和get_mat_and_return_buffer的区别在于返回类型不同,分别是uchar*和buffer,但是从函数类型可以看出都是uchar,所以在python中的代码都一样

    import cv2
    import ctypes
    from ctypes import *
    import numpy as np
    
    
    # 加载共享链接库
    matso = ctypes.cdll.LoadLibrary("build/libMatSo.so")
    
    # matso中有两个函数我们会使用到
    # 现在对这两个函数定义入参和出参的类型
    # 参考:https://blog.csdn.net/qq_40047008/article/details/107785856
    matso.get_mat_and_return_buffer.argtypes = (POINTER(c_ubyte), c_int, c_int, c_int)
    # matso.get_mat_and_return_buffer.restype = POINTER(c_uint8)
    matso.get_mat_and_return_buffer.restype = POINTER(c_ubyte)
    
    img = cv2.imread("face.jpeg")
    rows, cols, channels = img.shape
    
    img = img.ctypes.data_as(POINTER(c_ubyte))  # 将ndarray转为c++的uchar类型
    
    return_uchar_data = matso.get_mat_and_return_buffer(img, rows, cols, channels)   # 调用链接库函数,得到uchar*数据
    
    # 注意这里的rows、rows和channels是C++函数中返回时的Mat尺寸,与上面中不是同一个意思
    # 但是因为上面传入函数并返回过程中没有改变图像的shape,所以在数值上是一样的
    np_canny = np.array(np.fromiter(return_uchar_data, dtype=np.uint8, count=cols * rows * channels))
    np_canny = np_canny.reshape((rows, cols, 3))
    
    cv2.imshow("q", np_canny)
    cv2.waitKey(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
  • 相关阅读:
    MongoDB数据库协议解析及C/C++代码实现
    linux jar包class热部署 工具 arthas安装及使用
    matplotlib中关于极坐标轴的控制
    分布式Trace:横跨几十个分布式组件的慢请求要如何排查?
    Leetcode 877. 石子游戏
    决策 AI:以高效落地为目标的工程技术
    Packet Tracer - 研究 DUAL FSM
    HTML5(第一部分)
    Ubuntu 中Gitbook 使用笔记
    jvm查看永久区内存类加载情况
  • 原文地址:https://blog.csdn.net/qq_40243750/article/details/125489067