python调用C++函数

其实。这个东西一年前就想记录下来了。就。现在才写。

之前做某个项目的时候有个处理步骤用SSD检测效果比较好。本来想为了统一学习框架,用tensorflow版本的SSD来做。但是因为环境问题,无法一起使用。正好以前其他项目有用过C++写的caffe版本SSD,可以拿来用。

那个时候用的还是Python写的项目,所以要想办法把C++的代码用到Python里面去。

python调用C++的代码可以把C++代码封装成python模块,也可以编成动态库用FFI方式调用。因为只是研究性质的东西,所以怎么快怎么来了。

一开始用了CFFI第三方模块,功能是能实现了,但是引入了第三方依赖又硬贴入一大坨C代码巨丑,后来用ctypes重新实现了。(其实原来一直以为CFFI就是python官方的FFI调用方式,忘了还有个叫ctypes的

先把C++封装成C接口,这个就很简单了,一共就暴露出

  • ssd_create
  • ssd_caffe_det
  • ssd_destroy

3个接口,调用流程也就创建context,开始检测,销毁context。

使用ctypes.CDLL参数为动态库文件(so,dll,dylib)可以dlopen方式加载需要调用的动态库。

可以直接从打开的库对象调用导出的函数,名字也和c里面定义的一样。

因为调用的是c的函数接口,所以交互不能直接使用python的数据结构,而是用ctypes里面定义的c数据类型。具体要查看相关文档了。

c中的数组/指针在python中可以直接用[]获取对应位置的元素,结构体中的元素也可以直接用.获取,使用起来不算麻烦。

下面就贴上完整的python代码吧。

# -*- coding: utf-8 -*-

import cv2
import ctypes

class SSD(object):

    """FFI调用caffe版SSD封装类
    使用:
        ssd = SSD() # 参数可加threshold,
                    # 阈值,置信度大于阈值的结果才返回
        res = ssd.ssd_caffe(img) # img,要识别的图片
        # res 为结果list ,每个元素为一个结果
        # [image,[x,y,w,h],score] image为截取出来的图像
    """
    class RetVal(ctypes.Structure):
        _fields_ = ("size", ctypes.c_ubyte), ("values", ctypes.c_float*250)

    def __init__(self, threshold = 0.5, model = "deploy512_small.prototxt", weights = "models/car5_SSD_512x512_iter_5000.caffemodel"):
        # 阈值,置信度大于阈值的结果才返回
        self.th = threshold
        # 加载so库
        self.so = ctypes.CDLL("./libssd_caffe.so")
        # 创建SSD的handle

        mod = model.encode("u8")
        wei = weights.encode("u8")

        create_handle = self.so.ssd_create
        create_handle.restype = ctypes.c_void_p
        self.handle = ctypes.c_void_p(create_handle(mod, wei))

    def set_threshold(self, threshold):
        self.th = threshold

    def ssd_caffe(self, image):
        # 加载图片
        img = cv2.resize(image,(512,512))
        p = img.ctypes.data_as(ctypes.POINTER(ctypes.c_ubyte))

        # 申请存储返回值结构体
        res = SSD.RetVal()
        pres = ctypes.byref(res)

        # 开始检测
        self.so.ssd_caffe_det(self.handle, p, pres)

        result = []

        for i in range(res.size):
            score = res.values[i*5]
            if(score < self.th):
                continue
            xmin = int(res.values[i*5+1] * image.shape[1])
            ymin = int(res.values[i*5+2] * image.shape[0])
            if xmin < 0:
                xmin = 0
            if ymin < 0:
                ymin = 0
            xmax = int(res.values[i*5+3] * image.shape[1])
            ymax = int(res.values[i*5+4] * image.shape[0])
            w = xmax - xmin
            h = ymax - ymin
            result.append((image[ymin:ymax, xmin:xmax],[xmin,ymin,w,h],score))

        return result


    def __del__(self):
        # 释放handle
        self.so.ssd_destroy(self.handle)

@

Show Comments