Python与C/C++调用之ctypes,以及二级指针
-
python访问C/C++
- python的底层大部分都是C/C++实现,python和C和C++具有天然的互相调用优势;
- 很多核心的算法库都是C/C++写的,在python开发过程中,经常访问别人的动态库;
- 知名人工智能(深度学习)框架训练系统都是python写的,而运行时一般都是以动态库的形式提供;
-
python访问C/C++的方式
- ctypes;
- pybind11;
- cffi
- swig
-
ctypes的优势
- 不要修改动态库的源码;
- 只需要动态库和头文件;
- 使用比较简单,而且目前大部分库都是兼容C/C++;
本文以一个典型的深度学习(人工智能AI)的图像检测的python自动化测试,介绍ctypes的使用;
- ctypes的使用
结构体头文件:
接口头文件:
#pragma once #include "mt_image_common.h" CV_IMAGE_API mt_result_t mt_image_detect_init_config(const char* congif); CV_IMAGE_API mt_result_t mt_image_detect_create(const char* model_path, mt_handle_t* handle); CV_IMAGE_API void mt_image_detect_destroy(mt_handle_t handle); CV_IMAGE_API void mt_image_release_detect_result(detection_result_t* detection_result, int count); CV_IMAGE_API mt_result_t mt_image_detect_compact(mt_handle_t handle, const unsigned char* img, int format, int image_width, int image_height, int image_stride, detection_result_t** detect_info, int* count); CV_IMAGE_API mt_result_t mt_image_detect_reset(mt_handle_t handle);
结构体的映射:
from ctypes import * import os import shutil class rect_t(Structure): pass rect_t._fields_ = [ ('left', c_int), ('top', c_int), ('right', c_int), ('bottom', c_int), ] class point3f_t(Structure): pass point3f_t._fields_ = [ ('x', c_float), ('y', c_float), ('z', c_float), ] class extra_info(Structure): pass extra_info._fields_ = [ ('mvp_mat', c_float*3*3), ('point_t', POINTER(point3f_t)), ('point_count', c_int), ] class detection_result(Structure): pass detection_result._fields_ = [ ('rect', rect_t), ('score', c_float), ('label', c_int), ('orientation', c_int), ('extra_info', extra_info), ] def movefile(srcpath, dstpath): if not os.path.isfile(srcpath): print(srcpath + ' is not exist!') else: fpath, fname = os.path.split(dstpath) if not os.path.exists(fpath): os.makedirs(fpath) shutil.copy(srcpath, dstpath) print('copy ' + srcpath + '->' + dstpath)
接口映射:
import ctypes import os class MtLibrary: def __init__(self, path): self.path = path self.lib = None self.hasInit = False def load_library(self): dl = ctypes.cdll.LoadLibrary print('load_library lib is Exist : ' + str(os.path.exists(self.path))) print(os.getcwd()) lib = dl(self.path) self.lib = lib self.hasInit = True def init_license(self, licence): if not self.hasInit: print('lib has not init!!') return False licence_context = bytes(licence, "utf8") return self.lib.mt_image_detect_init_config(licence_context) def create_handle(self, path, handle): if not self.hasInit: print('lib has not init!!') return None return self.lib.mt_image_detect_create(path, handle) def reset_handle(self, handle): return self.lib.mt_image_detect_reset(handle) def detect_image(self, handle, image, format, width, height, stride, detect_info, count): if not self.hasInit: print('lib has not init!!') return None return self.lib.mt_image_detect_compact(handle, image, format, width, height, stride, detect_info, count) def release_result(self, detect_result, count): if not self.hasInit: print("lib has not init!!") return None return self.lib.mt_image_release_detect_result(detect_result, count) def destroy_handle(self, handle): if not self.hasInit: print("lib has not init!!") return None return self.lib.mt_image_detect_destroy(handle)
重点问题:
- 结构体和复杂结构提的映射
C中的结构体
typedef struct extra_info_t{ float mvp_mat[3][3]; point_t *points_ori; int point_count; }extra_info_t; typedef struct detection_result_t{ rect_t rect; float score; int label; int orientation; extra_info_t extra_info; } detection_result_t;
Python中的类
class extra_info(Structure): pass extra_info._fields_ = [ ('mvp_mat', c_float*3*3), ('point_t', POINTER(point3f_t)), ('point_count', c_int), ] class detection_result(Structure): pass detection_result._fields_ = [ ('rect', rect_t), ('score', c_float), ('label', c_int), ('orientation', c_int), ('extra_info', extra_info), ]
多维数组
float mvp_mat[3][3] --> c_float33
数组指针
point_t *points_ori --> POINTER(point3f_t)
- 调用时指针(二级指针)的映射
CV_IMAGE_API mt_result_t mt_image_detect_compact(mt_handle_t handle, const unsigned char* img, int format, int image_width, int image_height, int image_stride, detection_result_t** detect_info, int* count);
python调用:
TARGETPOINTER_t = POINTER(detection_result) result_handle = TARGETPOINTER_t() print('result_handle: ' + str(result_handle)) count = c_int(0) status = mt_image_detect.detect_image(handle, byref(image_data), 0, width, height, width * 3, byref(result_handle), pointer(count)) print('detect_image status: ' + str(status) + " count : " + str(count.value)) detect_content = result_handle.contents
针对于二级指针,必须POINTER(detection_result)生成T*,然后创建result_handle = TARGETPOINTER_t(),然后通过byref(result_handle)得到二级指针
- byref(n)返回的相当于C的指针右值&n,本身没有被分配空间;
- pointer返回的相当于指针左值T* p=&n,可以改变,可以取地址; POINTER得到是类;
调用结果
/home/sensetime/miniconda3/envs/pythonPIL/bin/python /home/sensetime/jayzwang/workspace/clion_workspace/PyImageTest/image_test.py
copy ../CvImageTest/build/libmtimage.so->./extents/libs/libmtimage.so
copy ../CvImageTest/mt_image_common.h->./extents/include/mt_image_common.h
copy ../CvImageTest/mt_image_detect.h->./extents/include/mt_image_detect.h
test license
load_library lib is Exist : True
/home/sensetime/jayzwang/workspace/clion_workspace/PyImageTest
mt_image_detect_init_config.14: in
init_license : 0
mt_image_detect_create.24: in
create_handle : 0 handle : c_long(94128605088976)
pil image : 768 height : 576
width : 768 height : 576 format : None
image pointer : <cparam 'P' (0x559c061f1960)> image_date [-1] : 255
result_handle: <__main__.LP_detection_result object at 0x7fd92de1d1e0>
mt_image_detect_compact.62: in
mt_image_detect_compact.75: mt_image_detect_compact : 0x559c060ce080
detect_image status: 0 count : 1
detect result left : 20
detect result label: 1
detect result points: 1
mt_image_detect_reset.82: in
reset_handle status: 0
mt_image_release_detect_result.46: in
mt_image_detect_destroy.34: in
destroy_handle status: 0 handle : c_long(94128605088976)
其他:
- 文件移动
def movefile(srcpath, dstpath): if not os.path.isfile(srcpath): print(srcpath + ' is not exist!') else: fpath, fname = os.path.split(dstpath) if not os.path.exists(fpath): os.makedirs(fpath) shutil.copy(srcpath, dstpath) print('copy ' + srcpath + '->' + dstpath)
- 图片读取和转码,使用pil读取,并转换成BGR(AI/深度学习的大部分输入都是BGR)
hand_image = Image.open('./extents/test_image/timg.jpeg') hand_image = hand_image.convert('RGB') width, height = hand_image.size image_format = hand_image.format image_data = (c_ubyte * (width * height * 3))() print('pil image : ' + str(width) + " height : " + str(height)) # hand_image.show() for x in range(height): for y in range(width): r, g, b = hand_image.getpixel((y, x)) #bgr = b, g, r image_data[(x * width + y)*3] = b image_data[(x * width + y)*3 + 1] = g image_data[(x * width + y)*3 + 2] = r
- 写文件
out_file = open('image_in', 'wb') out_file.write(image_data) out_file.close()
结语:
ctypes是非常轻量级的python调用C/C++的框架,非常适用于第三库的测试,运行.能够快速实现自动化测试,压力测试等,十分实用;
转载
作者:NullBugs
链接:https://www.jianshu.com/p/3fc763dbd0aa