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