Guide
ctypes intro
python only support c api.
ctype usage
# (1) get and set value for c_int
from ctypes import *
i = c_int(45)
i.value
45
i.value = 55
i.value
55
# (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>
p.raw
'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
p.value = "Student"
p.raw
'Student\x00\x00\x00'
p.value = "Big" # set 3 chars "Big" and 4-th char "\0"
p.raw
'Big\x00ent\x00\x00\x00'
# (3) get and set value by pointer
i = c_int(999)
pi = pointer(i) # pointer to c_int
pi.contents
c_int(999)
pi.contents = c_int(1000)
pi.contents
c_int(1000)
# (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)
# (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'>
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
# (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'>
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
# (6) pointer
# byref(x [, offset]) ---> get address of x `&num`
# pointer(x) ---> create a pointer which points to x `int* p = #`
# 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
#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
#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
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
#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
#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
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.