python类的成员函数_C++使用Py*调用Python3模块中类成员函数及数组参数传递

1.首先来看Python模块的部分结构和代码。ssd_network_classify.py文件中有SSD_Network_Classify类及其识别的成员函数detect_image(),返回值是一个1维的不定长double型数组。

class SSD_Network_Classify:

#其他函数实现省略。。。

def detect_image(self, img_raw=None):

'''

获取图片数据进行检测。

:param img_raw: 从c++传来的一维480*640*3大小的int型图片数据(包含负值)

:return: 返回的是一维数组

'''

#将原始图片数据shape成480*640*3的uint8类型数据。

img_data = np.reshape(img_raw, (480, 640, 3)).astype(np.uint8)

#以下两行输出用于显示传参的值帮助理解。

print(img_data.shape)

print(img_data)

#进行目标检测并返回检测结果。

rclasses, rscores, rbboxes = self.detect(img_data)

#显示检测结果

cv2.imshow('DetectImage', img_data)

cv2.waitKey(1)

#以下将返回的结果拼接成一维的数组返回给调用该函数的c程序中。

rclasses = np.reshape(rclasses, (-1, 1))

rscores = np.reshape(rscores, (-1, 1))

result = np.concatenate([rclasses, rscores, rbboxes], axis=1)

result = np.squeeze(np.reshape(result, (1, -1)))

#print(result)

return result

在Python中输出传入的参数(从c++传送过来)示例如下:

c9f5f4ce3e7a?utm_campaign=maleskine

data.png

2. C++端获取摄像头数据的类CameraBase:

//以下是.h文件中的成员变量

//cv::VideoCapture *capture;

//cv::Mat bgr_image;

//int device_num;

//以下是.cpp中主要成员函数的实现

"构造函数,打开摄像头"

CameraBase::CameraBase(int dev_num) {

this->device_num = dev_num;

this->capture = new cv::VideoCapture(this->device_num);

if (!capture->isOpened()) {

std::cout << "camera open failed\n";

}

}

"读取网络摄像头的图片数据"

void CameraBase::grabImages() {

this->capture->read(this->bgr_image);

}

"图片的Mat数据转换成char类型的数组数据"

char *CameraBase::bgrImageMatToArray( char *img_arr) {

size_t img_size = this->bgr_image.total() * this->bgr_image.elemSize() ;

std::memcpy(img_arr, this->bgr_image.data, img_size * sizeof(char));

return img_arr;

}

3.核心部分。main.cpp中c++调用Python的函数callPythonForDetect。这块代码是c++调用Python的核心代码,具体代码解释已经在代码注释中写得很清楚了。

int callPythonForDetect() {

//调用web摄像头进行处理

CameraBase *camera = new CameraBase();

//------------------以下是调用Python模块的代码------------------//

//python环境初始化

Py_Initialize();

if (!Py_IsInitialized())

return -1;

//导入系统包用于扩展需要加载的Python模块的路径,否则即使Python模块在当前目录也无法加载

PyRun_SimpleString("import sys \nsys.argv = ['']");

//加载Python模块的路径

PyRun_SimpleString("sys.path.append('/absolute/path/to/python/module')");

//导入需要调用的模块

PyObject *pyModule = PyImport_ImportModule("ssd_network_classify");

if (!pyModule) {

printf("Can not open python module\n");

return -1;

}

//获取python模块中的类名并创建对象实例

PyObject *pyClass = PyObject_GetAttrString(pyModule, "SSD_Network_Classify");

PyObject *pyClassInstance = PyObject_CallObject(pyClass, NULL);

//获取Python模块中相应的函数名

PyObject *pyFunc = PyObject_GetAttrString(pyClass, "detect_image");

//声明或定义变量

npy_intp IMGSHAPE[1] = {480 * 640 * 3};//图片数据的shape参数值

char *img_data = new char[IMGSHAPE[0]];//从摄像头中获取的图片数据保存的变量

PyByteArrayObject *pyIMgArr;//image数组的Python对象

//设置发送给Python函数的参数对象

PyObject *pyArgs = PyTuple_New(2);

while (true) {

camera->grabImages();//获取摄像头数据

//获取image数据并保存至img_data数组中

camera->bgrImageMatToArray(img_data);

//必须添加如下函数,否则无法执行PyArray_SimpleNewFromData

import_array ();

//将c的img数组数据转换成pyobject类型的数组数据

pyIMgArr = reinterpret_cast

(PyArray_SimpleNewFromData(1, IMGSHAPE, NPY_BYTE, reinterpret_cast(img_data)));

//设置调用函数的self值为前面该类创建的实例,否则无法使用self变量进行调用而出错

PyTuple_SetItem(pyArgs, 0, Py_BuildValue("O", pyClassInstance));

//设置变量的第二个参数值为byte类型的数组作为图片数据

PyTuple_SetItem(pyArgs, 1, reinterpret_cast(pyIMgArr));

//调用python函数进行识别任务并返回相应的结果

PyObject *pyResult = PyObject_CallObject(pyFunc, pyArgs);

//以下是对返回的一维数组结果进行处理

if (pyResult) {

//将结果类型转换成数组对象类型

PyArrayObject *pyResultArr = (PyArrayObject *) pyResult;

//也可以使用以下两行代码来代替上面的类型转换。

//PyArray_Descr *descr = PyArray_DescrFromType(NPY_DOUBLE);

//PyArrayObject *pyResultArr = (PyArrayObject*)PyArray_FromAny(pyResult, descr,1,1,NPY_ARRAY_C_CONTIGUOUS,NULL);

//从Python中的PyArrayObject解析出数组数据为c的double类型。

double *resDataArr = (double *) PyArray_DATA(pyResultArr);

int dimNum = PyArray_NDIM(pyResultArr);//返回数组的维度数,此处恒为1

npy_intp *pdim = PyArray_DIMS(pyResultArr);//返回数组各维度上的元素个数值

//以下是对返回结果的输出显示

for (int i = 0; i < dimNum; ++i) {

for (int j = 0; j < pdim[0]; ++j)

cout << resDataArr[i * pdim[0] + j] << ",";

}

cout << endl;

}

}

//释放Python环境

Py_Finalize();

}

从调用的Python函数中返回的数组结果示例如下:

c9f5f4ce3e7a?utm_campaign=maleskine

dataout.png

以下是最终C++通过调用Python版本实现的目标识别网络展示的结果:

c9f5f4ce3e7a?utm_campaign=maleskine

result.png

总结:

*** 使用C++调用python3模块接口的示例基本没有,有的大部分都是python2版本的示例,而新版本的很多函数名称和用法改变都很大,导致我在写这块代码的时候碰到很多问题,就这个简单需求花了我整整3天的时间,期间有考虑使用第三方框架进行解决,但是发现也很麻烦。其中需要注意的是,因为Python的类函数的第一个参数是self,并且是传入对象本身,因此在c中调用的时候也要考虑为其赋值(PyTuple_SetItem(pyArgs, 0, Py_BuildValue("O", pyClassInstance)); 这行代码很重要),否则会出错。很多例子都考虑的是调用非类成员函数,因此不需要考虑self变量而比较容易。我是自己尝试很久并经过调试才知道该怎么给self赋值的,这也是我写这篇文章的原因所在,希望给其他需要的人一些参考,少走弯路。***

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值