在(一)python调用c++代码《从C++共享链接库编译到python调用指南》中,我们已经实现了在python中调用C++代码的效果。但是新的问题产生了,python中opencv的图像数据是ndarray格式,C++中opencv的图像数据是Mat格式,在C++中定义的test函数入参是Mat数据,在python中调用是不能直接将ndarray数据作为test函数的参数。
该部分我们将设计c++函数接收并返回图像数据,你可以看到图像数据是如何在python的ndarray和c++的Mat直接流转的。
在这里我们定义一个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})
在这里我们要完成cv2读取一张图片,并调用MatSo类中的函数,得到返回的数据并显示。
主要步骤为:
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)
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)