怎么在Python項(xiàng)目中調(diào)用C++進(jìn)行封裝?針對(duì)這個(gè)問題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡(jiǎn)單易行的方法。
創(chuàng)新互聯(lián)專注于大余企業(yè)網(wǎng)站建設(shè),自適應(yīng)網(wǎng)站建設(shè),購(gòu)物商城網(wǎng)站建設(shè)。大余網(wǎng)站建設(shè)公司,為大余等地區(qū)提供建站服務(wù)。全流程按需策劃,專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,創(chuàng)新互聯(lián)專業(yè)和態(tài)度為您提供的服務(wù)Python主要應(yīng)用于:1、Web開發(fā);2、數(shù)據(jù)科學(xué)研究;3、網(wǎng)絡(luò)爬蟲;4、嵌入式應(yīng)用開發(fā);5、游戲開發(fā);6、桌面應(yīng)用開發(fā)。
配置為Release平臺(tái),不然numpy的頭文件無法被包含,導(dǎo)致編譯器鏈接出錯(cuò)。
特別要注意的一點(diǎn)是用cmd生成pyd文件時(shí),VS2013可能要輸入: SET VS90COMNTOOLS=%VS120COMNTOOLS%(每次重新打開cmd窗口運(yùn)行pythonsetup.py build的時(shí)候都要輸入一次)才能生成成功。
Python中的代碼通過CPython等將語(yǔ)句解釋為C/C++語(yǔ)言,然后編譯器調(diào)用binding入口函數(shù),將傳進(jìn)來的PyObject*參數(shù)通過PyFloat_AsDouble()等轉(zhuǎn)換成C/C++變量。
這些作為輸入變量傳進(jìn)已經(jīng)寫好的C++函數(shù),調(diào)用該函數(shù),返回C++結(jié)果。最后反過來,將C/C++變量轉(zhuǎn)成CPython可以識(shí)別的PyObject*對(duì)象返回給python編譯器(如函數(shù)PyFloat_FromDouble()),完成python到C++的調(diào)用。
當(dāng)C/C++里面的輸入變量或者返回值都不是基本類型時(shí),比如自定義的類,那我們同樣要按照類里面定義數(shù)據(jù)的方式以數(shù)據(jù)的方式來對(duì)應(yīng)改成python能識(shí)別的基本類型的組合。
//以Mat的allocator作為基類,Numpy的Allocator作為繼承類 //這樣可以用派生對(duì)象指針對(duì)基類數(shù)據(jù)進(jìn)行操作 class NumpyAllocator : public MatAllocator { public: NumpyAllocator() { stdAllocator = Mat::getStdAllocator(); } ~NumpyAllocator() {} UMatData* allocate(PyObject* o, int dims, const int* sizes, int type, size_t* step) const { UMatData* u = new UMatData(this); u->data = u->origdata = (uchar*)PyArray_DATA((PyArrayObject*) o); npy_intp* _strides = PyArray_STRIDES((PyArrayObject*) o); for( int i = 0; i < dims - 1; i++ ) step[i] = (size_t)_strides[i]; step[dims-1] = CV_ELEM_SIZE(type); u->size = sizes[0]*step[0]; u->userdata = o; return u; } UMatData* allocate(int dims0, const int* sizes, int type, void* data, size_t* step, int flags, UMatUsageFlags usageFlags) const { if( data != 0 ) { CV_Error(Error::StsAssert, "The data should normally be NULL!"); // probably this is safe to do in such extreme case return stdAllocator->allocate(dims0, sizes, type, data, step, flags, usageFlags); } //確保當(dāng)前使用python的C API是線程安全的 PyEnsureGIL gil; int depth = CV_MAT_DEPTH(type); int cn = CV_MAT_CN(type); const int f = (int)(sizeof(size_t)/8); int typenum = depth == CV_8U ? NPY_UBYTE : depth == CV_8S ? NPY_BYTE : depth == CV_16U ? NPY_USHORT : depth == CV_16S ? NPY_SHORT : depth == CV_32S ? NPY_INT : depth == CV_32F ? NPY_FLOAT : depth == CV_64F ? NPY_DOUBLE : f*NPY_ULONGLONG + (f^1)*NPY_UINT; int i, dims = dims0; cv::AutoBuffer_sizes(dims + 1); for( i = 0; i < dims; i++ ) _sizes[i] = sizes[i]; if( cn > 1 ) _sizes[dims++] = cn; PyObject* o = PyArray_SimpleNew(dims, _sizes, typenum); if(!o) CV_Error_(Error::StsError, ("The numpy array of typenum=%d, ndims=%d can not be created", typenum, dims)); return allocate(o, dims0, sizes, type, step); } bool allocate(UMatData* u, int accessFlags, UMatUsageFlags usageFlags) const { return stdAllocator->allocate(u, accessFlags, usageFlags); } void deallocate(UMatData* u) const { if(!u) return; PyEnsureGIL gil; CV_Assert(u->urefcount >= 0); CV_Assert(u->refcount >= 0); if(u->refcount == 0) { PyObject* o = (PyObject*)u->userdata; Py_XDECREF(o); delete u; } } //基類指針,調(diào)用allocate函數(shù)進(jìn)行內(nèi)存分配 const MatAllocator* stdAllocator; };
上面是先構(gòu)造好能夠相互交互的allocator。
//將PyObject的特性幅值給size,ndims,type int typenum = PyArray_TYPE(oarr), new_typenum = typenum; int type = typenum == NPY_UBYTE ? CV_8U : typenum == NPY_BYTE ? CV_8S : typenum == NPY_USHORT ? CV_16U : typenum == NPY_SHORT ? CV_16S : typenum == NPY_INT ? CV_32S : typenum == NPY_INT32 ? CV_32S : typenum == NPY_FLOAT ? CV_32F : typenum == NPY_DOUBLE ? CV_64F : -1; //.... int ndims = PyArray_NDIM(oarr); //.... const npy_intp* _sizes = PyArray_DIMS(oarr); const npy_intp* _strides = PyArray_STRIDES(oarr); for ( int i = ndims - 1; i >= 0; --i ) { size[i] = (int)_sizes[i]; if ( size[i] > 1 ) { step[i] = (size_t)_strides[i]; default_step = step[i] * size[i]; } else { step[i] = default_step; default_step *= size[i]; } } //.... //這一步直接用PyObject初始化Mat m m = Mat(ndims, size, type, PyArray_DATA(oarr), step); m.u = g_numpyAllocator.allocate(o, ndims, size, type, step); m.addref();
上面是將PyObject對(duì)象轉(zhuǎn)為Mat的部分代碼,具體可以參考o(jì)pencv的cv2.cpp文件:..\OpenCV\sources\modules\python\src2
//將Mat轉(zhuǎn)換為PyObject* template<> PyObject* pyopencv_from(const Mat& m) { if( !m.data ) Py_RETURN_NONE; Mat temp, *p = (Mat*)&m; //確保數(shù)據(jù)拷貝不會(huì)對(duì)原始數(shù)據(jù)m產(chǎn)生破壞 if(!p->u || p->allocator != &g_numpyAllocator) { temp.allocator = &g_numpyAllocator; ERRWRAP2(m.copyTo(temp)); p = &temp; } //將Mat封裝好的userdata指針轉(zhuǎn)給Pyobject* PyObject* o = (PyObject*)p->u->userdata; //引用計(jì)數(shù)器加一 Py_INCREF(o); return o; }
一個(gè)很重要的知識(shí)點(diǎn)是,pyd文件跟dll文件非常相似,所以生成dll比較困難的C++代碼同樣難以生成pyd,C++跟python編譯器各自編譯特性的區(qū)別也會(huì)使得轉(zhuǎn)換存在困難,比如C++的動(dòng)態(tài)編譯。
類;構(gòu)造函數(shù)和析構(gòu)函數(shù);虛函數(shù);(多重)公有繼承;
靜態(tài)函數(shù);重載(包括大多數(shù)操作符重載);引用;
模板編程(特化和成員模板);命名空間;默認(rèn)參數(shù);智能指針。
下面是不能或者比較困難進(jìn)行轉(zhuǎn)換的C++特性:
嵌套類;特定操作符的重載比如new和delete。
關(guān)于怎么在Python項(xiàng)目中調(diào)用C++進(jìn)行封裝問題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。