OpenCV-Python bindings是如何生成的(1)
翻譯自How OpenCV-Python Bindings Works?
目標(biāo)
學(xué)習(xí)
- OpenCV-Python bindings是如何生成的
- 如何為Python擴(kuò)展新的opencv模塊
OpenCV-Python bindings是如何生成的
在OpenCV里,所有算法都是用C++實(shí)現(xiàn)的。但是這些算法可以在別的語(yǔ)言里使用,比如Python,Java等。這就是通過bindings生成器實(shí)現(xiàn)的。這個(gè)生成器產(chǎn)生一個(gè)C++和Python之間的橋梁,讓用戶可以在Python里調(diào)用C++函數(shù)。要完整了解后臺(tái)發(fā)生了什么,需要Python/C API的知識(shí),在Python官方文檔里有一個(gè)擴(kuò)展C++的函數(shù)到Python里的簡(jiǎn)單例子。通過手工寫包裝函數(shù)的方法在OpenCV里把所有函數(shù)擴(kuò)展到Python是個(gè)很費(fèi)時(shí)的任務(wù),所以O(shè)penCV用了個(gè)更智能的辦法。OpenCV用一些Python腳本從C++的頭文件自動(dòng)生成這些包裝函數(shù),這些腳本放在modules/python/src2。我們來(lái)看看他們做了什么。
首先,modules/python/CMakeFiles.txt是一個(gè)CMake腳本,用來(lái)檢查要擴(kuò)展到Python的模塊。它會(huì)自動(dòng)檢查所有要擴(kuò)展的模塊,并抓取他們的頭文件。這些頭文件包含那個(gè)模塊的所有類,函數(shù),常量的列表等。
然后,這些頭文件被傳給Python腳本modules/python/src2/gen2.py。這就是那個(gè)Python bindings生成腳本。它會(huì)調(diào)用另外一個(gè)Python腳本modules/python/src2/hdr_parser.py。這是頭文件語(yǔ)法分析器腳本。這個(gè)頭文件語(yǔ)法分析器把整個(gè)頭文件拆分成小的Python列表。這些列表包含所有特定函數(shù)和類的詳細(xì)內(nèi)容。比如,一個(gè)函數(shù)會(huì)被解析成一個(gè)包含函數(shù)名,返回類型,輸入?yún)?shù),參數(shù)類型等的列表。最后的列表會(huì)包含那個(gè)頭文件里所有的函數(shù),結(jié)構(gòu)體,類等的詳細(xì)信息。
但是頭文件語(yǔ)法分析器并不解析頭文件里所有的函數(shù)和類。開發(fā)人員得指定哪些函數(shù)是要導(dǎo)出到Python的。為此在這些聲明開始添加了一些宏,好讓頭文件語(yǔ)法分析器能識(shí)別要解析的函數(shù)。這些宏是由要寫特定函數(shù)的開發(fā)人員添加的。簡(jiǎn)單來(lái)說(shuō),開發(fā)人員來(lái)決定哪些函數(shù)要擴(kuò)展到Python,而哪些不用。
頭文件語(yǔ)法分析器會(huì)返回一個(gè)最后的解析了的函數(shù)的大列表。我們的生成器腳本(gen2.py)會(huì)為所有被分析器解析的 函數(shù)/類/枚舉/結(jié)構(gòu)體 創(chuàng)建包裝函數(shù)(你可以在編譯階段在build/modules/python/目錄找到類似于pyopencv_generated_*.h這樣的頭文件)。但是有些基礎(chǔ)的OpenCV數(shù)據(jù)類型比如Mat, Vec4i, Size 需要手工擴(kuò)展。比如,一個(gè)Mat類型應(yīng)該被擴(kuò)展成Numpy數(shù)組,Size應(yīng)該被擴(kuò)展成兩個(gè)整數(shù)的元組等。類似的,有些復(fù)雜的 結(jié)構(gòu)體/類/函數(shù) 需要被手工擴(kuò)展。所有這種手工擴(kuò)展函數(shù)都放在modules/python/src2/cv2.cpp。
現(xiàn)在唯一剩下的事情是編譯這些包裝文件成cv2模塊。所以當(dāng)你在Python里調(diào)用一個(gè)函數(shù)時(shí),比如 res = equalizeHist(img1, img2)。你傳入兩個(gè)numpy數(shù)組,并希望輸出另一個(gè)numpy數(shù)組。這些numpy數(shù)組被轉(zhuǎn)換成了cv::Mat并在C++里調(diào)用equalizeHist()函數(shù)。最后的結(jié)果res被轉(zhuǎn)換回Numpy數(shù)組。簡(jiǎn)單來(lái)說(shuō),就是幾乎所有的運(yùn)算都是在C++里完成的,這使執(zhí)行速度也和C++里幾乎一樣。
這就是OpenCV-Python binding 如何產(chǎn)生的基本介紹。
如何在Python里擴(kuò)展新的模塊
頭文件語(yǔ)法分析器基于一些添加在函數(shù)聲明里的包裝的宏來(lái)分析頭文件。枚舉常量不需要任何包裝宏,他們是被自動(dòng)包裝的。但是剩下的函數(shù)和類需要包裝宏。
函數(shù)使用CV_EXPORTS_W宏來(lái)擴(kuò)展,下面是例子:
CV_EXPORTS_W void equalizeHist( InputArray src, OutputArray dst );頭文件語(yǔ)法分析器可以從關(guān)鍵字如 InputArray, OutputArray等理解輸入和輸出參數(shù)。但是有時(shí)候,我們可能需要硬編碼輸入和輸出,這時(shí)就需要用到CV_OUT, CV_IN_OUT等宏。
CV_EXPORTS_W void minEnclosingCircle( InputArray points, CV_OUT Point2f& center, CV_OUT float& radius );對(duì)大的類也用 CV_EXPORTS_W。要擴(kuò)展類方法,可以用 CV_WRAP。類似的,CV_PROP用來(lái)擴(kuò)展類字段。
class CV_EXPORTS_W CLAHE : public Algorithm { public: CV_WRAP virtual void apply(InputArray src, OutputArray dst) = 0; CV_WRAP virtual void setClipLimit(double clipLimit) = 0; CV_WRAP virtual double getClipLimit() const = 0; }重載函數(shù)可以用CV_EXPORTS_AS來(lái)擴(kuò)展。但是我們需要傳入一個(gè)新名字,這樣在Python里能夠通過新名字調(diào)用函數(shù)。下面的例子,有三個(gè)函數(shù),每個(gè)在Python里都帶一個(gè)前綴。類似的CV_WRAP_AS可以被用來(lái)包裝重載方法。
CV_EXPORTS_W void integral( InputArray src, OutputArray sum, int sdepth = -1 ); CV_EXPORTS_AS(integral2) void integral( InputArray src, OutputArray sum,OutputArray sqsum, int sdepth = -1, int sqdepth = -1 ); CV_EXPORTS_AS(integral3) void integral( InputArray src, OutputArray sum,OutputArray sqsum, OutputArray tilted,int sdepth = -1, int sqdepth = -1 );小的類/結(jié)構(gòu)體用CV_EXPORTS_W_SIMPLE來(lái)擴(kuò)展。這些結(jié)構(gòu)體通過傳值的方式給C++函數(shù)。比如KeyPoint, Match等。他們的方法用CV_WRAP擴(kuò)展,屬性字段用CV_PROP_RW擴(kuò)展。
class CV_EXPORTS_W_SIMPLE DMatch { public: CV_WRAP DMatch();CV_WRAP DMatch(int _queryIdx, int _trainIdx, float _distance); CV_WRAP DMatch(int _queryIdx, int _trainIdx, int _imgIdx, float _distance);CV_PROP_RW int queryIdx; // query descriptor indexCV_PROP_RW int trainIdx; // train descriptor indexCV_PROP_RW int imgIdx; // train image indexCV_PROP_RW float distance; };一些其他的小類/結(jié)構(gòu)體可以用CV_EXPORTS_W_MAP來(lái)導(dǎo)出,導(dǎo)出到Python的原生字典。Moments()就是這樣一個(gè)例子:
class CV_EXPORTS_W_MAP Moments { public: //! spatial moments CV_PROP_RW double m00, m10, m01, m20, m11, m02, m30, m21, m12, m03; //! central moments CV_PROP_RW double mu20, mu11, mu02, mu30, mu21, mu12, mu03; //! central normalized momentsCV_PROP_RW double nu20, nu11, nu02, nu30, nu21, nu12, nu03; };所以這些是OpenCV里主要的擴(kuò)展宏,一般來(lái)說(shuō),開發(fā)人員得在合適的位置放上合適的宏。剩下的工作就由生成器腳本完成。有時(shí)候可能有意外情況生成器腳本沒法創(chuàng)建包裝器,這樣的函數(shù)需要手工處理。但是大多數(shù)情況,用OpenCV編碼指導(dǎo)寫的代碼應(yīng)該就能被生成器腳本自動(dòng)包裝了。
總結(jié)
以上是生活随笔為你收集整理的OpenCV-Python bindings是如何生成的(1)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 常用的lucene分词器-笔记
- 下一篇: websocket python爬虫_p