在(一)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)
參考:Python調用c++的動態dll中數據映射(Mat類型傳遞及結構體傳遞)
Catalog One 、 Image data type
Resource download address :htt