0%

use python ctypes to load and use functions from C C++ DLL

Guide

ctypes intro

python only support c api.

ctypes types, C type, Python type

ctype usage

1
2
3
4
# (1) get and set value for c_int
from ctypes import *
i = c_int(45)
i.value
45
1
2
i.value = 55
i.value
55
1
2
3
# (2) set and get value for c_char_Array_10  (c array)
p = create_string_buffer(10)
p
<ctypes.c_char_Array_10 at 0x7fe008061170>
1
p.raw
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
1
2
p.value = "Student"
p.raw
'Student\x00\x00\x00'
1
2
p.value = "Big" # set 3 chars "Big" and 4-th char "\0"
p.raw
'Big\x00ent\x00\x00\x00'
1
2
3
4
# (3) get and set value by pointer 
i = c_int(999)
pi = pointer(i) # pointer to c_int
pi.contents
c_int(999)
1
2
pi.contents = c_int(1000)
pi.contents
c_int(1000)
1
2
3
4
5
6
7
8
# (4) use struct
class Point(Structure):
_fields_ = [("x", c_int), ("y", c_int)]
pt = Point(2,5)
print(pt.x, pt.y)

pt2 = Point(y=5) # x default value = 0
print(pt2.x, pt2.y)
(2, 5)
(0, 5)
1
2
3
4
# (5.1) use c array  c_int_Array_10
INT_ARRAY_10 = c_int *10
print(c_int)
print(INT_ARRAY_10)
<class 'ctypes.c_int'>
<class '__main__.c_int_Array_10'>
1
2
3
4
5
6
7
i_array = INT_ARRAY_10() # by default all 0

for i in range(10):
i_array[i] = i

for value in i_array:
print(value)
0
1
2
3
4
5
6
7
8
9
1
2
3
4
# (5.2) use c array  c_char_Array_10
CHAR_ARRAY_10 = c_char * 10
print(c_char)
print(CHAR_ARRAY_10)
<class 'ctypes.c_char'>
<class 'ctypes.c_char_Array_10'>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
c_array = CHAR_ARRAY_10() # by default all 0

for i in range(10):
c_array[i] = "a"

for value in c_array:
print(value)

# value and raw field for c_char_Array_10
print(c_array.value)
print(c_array.raw)

c_array.value = "Student"
print(c_array.value)
print(c_array.raw)
a
a
a
a
a
a
a
a
a
a
aaaaaaaaaa
aaaaaaaaaa
Student
Student\00aa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# (6) pointer
# byref(x [, offset]) ---> get address of x `&num`
# pointer(x) ---> create a pointer which points to x `int* p = &num;`
# POINTER(type) ---> return a `new type` which points to type instance `Point*`
a = Point(2,5)
b = pointer(a)
print(a)
print(a.x, a.y)

print("\n")
print(b)
print(b.contents) # a
print(b.contents.x, b.contents.y)

c = POINTER(Point)(a)
print("\n")
print(c)
print(c.contents)
print(c.contents.x, c.contents.y)
<__main__.Point object at 0x7fe00805d3b0>
(2, 5)


<__main__.LP_Point object at 0x7fe00805d710>
<__main__.Point object at 0x7fe00805d5f0>
(2, 5)


<__main__.LP_Point object at 0x7fe00805d5f0>
<__main__.Point object at 0x7fe00805d440>
(2, 5)

Example

c++/c code

api.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#pragma once
#include "algorithm_shared_export.h"

#include <iostream>

#include <opencv2/opencv.hpp> // Mat

struct SHARED_EXPORT Point
{
float x, y;
};

typedef struct {
int H;
int W;
int C;
float *data;
} image_float_t;

typedef struct {
int H;
int W;
int C;
unsigned char *data;
} image_char_t;

class SHARED_EXPORT MyClass
{
public:
MyClass(int id);
int add(int a, int b);
float addf(float a, float b);
void print_point(Point* p);

private:
int id_;
};


#ifdef __cplusplus
extern "C" {
#endif

// pure C API (can not overload)
SHARED_EXPORT int add(int a, int b);
SHARED_EXPORT float addf(float a, float b);
SHARED_EXPORT void print_point(Point* p);

// warpper for c++ class
SHARED_EXPORT void wrapper_init_class(int id);
SHARED_EXPORT void wrapper_free_class();

SHARED_EXPORT int wrapper_add(int a, int b);
SHARED_EXPORT float wrapper_addf(float a, float b);
SHARED_EXPORT void wrapper_print_point(Point* p);

SHARED_EXPORT void process_image_float(image_float_t im);
SHARED_EXPORT void process_image_char(image_char_t im);

#ifdef __cplusplus
}
#endif

api.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#include "api.h"

// c++ class impl

MyClass::MyClass(int id)
:id_(id)
{

}

int MyClass::add(int a, int b)
{
return a + b;
}

float MyClass::addf(float a, float b)
{
return a + b;
}

void MyClass::print_point(Point* p)
{
if (p)
printf("position x %f y %f \n", p->x, p->y);
printf("myclass id = %d \n", id_);
}


#ifdef __cplusplus
extern "C" {
#endif

// pure C API impl
int add(int a, int b)
{
return a + b;
}

float addf(float a, float b)
{
return a + b;
}

void print_point(Point* p)
{
if (p)
printf("position x %f y %f \n", p->x, p->y);
}


// warpper for c++ class
void wrapper_init_class(int id)
{
my = new MyClass(id);
}

void wrapper_free_class()
{
if (my)
{
delete my;
}
}

int wrapper_add(int a, int b)
{
return my->add(a,b);
}

float wrapper_addf(float a, float b)
{
return my->addf(a,b);
}

void wrapper_print_point(Point* p)
{
return my->print_point(p);
}

//(1) bgr, hwc, 0-255 h*W*C+w*C+c int index = h*im.W*im.C + (w*im.C + c);
//(2) bgr, chw, 0-255 c*H*W+h*W+w int index = c*im.H*im.W + (h*im.W + w);

float get_image_pixel_hwc(const image_float_t& im, int h, int w, int c)
{
//(1) bgr, hwc, 0-255 h*W*C+w*C+c int index = h*im.W*im.C + (w*im.C + c);
int index = h*im.W*im.C + (w*im.C + c);
return im.data[index];
}

float get_image_pixel_chw(const image_float_t& im, int h, int w, int c)
{
//(2) bgr, chw, 0-255 c*H*W+h*W+w int index = c*im.H*im.W + (h*im.W + w);
int index = c*im.H*im.W + (h*im.W + w);
return im.data[index];
}

cv::Mat image_float_to_mat(const image_float_t& im)
{
// bgr,hwc, 0-255
cv::Mat mat(im.H, im.W, CV_8UC3);
for(int h=0; h< im.H; ++h)
{
cv::Vec3b *p = mat.ptr<cv::Vec3b>(h);
for(int w=0; w < im.W; ++w)
{
for(int c=0; c< im.C; ++c)
{
// b,g,r
(*p)[c] = (unsigned char)get_image_pixel_hwc(im, h, w, c);
}
p++;
}
}
return mat;
}

// same as image_float_to_mat, but faster
cv::Mat image_float_to_mat2(const image_float_t& im)
{
// bgr,hwc, 0-255
cv::Mat mat(im.H, im.W, CV_8UC3);
const float* p_data = im.data;

for(int h=0; h< im.H; ++h)
{
cv::Vec3b *p = mat.ptr<cv::Vec3b>(h);
for(int w=0; w < im.W; ++w)
{
for(int c=0; c< im.C; ++c)
{
// b,g,r
(*p)[c] = (unsigned char)(*p_data++);
}
p++; // for width
}
}
return mat;
}


cv::Mat image_char_to_mat(const image_char_t& im)
{
// bgr,hwc, 0-255
cv::Mat mat(im.H, im.W, CV_8UC3, im.data);
return mat; // data in python
}

void process_image_float(image_float_t im)
{
cv::Mat mat = image_float_to_mat2(im);

cv::imwrite("result2.jpg", mat);
std::cout<<" saved image to result.jpg"<< std::endl;
}

void process_image_char(image_char_t im)
{
cv::Mat mat = image_char_to_mat(im);

cv::imwrite("result3.jpg", mat);
std::cout<<" saved image to result.jpg"<< std::endl;
}

#ifdef __cplusplus
}
#endif

python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
from ctypes import *

import cv2
import numpy as np


#==================================================
# os.name "posix" for linux, "nt" for windows
lib = CDLL("./libcapi.so", RTLD_GLOBAL)
#==================================================

"""
// struct
struct Point{
float x, y;
};

// pure C API
SHARED_EXPORT int add(int a, int b);
SHARED_EXPORT float addf(float a, float b);
SHARED_EXPORT void print_point(Point* p);

// warpper for c++ class
SHARED_EXPORT void wrapper_init_class(int id);
SHARED_EXPORT void wrapper_free_class();

SHARED_EXPORT int wrapper_add(int a, int b);
SHARED_EXPORT float wrapper_addf(float a, float b);
SHARED_EXPORT void wrapper_print_point(Point* p);
"""

# struct
class POINT(Structure):
_fields_ = [("x", c_float),
("y", c_float)]


# pure C API
lib.add.argtypes = [c_int, c_int]
lib.add.restype = c_int

lib.addf.argtypes = [c_float, c_float]
lib.addf.restype = c_float

lib.print_point.argtypes = [POINTER(POINT)]
lib.print_point.restype = None

# warpper for c++ class
lib.wrapper_init_class.argtypes = [c_int]
lib.wrapper_init_class.restype = None

lib.wrapper_free_class.argtypes = None
lib.wrapper_free_class.restype = None

lib.wrapper_add.argtypes = [c_int, c_int]
lib.wrapper_add.restype = c_int

lib.wrapper_addf.argtypes = [c_float, c_float]
lib.wrapper_addf.restype = c_float

lib.wrapper_print_point.argtypes = [POINTER(POINT)]
lib.wrapper_print_point.restype = None


def test_capi():
print("test_capi")
print(lib.add(3,5)) # 8
print(lib.addf(3.3,5.5)) # 8.8

p = POINT(9.1,9.2)
print(p) # POINT
print(p.x, p.y) # 9.1,9.2
lib.print_point(byref(p)) # faster
lib.print_point(pointer(p))

def test_wrapper_capi():
print("test_wrapper_capi")

# init class wrapper
lib.wrapper_init_class(100)

print(lib.wrapper_add(3,5)) # 8
print(lib.wrapper_addf(3.3,5.5)) # 8.8

p = POINT(9.1,9.2)
print(p) # POINT
print(p.x, p.y) # 9.1,9.2
lib.wrapper_print_point(byref(p)) # faster
lib.wrapper_print_point(pointer(p))

# free class wrapper
lib.wrapper_free_class()


#======================================

# unsigned char* ===> c_ubyte ===> np.uint8
# float* ===> c_float ===> np.float32
class IMAGE_FLOAT(Structure):
_fields_ = [("h", c_int),
("w", c_int),
("c", c_int),
("data", POINTER(c_float))]

class IMAGE_CHAR(Structure):
_fields_ = [("h", c_int),
("w", c_int),
("c", c_int),
("data", POINTER(c_ubyte))]

lib.process_image_float.argtypes = [IMAGE_FLOAT]
lib.process_image_float.restype = None

lib.process_image_char.argtypes = [IMAGE_CHAR]
lib.process_image_char.restype = None


def mat_to_darknet_image_float(bgr):
# BGR,hwc,[0,255] === > RGB,chw,[0,1]
rgb = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGB)
rgb = rgb.transpose(2, 0, 1) # hwc ===> chw
c = rgb.shape[0]
h = rgb.shape[1]
w = rgb.shape[2]
arr = np.ascontiguousarray(rgb.flat, dtype=np.float32) / 255.0 # [0-1]

data = arr.ctypes.data_as(POINTER(c_float))
im = IMAGE_FLOAT(w, h, c, data)
return im, arr # return `arr` to avoid python freeing memory

def mat_to_image_float(bgr):
# BGR,hwc,[0,255]
print(bgr.shape) # height, width ,channel

#bgr = bgr.transpose(2, 0, 1) # hwc ===> chw
#c = bgr.shape[0]
#h = bgr.shape[1]
#w = bgr.shape[2]

(h,w,c) = bgr.shape

#factor = 1.0
arr = np.ascontiguousarray(bgr.flat, dtype=np.float32)

data = arr.ctypes.data_as(POINTER(c_float))
im = IMAGE_FLOAT(h, w, c, data) # bgr,hwc,0-255
return im, arr # return `arr` to avoid python freeing memory

def mat_to_image_char(bgr):
# BGR,hwc,[0,255]
print(bgr.shape) # height, width ,channel

(h,w,c) = bgr.shape
arr = np.ascontiguousarray(bgr.flat, dtype=np.uint8)

data = arr.ctypes.data_as(POINTER(c_ubyte))
im = IMAGE_CHAR(h, w, c, data) # bgr,hwc,0-255
return im, arr # return `arr` to avoid python freeing memory

def test_process_image_float():
image_filepath = "dog.jpg"
bgr = cv2.imread(image_filepath)
im, arr = mat_to_image_float(bgr)
# image memory allocated in Python with `arr`, `im` only point to `arr`
# so there is no need to free_image(im), because python will free `arr` automatically
lib.process_image_float(im)

def test_process_image_char():
image_filepath = "dog.jpg"
bgr = cv2.imread(image_filepath)
im, arr = mat_to_image_char(bgr)
# image memory allocated in Python with `arr`, `im` only point to `arr`
# so there is no need to free_image(im), because python will free `arr` automatically
lib.process_image_char(im)


def main():
#test_capi()
#test_wrapper_capi()
test_process_image_float()
test_process_image_char()

if __name__ == "__main__":
main()

Python OpenCV Mat to CAPI

C

api.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#pragma once

typedef struct {
int H;
int W;
int C;
unsigned char *data;
} image_char_t;


#ifdef __cplusplus
extern "C" {
#endif

SHARED_EXPORT void process_image_char(image_char_t im);

#ifdef __cplusplus
}
#endif

api.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "api.h"

#include <opencv2/opencv.hpp> // Mat
#include <opencv2/imgproc.hpp> // cvtColor
#include <opencv2/highgui.hpp> // imdecode imshow


#ifdef __cplusplus
extern "C" {
#endif

void process_image_char(image_char_t im)
{
cv::Mat mat(im.H, im.W, CV_8UC3, im.data);

cv::imwrite("result.jpg", mat);
std::cout<<" saved image to result.jpg"<< std::endl;
}

#ifdef __cplusplus
}
#endif

python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
from ctypes import *

import cv2
import numpy as np


#==================================================
# os.name "posix" for linux, "nt" for windows
lib = CDLL("./libcapi.so", RTLD_GLOBAL)
#==================================================


# unsigned char* ===> c_ubyte ===> np.uint8
class IMAGE_CHAR(Structure):
_fields_ = [("h", c_int),
("w", c_int),
("c", c_int),
("data", POINTER(c_ubyte))]

lib.process_image_char.argtypes = [IMAGE_CHAR]
lib.process_image_char.restype = None


def mat_to_image_char(bgr):
# BGR,hwc,[0,255]
print(bgr.shape) # height, width ,channel

(h,w,c) = bgr.shape
arr = np.ascontiguousarray(bgr.flat, dtype=np.uint8)

data = arr.ctypes.data_as(POINTER(c_ubyte))
im = IMAGE_CHAR(h, w, c, data) # bgr,hwc,0-255
return im, arr # return `arr` to avoid python freeing memory

def test_process_image_char():
image_filepath = "dog.jpg"
bgr = cv2.imread(image_filepath)
im, arr = mat_to_image_char(bgr)
# image memory allocated in Python with `arr`, `im` only point to `arr`
# so there is no need to free_image(im), because python will free `arr` automatically
lib.process_image_char(im)


def main():
test_process_image_char()

if __name__ == "__main__":
main()

Boost-python extension

Reference

History

  • 20190308: created.