Python與C/C++調(diào)用之ctypes

標簽(空格分隔): C/C++ python python調(diào)用C 人工智能 AI


  • python訪問C/C++

    • python的底層大部分都是C/C++實現(xiàn),python和C和C++具有天然的互相調(diào)用優(yōu)勢;
    • 很多核心的算法庫都是C/C++寫的,在python開發(fā)過程中,經(jīng)常訪問別人的動態(tài)庫;
    • 知名人工智能(深度學習)框架訓練系統(tǒng)都是python寫的,而運行時一般都是以動態(tài)庫的形式提供;
  • python訪問C/C++的方式

    • ctypes乡洼;
    • pybind11;
    • cffi
    • swig
  • ctypes的優(yōu)勢

    • 不要修改動態(tài)庫的源碼;
    • 只需要動態(tài)庫和頭文件;
    • 使用比較簡單,而且目前大部分庫都是兼容C/C++;

本文以一個典型的深度學習(人工智能AI)的圖像檢測的python自動化測試,介紹ctypes的使用;

  • ctypes的使用
    結(jié)構(gòu)體頭文件:
//
// 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);

結(jié)構(gòu)體的映射:

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)

重點問題:

  • 結(jié)構(gòu)體和復雜結(jié)構(gòu)提的映射
    C中的結(jié)構(gòu)體
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),
]

多維數(shù)組
float mvp_mat[3][3] --> c_float33

數(shù)組指針
point_t *points_ori --> POINTER(point3f_t)

  • 調(diào)用時指針(二級指針)的映射
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調(diào)用:

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*,然后創(chuàng)建result_handle = TARGETPOINTER_t(),然后通過byref(result_handle)得到二級指針

  • byref(n)返回的相當于C的指針右值&n,本身沒有被分配空間;
  • pointer返回的相當于指針左值T* p=&n广匙,可以改變裤园,可以取地址; POINTER得到是類;

調(diào)用結(jié)果

/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)
  • 圖片讀取和轉(zhuǎn)碼,使用pil讀取,并轉(zhuǎn)換成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()

結(jié)語:
ctypes是非常輕量級的python調(diào)用C/C++的框架,非常適用于第三庫的測試,運行.能夠快速實現(xiàn)自動化測試,壓力測試等,十分實用;

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末翠胰,一起剝皮案震驚了整個濱河市睹酌,隨后出現(xiàn)的幾起案子侯养,更是在濱河造成了極大的恐慌畔况,老刑警劉巖鲸鹦,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異跷跪,居然都是意外死亡馋嗜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進店門吵瞻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來葛菇,“玉大人,你說我怎么就攤上這事橡羞∶型#” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵卿泽,是天一觀的道長莺债。 經(jīng)常有香客問我,道長又厉,這世上最難降的妖魔是什么九府? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮覆致,結(jié)果婚禮上侄旬,老公的妹妹穿的比我還像新娘。我一直安慰自己煌妈,他們只是感情好儡羔,可當我...
    茶點故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布宣羊。 她就那樣靜靜地躺著,像睡著了一般汰蜘。 火紅的嫁衣襯著肌膚如雪仇冯。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天族操,我揣著相機與錄音苛坚,去河邊找鬼。 笑死色难,一個胖子當著我的面吹牛泼舱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播枷莉,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼娇昙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了笤妙?” 一聲冷哼從身側(cè)響起冒掌,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蹲盘,沒想到半個月后股毫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡辜限,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年皇拣,在試婚紗的時候發(fā)現(xiàn)自己被綠了严蓖。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片薄嫡。...
    茶點故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖颗胡,靈堂內(nèi)的尸體忽然破棺而出毫深,到底是詐尸還是另有隱情,我是刑警寧澤毒姨,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布哑蔫,位于F島的核電站,受9級特大地震影響弧呐,放射性物質(zhì)發(fā)生泄漏闸迷。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一俘枫、第九天 我趴在偏房一處隱蔽的房頂上張望腥沽。 院中可真熱鬧,春花似錦鸠蚪、人聲如沸今阳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盾舌。三九已至墓臭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間妖谴,已是汗流浹背窿锉。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留膝舅,地道東北人榆综。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像铸史,于是被迫代替她去往敵國和親鼻疮。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,577評論 2 353

推薦閱讀更多精彩內(nèi)容