其实。这个东西一年前就想记录下来了。就。现在才写。
之前做某个项目的时候有个处理步骤用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)
@