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) 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 = &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

#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.

Author: kezunlin
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint polocy. If reproduced, please indicate source kezunlin !
评论
  TOC