一、简单使用
ctypes — Python 的外部函数库 【中文文档】
字符串传递
ctypes
定义了一些和C兼容的基本数据类型:
ctypes 类型 | C 类型 | Python 类型 |
---|---|---|
c_bool |
_Bool | bool (1) |
c_char |
char | 单字符字节串对象 |
c_wchar |
wchar_t |
单字符字符串 |
c_byte |
char | int |
c_ubyte |
unsigned char | int |
c_short |
short | int |
c_ushort |
unsigned short | int |
c_int |
int | int |
c_uint |
unsigned int | int |
c_long |
long | int |
c_ulong |
unsigned long | int |
c_longlong |
__int64 或 long long |
int |
c_ulonglong |
unsigned __int64 或 unsigned long long | int |
c_size_t |
size_t |
int |
c_ssize_t |
ssize_t 或 Py_ssize_t |
int |
c_float |
float | float |
c_double |
double | float |
c_longdouble |
long double | float |
c_char_p |
char* (以 NUL 结尾) | 字节串对象或 None |
c_wchar_p |
wchar_t* (以 NUL 结尾) | 字符串或 None |
c_void_p |
void* | int 或 None |
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<iostream>
#define DLLEXPORT extern "C" __declspec(dllexport)
DLLEXPORT float __stdcall add(int a, float b) {
printf("a=%d\n", a);
printf("b=%f\n", b);
return a + b;
}
DLLEXPORT char* __stdcall get_ascii_str(char* path)
{
std::cout << "path:" << path << std::endl;
char* ret;
ret = (char*)malloc(24);
strcpy(ret, "hello word123,./");
return ret;
}
DLLEXPORT char* __stdcall get_utf8_str(char* path)
{
std::cout << "path:" << path << std::endl;
char* ret;
ret = (char*)malloc(24);
strcpy(ret, "你好hello word123,./");
return ret;
}
//构建字符串数组,2个元素
typedef struct struct_str_arr
{
const char* str_ptr[2];
} str_arr;
struct_str_arr* str_arr_ptr = (struct_str_arr*)malloc(sizeof(str_arr));
DLLEXPORT struct_str_arr* __stdcall get_str_list(int n, char* b[2])
{
for (int i = 0; i < n; i++)
{
printf("%s", *(b + i));
printf("\n");
}
str_arr_ptr->str_ptr[0] = "你好";
str_arr_ptr->str_ptr[1] = "hell";
return str_arr_ptr;
}
test.py
# -*- coding: utf-8 -*-
from ctypes import *
# ==================================
# dll =CDLL("./ctype_test1.dll")
# dll = cdll.LoadLibrary("./ctype_test1.dll")
# dll = windll.LoadLibrary("./ctype_test1.dll")
# ==================================
dll = PyDLL("./ctype_test1.dll")
dll.add.argtypes=[c_int,c_float]
dll.add.restype=c_float
a=c_int(10)
b=c_float(20.5)
res= dll.add(a,b)
print(res)
print("#######################################")
dll.get_ascii_str.argtypes=[c_char_p]
dll.get_ascii_str.restype=c_char_p
tex= b"dsf123,./"
text = c_char_p(tex)
rt_str=dll.get_ascii_str(text)
print(rt_str)
dll.get_utf8_str.argtypes=[c_char_p]
dll.get_utf8_str.restype=c_char_p
tex= "你好呀dsf123,./".encode()
text = c_char_p(tex)
rt_str=dll.get_utf8_str(text)
print(rt_str.decode('gbk'))
print("#######################################")
# 返回的数据
class StructPointer(Structure):
_fields_ = [("str_ptr", c_char_p * 2)]
dll.get_str_list.restype = POINTER(StructPointer)
str1 = c_char_p(bytes("nihao", 'utf-8'))
str2 = c_char_p(bytes("shijie", 'utf-8'))
a = (c_char_p*2)(str1, str2)
ret_ptr =dll.get_str_list(2, a)
print(ret_ptr.contents.str_ptr[0].decode("gbk"))
print(ret_ptr.contents.str_ptr[1].decode("gbk"))
二、结构体
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <iostream>
#include <opencv2/opencv.hpp>
#ifdef _MSC_VER
#define DLL_EXPORT extern "C" __declspec(dllexport)
#else
#define DLL_EXPORT extern "C"
#endif
DLL_EXPORT float __stdcall add(int a, float b) {
printf("a=%d\n", a);
printf("b=%f\n", b);
return a + b;
}
DLL_EXPORT void add_point(float* a, float* b, float* c)
{
*c = *a + *b;
*a = 129.7;
}
//作为返回值返回
DLL_EXPORT uchar* mattostring(uchar* src_data, int rows, int cols, int* size)
{
cv::Mat dst = cv::Mat(cv::Size(cols, rows), CV_8UC3, cv::Scalar(255, 255, 255));
dst.data = src_data;
circle(dst, cv::Point(60, 60), 10, cv::Scalar(255, 0, 0));
std::vector<uchar> data_encode;
cv::imencode(".png", dst, data_encode);
std::string str_encode(data_encode.begin(), data_encode.end());
uchar* char_r = new uchar[str_encode.size() + 10];
*size = str_encode.size() + 10;
memcpy(char_r, str_encode.data(), sizeof(char) * (str_encode.size()));
return char_r;
}
//作为入参指针传递
DLL_EXPORT void draw_circle(int rows, int cols, unsigned char* src_data, unsigned char* ret_data)
{
cv::Mat src = cv::Mat(rows, cols, CV_8UC3, src_data); // uchar* 转cvMat
cv::circle(src, cv::Point(60, 60), 10, cv::Scalar(255, 0, 0)); // 画图
memcpy(ret_data, src.data, rows * cols * 3); // 将Mat转换成unsigned char
}
test.py
from ctypes import *
import cv2
import numpy as np
from PIL import Image
MYDLL= CDLL("./x64/Release/ctype_test2.dll", winmode=0)
x1 = c_float(1.9)
x2 = c_float(10.1)
x3 = c_float(0)
MYDLL.add_point(byref(x1),byref(x2),byref(x3))
print("x1=",x1)
print("x2=",x2)
print("x3=",x3)
image=cv2.imread("./20220818142610.png")
rows = image.shape[0]
cols = image.shape[1]
channels =3
print("[cols*rows*channels]",cols*rows*channels)
MYDLL.mattostring.argtypes = (POINTER(c_ubyte), c_int,c_int, POINTER(c_int)) # c_char_p也可以
MYDLL.mattostring.restype = c_void_p # POINTER(c_ubyte) 跟c_void_p都可以
#MYDLL.mattostring.restype = POINTER(c_uint64) # POINTER(c_ubyte) 跟c_void_p都可以
MYDLL.draw_circle.argtypes=[c_int,c_int,POINTER(c_ubyte),POINTER(c_ubyte)]
MYDLL.draw_circle.restype=c_void_p
srcPointer=image.ctypes.data_as(POINTER(c_ubyte)) #方式1.1
# srcPointer=image.ctypes.data_as(c_char_p) #方式1.2
# srcPointer = image.astype(np.uint8).tostring() #方式2
size = c_int(0)
a = MYDLL.mattostring(srcPointer,rows,cols,byref(size))
#b = string_at(a,cols*rows*channels) # 类似于base64
print("[size]=",size.value)
b = string_at(a,size.value) # 类似于base64
# b = string_at(a,99132) # 类似于base64
nparr = np.frombuffer(b, np.uint8)
img_decode= cv2.imdecode(nparr,cv2.IMREAD_COLOR) #转cvMat
cv2.imshow("img_decode", img_decode)
cv2.waitKey(0)
cv2.destroyAllWindows()
img_decode=Image.fromarray(img_decode[:,:,::-1]) # 由于直接cv2.imshow()显示出来的图是错误的,保存或者转为Image格式,显示正确
img_decode.show()
ret_img = np.zeros(dtype=np.uint8, shape=(rows, cols, 3))
retPoint = ret_img.ctypes.data_as(POINTER(c_ubyte))
MYDLL.draw_circle(rows, cols, srcPointer, retPoint)
cv2.imshow("ret_img", ret_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
ret_img_out = Image.fromarray(ret_img[:,:,::-1]) # 参数指针传递,不需要从uchar*转换,只需要取他的源头数据即可.
ret_img_out.show()
三、Mat2string
#include <iostream>
#include <memory.h>
#include <string>
#include <opencv2/opencv.hpp>
void Mat_to_string(cv::Mat& image, std::string& Imagestring)
{
std::vector<uchar> buff;
cv::imencode(".jpg", image, buff);
std::string image_string(reinterpret_cast<char*>(&buff[0]), buff.size());
Imagestring = image_string;
}
void string_to_Mat(std::string& Imagestring, cv::Mat& image)
{
std::vector<char> vec_data(Imagestring.c_str(), Imagestring.c_str() + Imagestring.size());
cv::Mat dst = cv::imdecode(vec_data, cv::IMREAD_UNCHANGED);
image = dst;
}
int main(int argc, char* argv[])
{
cv::Mat image = cv::imread("E:\\Desktop\\20220818142610.png");
if (image.empty())
{
std::cout << "image is empty" << std::endl;
}
std::string Imagestring;
Mat_to_string(image, Imagestring);
cv::Mat dst;
string_to_Mat(Imagestring, dst);
cv::imshow("dst", dst);
cv::waitKey(0);
cv::destroyAllWindows();
return 0;
}
示例,传递ndarray给cpp,并返回string/ndarray
#pragma once
#ifdef __cplusplus
extern "C"
{
#endif
#ifdef _WIN32
#ifndef _TEST_EXPORT_API
#define _TEST_API __declspec(dllexport)
#else
#define _TEST_API __declspec(dllimport)
#endif
#else
#define _TEST_API
#endif
typedef void* TESTHANDLE;
typedef char BOOL;
#ifndef NULL
#define NULL 0
#endif
#define TRUE 1
#define FALSE 0
_TEST_API void TESTLableDetect(TESTHANDLE handle, unsigned char* imgPtr, int height, int width, int step, char* result)
{
cv::Mat image = cv::Mat(height, width, CV_8UC3, imgPtr, step);
TESTLable * pAslObj = (TESTLable*)handle;
std::string result_string;
if (pAslObj)
pAslObj->detect(image, result_string);
strncpy(result, result_string.c_str(), result_string.size());
}
_TEST_API void TESTDetect(TESTHANDLE handle, unsigned char* imgPtr, int height, int width, int step, unsigned char* retPtr)
{
cv::Mat image = cv::Mat(height, width, CV_8UC3, imgPtr, step);
cv::Mat outImage;
TEST* pTESTObj = (TEST*)handle;
std::string result_string;
if (pTESTObj)
pTESTObj->detect(image, outImage);
std::memcpy(retPtr, outImage.data, height * width);
}
#ifdef __cplusplus
}
#endif
class TEST:
def __init__(self, tres_path, label_path):
self.MYDLL = CDLL("./libtest.so")
#############################################
# input output arg
#############################################
self.MYDLL.TESTInit.argtypes = [c_char_p, c_char_p]
self.MYDLL.TESTInit.restype = POINTER(c_ubyte)
self.MYDLL.TESTDetect.argtypes = [POINTER(c_ubyte), POINTER(c_ubyte), c_int, c_int, c_int, c_char_p]
self.MYDLL.TEST2Detect.argtypes = [POINTER(c_ubyte), POINTER(c_ubyte), c_int, c_int, c_int, POINTER(c_ubyte)]
self.MYDLL.TESTDetect.restype = c_void_p
self.MYDLL.TESTRelease.argtypes = [POINTER(c_ubyte)]
self.MYDLL.TESTRelease.restype = c_void_p
self.ASLgcnHandle = self.MYDLL.TESTInit(c_char_p(tres_path),c_char_p(label_path))
def predict_test(self, image):
height, width, channels = image.shape
if 4 == channels:
image = cv2.cvtColor(image, cv2.COLOR_BGRA2BGR)
elif 1 == channels:
image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
src_pointer = image.ctypes.data_as(POINTER(c_ubyte))
result = create_string_buffer(550)
self.MYDLL.TESTDetect(self.ASLgcnHandle, src_pointer, height, width, width * channels, result)
return result.value.decode('utf-8')
def predict_test2(self, image):
height, width = image.shape[:2]
image_resized = cv2.resize(image, (512, 512))
src_pointer = image_resized.ctypes.data_as(POINTER(c_ubyte))
ret_img = np.zeros((512, 512), dtype=np.uint8)
retPoint = ret_img.ctypes.data_as(POINTER(c_ubyte))
self.MYDLL.TEST2Detect(self.mvss_Handle, src_pointer, 512, 512, 512*3, retPoint)