这里主要通过python调用c++深度学习模型,传入图片,c++处理,并返回结果,python接收结果,这个过程的接口如何设计转换。
python端代码接口
# 定义dll返回的结果类型,这里应该和c++中定义的返回结构体一一对应
class Result_process(Structure):
_fields_ = [
('count', c_int),
('ID', c_int),
('score',c_float),
('x', c_float),
('y', c_float),
('width',c_float),
('height',c_float),
]
# 这里初始化模型参数,需要向c++的dll传入字符串即模型的路径,字符串必须转换为字节类型的才能传入
class model:
def __init__(self, engineFile_path, onnx_path):
self.engineFile_path = engineFile_path
self.onnx_path = onnx_path
def init(self):
# 初始化,加载模型,可以不用加b
dll= ctypes.cdll.LoadLibrary('.//src//model_c++.dll')
# 字符串需要加b,针对可变字符串必须使用ctypes.create_string_buffer进行转换,否则报错
#path = b"D://VScode_prj//tkjc//src//model.onnx"
chr1=ctypes.create_string_buffer(self.onnx_path)
#chr1=ctypes.create_string_buffer(path)
# 把转换后的字符串进行初始化,这里的初始化就是加载模型,传入模型的字符串
ret = dll.Init(chr1)
if ret == -1:
print("-----模型初始化失败--------")
return ret
# 这里把dll的推理函数拿出
self.process = dll.process
# 声明输入的数据类型
self.process.argtypes=[c_char_p,c_int,c_int,c_int]
# 声明返回结果的类型
self.process.restype = POINTER(Result_process * 1000)
def infer(self, src):
if src is None:
print("----图片为空----")
return -1
cols = src.shape[1]
rows = src.shape[0]
channels = 0
if 3==len(src.shape):
channels = 3
# 这里是把图片数据转换为dll可识别的类型
src = np.asarray(src, dtype=np.uint8)
# 这里就获取图片数据的指针
src1 = src.ctypes.data_as(c_char_p)
# 传入图片数据,因为只有指针不行,因此需要图片的宽高和通道数,dll才能准换为c++可处理的数据,c++拿到图片后处理并返回结果
struct_res = self.process(src1, rows, cols, channels)
data = dict()
#print("------开始获取数据--------")
for i in range(struct_res.contents[0].count):
ID=struct_res.contents[i].ID
score=struct_res.contents[i].score
x=struct_res.contents[i].x
y=struct_res.contents[i].y
width=struct_res.contents[i].width
height=struct_res.contents[i].height
#print("ID:{}, score:{}, x:{}, y:{}, width:{}, height:{}".format(ID, score, x, y, width, height))
data[ID] = [score, x, y, width, height]
return data
c++端接口设计
接口设计:
//这里是返回结果定义的结构体,和python端口定义的是一一对应的
typedef struct
{
int count = 0; //当前帧的目标个数
int ID = 0;//当前目标的ID
float score = 0;//当前目标的评分
//cv::Rect_<float> box_tlbr;//目标的左上角和右下角坐标,这里考虑使用python调用,使用简单数据类型
float x = 0.0;
float y = 0.0;
float width = 0.0;
float height = 0.0;
}SingleObjectResult;
/
//整体需要转换为c类型,因为python只支持c类型的代码和参数
extern "C" {
//c++的版本的深度学习模型应该是一个类,如果不知道深度学习如何使用类操作,建议看看yolov5的c++版本实现
//定义一个模型类的变量
Model tracks;
//这里是初始化接口函数,python调用的初始化函数就是来源这里
extern "C" _declspec(dllexport) int Init(char* onnx_path)
{
printf("-----开始初始化-----");
string str1;
str1 = onnx_path;
std::cout << str1.c_str() << endl;
return tracks.Init(str1.c_str());
}
//这里推理函数,python调用的推理函数也是来源这里
//传入的是uchar类型的指针,该指针就是指向python传入的图片数据
extern "C" _declspec(dllexport) SingleObjectResult* process(uchar * frame_data, int height, int width, int channels)
{
//printf("-----开始检测-----");
/*string str2;
str2 = video_path;
std::cout << str2.c_str() << endl;*/
return tracks.processs(frame_data, height, width, channels);
}
}
一些关键函数:
//解析从python传送过来图片数据,先把内存的图片数据转换为opencv类型的数据,共后面的处理
//这里处理方法很多,但是建议使用opencv提供的构造函数直接转换,这样效率高
Mat Model::readfrombuffer(uchar* frame_data, int height, int width, int channels)
{
//Mat img(height, width, CV_8UC3);
Mat img = Mat(height, width, CV_8UC3, frame_data);
return img;
}
c++主要函数实现
//初始化类函数,主要初始化模型的加载
int Model::Init(const std::string& onnx_path) {
//这里替换你自己的初始化程序即可
detconfig.model_file = onnx_path;//true
det = DetectorFactory::create_object(detconfig);
if (!det->init())
{
std::cout << "\n模型初始化失败,请检查传入模型的路径: " << onnx_path << endl;
return -1;
}
config.conf_thres = 0.4f;
config.K = 500;
config.track_buffer = 30;
std::cout << "模型加载成功,初始化完成" << std::endl;
return 1;
}
//推理函数
SingleObjectResult* Model::processs(uchar* frame_data, int height, int width, int channels)
{
//定义一个返回结构体,这里为了避免系统自动释放内存,因此申请内存
SingleObjectResult* Result = new SingleObjectResult[1000];
Mat frame = readfrombuffer(frame_data, height, width, channels);
if (frame.empty())
{
std::cout << "-----该帧出错,视频有问题-----" << std::endl;
return Result;
}
det->get_detection(frame, vec_db, vec_features);
std::vector<std::shared_ptr<STrack>> tracks = jde->update(vec_db, vec_features);
//提取跟踪信息
int count = tracks.size();
int index = 0;
for (auto& tr : tracks)
{
//检测框
//cv::Rect_<float> loc = tr->to_tlwh_rect();
DETECTBOX ret = tr->to_tlwh_box();
//检测ID
int ID = tr->track_id;
//检测得分
float score = tr->score;
//当前帧号
int frame_id = tr->frame_id;
Result[index].count = count;
Result[index].ID = ID;
Result[index].score = score;
Result[index].x = ret[0];
Result[index].y = ret[1];
Result[index].width = ret[2];
Result[index].height = ret[3];
index += 1;
}
return Result;
}