c++调用python numpy编程

背景

现在用c++重构了python工程,有一部分后处理不想再花时间重构了,所以直接拿过来调用。边搜资料边做的,做这个demo花了些时间,所以记下来以防忘记。

资料

找了很多的c++调用python的方法,首先可以肯定的有不止一种方式,直接使用python库、numpy arrayobject库来做;另外一种是使用boost/python boost/numpy的方式。后一种没有调通,是链接库的问题,也记录下来放在后面了。

1)常规的方式
  1. 调包的基本结构 C/C++调用Python(OpenCV与Numpy)作者收集了很多的信息,帮助蛮大的。
  2. 编译,收集自stackoverflow: how to build this project
    g++ -o cc cc.cpp `pkg-config --cflags --libs opencv` -I/usr/include/python3.5 -lpython3.5m
    值得说明一下的是,python3.5m是在路径/usr/lib/x86_64-linux-gnu/libpython3.5m.so下,可以自行搜素。另外,代码中有个warning,使用宏定义就可以去掉了,在代码中也有说明
  3. 代码块
  1. 补充
  • import_array()有奇怪的返回值,这篇博客也提到了解决方法 import_array()报错,返回值类型与函数类型不匹配。但是要删东西,还有不报错误的风险。这儿我忘记了在哪看到的代码了,也没能在历史记录里找到,先把代码贴过来。其实就是将这个函数加一个壳。然后用init替换import_array。
size_t init() {
    import_array();
}
  • 一个小插曲,c++主函数需要使用多线程,python放在子线程中执行。在子线程中异步调用时会有segment fault,而相同代码顺序执行的没有问题。我一度怀疑是多线程的锅,所以尝试了小半天的c++多线程调用python,顺便把筛选的资料附录在下面;也尝试了子线程加锁mutex。哦,忘记说了,多线程调用python时,也是给python解释器加锁。那下面就是C++多线程调用python的链接,按优先顺序排放。
    c++多线程调用python
    C++调用PythonAPI线程状态和全局解释器锁
    C++ 多线程调用Python脚本

  • c++调用python类
    通过c++去调用python类的资料我收集了一部分。比如1).c++访问python3-实例化类的方法、2).c++ 调用 python 实例 涉及 类 多参数 列表作为参数、3).c++调用python的代码、函数、类。这些在传递numpy的时候没有找到合适的示例,这儿没有说明文档始终调不通。所以最后还是通过按照上面调用python函数的方法,创建一个全局类对象来调用完成。

  • segmentation 错误 以及程序假死
    python异常时,c++调用python的报错信息不及时且信息少。所以,出现假死或段错误等奇怪的现象时,优先排查python代码

2) boost库的方式
  1. 调包的基本结构 C++调用python并传递数组
  2. debug出现的错误,主要是boost/numpy问题,安装之后还是出现找不到库的情况,stackoverflow上有个对话,是统一的问题,需要修改环境变量后解决了,先在这儿记下来吧
  3. boost库的安装

cc.cpp

// c++:  cc.cpp
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION //需要放在numpy/arrayobject.h之前

#include<opencv/cv.hpp>
#include <Python.h>
#include <iostream>
#include <numpy/arrayobject.h>


using namespace cv;
using namespace std;

int main(int argc, char *argv[]) {
    wchar_t *program = Py_DecodeLocale(argv[0], NULL);
    cout << argv[0] << endl;
    if (program == NULL) {
        fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
        exit(1);
    }
    Py_SetProgramName(program);  /* 不见得是必须的 */
    /* 非常重要,折腾的时间主要是因为这儿引起的【1】 */
    Py_Initialize();
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("sys.path.append(\"/home/user/project/run_retina/build\")");
    import_array();
    /* 非常重要 */

    /* 读图 */
    Mat sml_img = imread("lena.jpg");

    /* 导入模块和函数,貌似两种方式都可以,不需要加.py,后面回再提到 */
    // PyObject *pName = PyUnicode_DecodeFSDefault("simple_module");
    PyObject *pName = PyUnicode_FromString("simple_module");
    /*这些检查也非常有帮助*/
    if (pName == NULL) {
        PyErr_Print();
        throw std::invalid_argument("Error: PyUnicode_FromString");
    }
    PyObject *pModule = PyImport_Import(pName);
    if (pModule == NULL) {
        PyErr_Print();
        throw std::invalid_argument("fails to import the module");
    }
    PyObject *pFunc = PyObject_GetAttrString(pModule, "super_resolution");
    if (pFunc == NULL) {
        PyErr_Print();
        throw std::invalid_argument("fails to PyObject_GetAttrString");
    }
    /* 准备输入参数 */
    PyObject *pArgs = PyTuple_New(2);
    if (!sml_img.isContinuous()) { sml_img = sml_img.clone(); }
    npy_intp dims[] = {sml_img.rows, sml_img.cols, 3};

    PyObject *pValue = PyArray_SimpleNewFromData(3, dims, NPY_UINT8, sml_img.data);
    PyTuple_SetItem(pArgs, 0, pValue);  /* pValue的引用计数被偷偷减一,无需手动再减 */
    PyTuple_SetItem(pArgs, 1, Py_BuildValue("i", 2));    /* 图像放大2倍 */
    /* 调用函数 */
    PyObject *pRetValue = PyObject_CallObject(pFunc, pArgs);
    /* 解析返回结果 */
    PyArrayObject *ret_array;
    PyArray_OutputConverter(pRetValue, &ret_array);
    npy_intp *shape = PyArray_SHAPE(ret_array);
    Mat big_img(shape[0], shape[1], CV_8UC3, PyArray_DATA(ret_array));
    imwrite("aa.jpg", big_img);
    /* 释放所有 */
    //Py_DECREF(...);
    return 0;
}

simple_module.py

# simple_module.py
import cv2 as cv
def simple_func(a,b):return a+b
def super_resolution(img, scale=4):
    height, width = img.shape[:2]
    dsize = (width*scale, height*scale)
    big_img = cv.resize(img, dsize)
    print(img.shape, big_img.shape)
    cv.imwrite('aaa.jpg',big_img)
    return big_img

多个numpy返回值

/* 解析返回结果 */
    PyArrayObject *r1, *r2, *r3, *r4, *r5, *r6;
    PyArg_UnpackTuple(pRetValue, "ref", 6, 6, &r1, &r2, &r3, &r4, &r5, &r6);
    npy_intp *shape1 = PyArray_SHAPE(r1);
    npy_intp *shape2 = PyArray_SHAPE(r2);
    npy_intp *shape3 = PyArray_SHAPE(r3);
    npy_intp *shape4 = PyArray_SHAPE(r4);
    npy_intp *shape5 = PyArray_SHAPE(r5);
    npy_intp *shape6 = PyArray_SHAPE(r6);
    std::cout << "shape[1]:" << shape1[0] <<
              " shape2:" << shape2[0] << "," << shape2[1] <<
              " shape3:" << shape3[0] <<
              " shape4:" << shape4[0] << "," << shape4[1] <<
              " shape5:" << shape5[0] <<
              " shape6:" << shape6[0] << "," << shape6[1] <<
              std::endl;
# 返回值的打印结果
#-#-#-#-#-#-#-#-#-#-
list_id float32 (11,)
list_track float32 float32 (20, 2) (11,)
list_box float32 (11, 4)
entran;pass_by;ratio float32 (3,)
entrance_line;rec float32 (4, 2)
#-#-#-#-#-#-#-#-#-#-
shape[1]:11 shape2:20,2 shape3:11 shape4:11,4 shape5:3 shape6:4,2


  1. 代码c++ : cc.cpp ↩︎

  2. 代码python :simple_module.py ↩︎

  • 14
    点赞
  • 102
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
C++调用 Python 进行矩阵运算,可以使用 Python 的 C API 或者使用第三方库。这里介绍使用第三方库 Boost.Python 实现 C++ 调用 Python 进行矩阵运算的方法。 首先,需要安装 Boost.PythonNumPy 库。Boost.Python 是一个 C++ 库,用于将 C++ 代码与 Python 解释器集成在一起。NumPy 是一个 Python 库,用于高效地计算多维数组和矩阵。 假设我们已经安装好了 Boost.PythonNumPy 库,并且已经编写好了一个 Python 脚本 `matrix.py`,用于实现矩阵计算。该脚本中包含了一个名为 `matrix_multiply` 的函数,用于计算两个矩阵的乘积。可以使用以下代码将该函数导出到 C++ 中: ```python #include <boost/python.hpp> #include <numpy/arrayobject.h> using namespace boost::python; // 导出函数 object matrix_multiply(object A, object B) { // 将 Python 对象转换为 NumPy 数组 PyArrayObject *array_A = reinterpret_cast<PyArrayObject*>(A.ptr()); PyArrayObject *array_B = reinterpret_cast<PyArrayObject*>(B.ptr()); // 获取数组的维度和元素型 int ndim_A = PyArray_NDIM(array_A); int ndim_B = PyArray_NDIM(array_B); int *shape_A = PyArray_SHAPE(array_A); int *shape_B = PyArray_SHAPE(array_B); int dtype_A = PyArray_TYPE(array_A); int dtype_B = PyArray_TYPE(array_B); // 检查数组的维度和元素型是否正确 if (ndim_A != 2 || ndim_B != 2 || shape_A[1] != shape_B[0] || dtype_A != NPY_DOUBLE || dtype_B != NPY_DOUBLE) { PyErr_SetString(PyExc_ValueError, "Invalid input arguments"); throw_error_already_set(); } // 创建输出数组 npy_intp shape_C[] = {shape_A[0], shape_B[1]}; PyArrayObject *array_C = reinterpret_cast<PyArrayObject*>(PyArray_SimpleNew(2, shape_C, NPY_DOUBLE)); // 计算矩阵乘积 double *data_A = reinterpret_cast<double*>(PyArray_DATA(array_A)); double *data_B = reinterpret_cast<double*>(PyArray_DATA(array_B)); double *data_C = reinterpret_cast<double*>(PyArray_DATA(array_C)); for (int i = 0; i < shape_A[0]; i++) { for (int j = 0; j < shape_B[1]; j++) { double sum = 0.0; for (int k = 0; k < shape_A[1]; k++) { sum += data_A[i * shape_A[1] + k] * data_B[k * shape_B[1] + j]; } data_C[i * shape_B[1] + j] = sum; } } // 将输出数组转换为 Python 对象并返回 object result = object(handle<>(array_C)); Py_INCREF(array_C); return result; } // 导出模块 BOOST_PYTHON_MODULE(matrix) { import_array(); def("matrix_multiply", matrix_multiply); } ``` 在上面的代码中,我们首先包含了 Boost.PythonNumPy 库的头文件。然后,定义了一个名为 `matrix_multiply` 的函数,用于计算两个矩阵的乘积。该函数的输入参数是两个 Python 对象 `A` 和 `B`,分别代表两个矩阵。在函数中,我们将 Python 对象转换为 NumPy 数组,并检查数组的维度和元素型是否正确。然后,创建输出数组 `C`,并使用三重循环计算矩阵乘积。最后,将输出数组转换为 Python 对象并返回。 在定义完函数后,我们使用 Boost.Python 导出该函数,并将其编译为动态链接库。在 C++ 中,可以使用以下代码调用该函数: ```cpp #include <iostream> #include <boost/python.hpp> using namespace std; using namespace boost::python; int main() { // 初始化 Python 解释器 Py_Initialize(); import_array(); try { // 导入 Python 模块 object module = import("matrix"); // 创建输入矩阵 npy_intp shape_A[] = {2, 3}; double data_A[] = {1, 2, 3, 4, 5, 6}; object A = object(handle<>(PyArray_SimpleNewFromData(2, shape_A, NPY_DOUBLE, data_A))); npy_intp shape_B[] = {3, 2}; double data_B[] = {7, 8, 9, 10, 11, 12}; object B = object(handle<>(PyArray_SimpleNewFromData(2, shape_B, NPY_DOUBLE, data_B))); // 调用 Python 函数 object C = module.attr("matrix_multiply")(A, B); // 打印输出矩阵 PyArrayObject *array_C = reinterpret_cast<PyArrayObject*>(C.ptr()); double *data_C = reinterpret_cast<double*>(PyArray_DATA(array_C)); int *shape_C = PyArray_SHAPE(array_C); for (int i = 0; i < shape_C[0]; i++) { for (int j = 0; j < shape_C[1]; j++) { cout << data_C[i * shape_C[1] + j] << " "; } cout << endl; } } catch (error_already_set) { PyErr_Print(); } // 关闭 Python 解释器 Py_Finalize(); return 0; } ``` 在上面的代码中,我们首先初始化 Python 解释器,并导入 Boost.PythonNumPy 库。然后,使用 Boost.Python 导入 Python 模块 `matrix`。接着,创建两个输入矩阵 `A` 和 `B`,并将其作为参数调用 Python 函数 `matrix_multiply`。最后,将输出矩阵 `C` 打印出来,以验证计算结果是否正确。 需要注意的是,使用 Boost.Python 调用 Python 函数时,需要在程序开头和结尾分别调用 `Py_Initialize()` 和 `Py_Finalize()` 函数来初始化和关闭 Python 解释器。在 C++ 中,可以使用 Boost.Python 提供的 `object` 型来表示 Python 对象,使用 `attr` 函数来调用 Python 对象的属性或方法。在将 Python 对象转换为 NumPy 数组时,需要使用 `reinterpret_cast` 函数进行型转换。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值