python extension c++ 扩展

模块编写参考教程:

https://thomasnyberg.com/cpp_extension_modules.html

https://docs.python.org/3/extending/extending.html

调试参考:

https://stackoverflow.com/questions/15253586/python-extension-debugging

https://wiki.python.org/moin/DebuggingWithGdb

https://blog.csdn.net/wfdtxz/article/details/7368357

python编程的确很方面,但有的时候速度上不如c和c++,对于处理的数据量比较大的情况下还是需要使用python 调用c++的方法,本使用的是python extension的方式,数据转换比较繁琐,这里只提供了自己的一个模板,方便记住。

首先需要创建一个文件夹,报错扩展模块:

 

其中cpp目录用来保存cpp文件,setup.py用来编译和安装扩展模块。

cpp目录下主要包含以下文件:

主要内容格式如下:

setup.py:

此文件主要负责编译c++模块,封装成python可以调用的形式。这里主要修改module_name1项为自己的包名字,sources中为自己用到的cpp文件路径。

import sys
import numpy as np
from distutils.core import setup
from distutils.extension import Extension
package_name = 'level-set'
module_name1 = 'drlse'
version = sys.version[0]
wrap_source = './cpp/wrap_py{0:}.cpp'.format(version)
module1 = Extension(module_name1,
                    include_dirs=[np.get_include(), './cpp'],
                    sources=['./cpp/drlse.cpp', 
                             './cpp/level_set.cpp', wrap_source])

setup(name=package_name,
      ext_modules=[module1])

drlse.cpp:

这里调用c++模块,将python变量的传递给c++方法,并返回c++的结果给python

此中切记要使用NPY_FLOAT32数据类型,不要使用NPY_FLOAT64类型,不然结果返回给python会出现错误。

#include <Python.h>
#include "numpy/arrayobject.h"
#include "level_set.hpp"
#include <iostream>

using namespace std;

// example to use numpy object: http://blog.debao.me/2013/04/my-first-c-extension-to-numpy/
// write a c extension ot Numpy: http://folk.uio.no/hpl/scripting/doc/python/NumPy/Numeric/numpy-13.html
static PyObject *
level_set_wrapper(PyObject *self, PyObject *args)
{
    PyObject *I=NULL,*phi=NULL;
	float W,H,mu,timestep,lambda,alfa,epsilon,potential_function,iters;
    PyArrayObject *arr_I=NULL;
	PyArrayObject *arr_phi=NULL;
    
    if (!PyArg_ParseTuple(args, "OOfffffffff", &I,&phi,&W,&H,&mu,&timestep,&lambda,&alfa,&epsilon,&potential_function,&iters)) return NULL;
    
    arr_I = (PyArrayObject*)PyArray_FROM_OTF(I, NPY_FLOAT32, NPY_IN_ARRAY);
    if (arr_I == NULL) return NULL;
	
	arr_phi = (PyArrayObject*)PyArray_FROM_OTF(phi, NPY_FLOAT32, NPY_IN_ARRAY);
    if (arr_phi == NULL) return NULL;
    
	npy_intp * shape = PyArray_DIMS(arr_I);  // npy_intp array of length nd showing length in each dim.
	
	
	
	drlse_Evolution((float*)arr_I->data, (float*)arr_phi->data, int(W), int(H), mu, timestep, lambda, alfa,
                                 epsilon, (int)potential_function, (int)iters);
    cout<<1<<endl;
    
	
    // level_set2D.InitPhi(mask, image_width, image_height)

    // alfa = 0;
    // level_set2D.Evolution();
    // float* final_phi = level_set2D.GetPhi();

	// PyArrayObject * phi_np = (PyArrayObject*) PyArray_SimpleNewFromData(2, shape, NPY_FLOAT64, final_phi);
	Py_DECREF(arr_I);
	//Py_DECREF(arr_phi);
	return PyArray_Return(arr_phi);

    
}

static PyMethodDef Methods[] = {
    {"drlse",  level_set_wrapper, METH_VARARGS, "computing drlse by cpp"}, 
    {NULL, NULL, 0, NULL}
};

wrap_py3.cpp:

其中需要修改方法名。

#include "drlse.cpp"


static struct PyModuleDef cGeosDis =
{
    PyModuleDef_HEAD_INIT,
    "drlse", /* name of module */
    "",          /* module documentation, may be NULL */
    -1,          /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
    Methods
};


PyMODINIT_FUNC PyInit_drlse(void) {
    import_array();
    return PyModule_Create(&cGeosDis);
}

主要方法模块就不再列了,跟正常的c++写法没有区别主要用来实现自己要提高效率的方法。

以上这些文件,最复杂的是drlse.cpp文件,也就是连接文件,这是这里边的核心,其他模块都比较模板。这些文件准备好以后,可以执行命令以后对方法进行调用。

python setup.py build

python setup.py install

随后可以再python中对模块进行调用和测试,如此调用:

import drlse
import numpy as np
from PIL import Image

filename = '216748_216749-100180_100181-18.jpg'
img = np.array(Image.open(filename).convert('L'))
W,H=img.shape
image_size = W * H
timestep = 5
potential_function=1
c0=2
mu = 0.2 / timestep
_lambda = 20
alfa = 4
epsilon = 1.5
iters = 50


initialLSF = 2 * np.ones(img.shape)
phi=initialLSF.copy()
phi[100:120,100:120]=-c0
final_phi=drlse.drlse(img.astype(np.float32),phi.astype(np.float32), W,H,mu,timestep,_lambda,alfa,epsilon, potential_function,iters)
ted=1
print(final_phi)


    

整个过程中最复杂的部分就是调试,目前没有能够实现再IDE中进行联调。我调试的方法是在cygwin中安装gdb对python测试模块进行调试,然后去发现那一部分有错误,再去查找代码。这部分主要是对drlse.cpp这种连接代码进行调试。c++部分自己单独写了main函数进行了调试,这个过程弄得及其麻烦。。。

调试的时候要注意,使用不同的build命令,我在cygwin中的命令为

CFLAGS='-Wall -O0 -g' python3 setup.py build

感想:之前已经进行过一次python 扩展编程,用了两三天,这回有一次实现另一个方法,还是用了两三天,因此特别记录一下,也希望可以帮助其他有需要的人一下。这个流程比较繁琐,希望有大牛能够提供一种更加有效率和简洁的解决方案。

猜你喜欢

转载自blog.csdn.net/lebusini/article/details/103925461