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 擴展編程,用了兩三天,這回有一次實現另一個方法,還是用了兩三天,因此特別記錄一下,也希望可以幫助其他有需要的人一下。這個流程比較繁瑣,希望有大牛能夠提供一種更加有效率和簡潔的解決方案。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章