Python与C/C++调用之ctypes

标签(空格分隔): C/C++ python python调用C 人工智能 AI


  • 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的使用
    结构体头文件:
//
// Created by yinlib on 18-12-4.
//

#ifndef CVIMAGETEST_CV_COMMON_H
#define CVIMAGETEST_CV_COMMON_H


#ifdef __MSC_VER
#       define CV_IMAGE_API_ __declspec(dllexport)
#else
#       define CV_IMAGE_API_ __attribute__((visibility("default")))
#endif

#ifdef __cplusplus
#       define CV_IMAGE_API extern "C" CV_IMAGE_API_
#else
#       define CV_IMAGE_API CV_IMAGE_API_
#endif

#define RC_OK 0
#define RC_E_HANDLE -1
#define RC_E_INVALIDARG -2
#define RC_E_OUTOFMEMORY -3
#define RC_E_INVALID_FORMAT -4
#define RC_E_FAIL -5

typedef void *mt_handle_t;
typedef int mt_result_t;

typedef struct rect_t{
    int left;
    int top;
    int right;
    int bottom;
} rect_t;


typedef struct point3f_t{
    float x;
    float y;
    float z;
}point_t;


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;

#endif //CVIMAGETEST_CV_COMMON_H

接口头文件:

#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++的框架,非常适用于第三库的测试,运行.能够快速实现自动化测试,压力测试等,十分实用;

参考:https://docs.python.org/3/library/ctypes.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容