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

  • Post author:
  • Post category:python


该系列文章:


(一)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);
    }
}

顺便给出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})



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)

在这里插入图片描述



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)



End

参考:

Python调用c++的动态dll中数据映射(Mat类型传递及结构体传递)



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