python bindings_OpenCV-Python Bindings 如何工作 | 六十四
作者|OpenCV-Python Tutorials
編譯|Vincent
來源|OpenCV-Python Tutorials
目標(biāo)
了解:
如何生成OpenCV-Python bindings?
如何將新的OpenCV模塊擴(kuò)展到Python?
OpenCV-Python bindings如何生成?
在OpenCV中,所有算法均以C ++實現(xiàn)。但是這些算法可以從不同的語言(例如Python,Java等)中使用。綁定生成器使這成為可能。這些生成器在C ++和Python之間建立了橋梁,使用戶能夠從Python調(diào)用C ++函數(shù)。為了全面了解后臺發(fā)生的事情,需要對Python / C API有充分的了解。在官方Python文檔中可以找到一個有關(guān)將C ++函數(shù)擴(kuò)展到Python的簡單示例[1]。因此,通過手動編寫包裝函數(shù)將OpenCV中的所有函數(shù)擴(kuò)展到Python是一項耗時的任務(wù)。因此,OpenCV以更智能的方式進(jìn)行操作。 OpenCV使用位于modules/python/src2中的一些Python腳本,從C ++頭自動生成這些包裝器函數(shù)。我們將調(diào)查他們的工作。
首先,modules/python / CMakeFiles.txt是一個CMake腳本,用于檢查要擴(kuò)展到Python的模塊。它將自動檢查所有要擴(kuò)展的模塊并獲取其頭文件。這些頭文件包含該特定模塊的所有類,函數(shù),常量等的列表。
其次,將這些頭文件傳遞到Python腳本modules/python/src2/gen2.py。這是Python Binding生成器腳本。它調(diào)用另一個Python腳本module/python/src2/hdr_parser.py。這是標(biāo)頭解析器腳本。此標(biāo)頭解析器將完整的標(biāo)頭文件拆分為較小的Python列表。因此,這些列表包含有關(guān)特定函數(shù),類等的所有詳細(xì)信息。例如,將對一個函數(shù)進(jìn)行解析以獲取一個包含函數(shù)名稱,返回類型,輸入?yún)?shù),參數(shù)類型等的列表。最終列表包含所有函數(shù),枚舉的詳細(xì)信息,頭文件中的structs,classs等。
但是標(biāo)頭解析器不會解析標(biāo)頭文件中的所有函數(shù)/類。開發(fā)人員必須指定應(yīng)將哪些函數(shù)導(dǎo)出到Python。為此,在這些聲明的開頭添加了某些宏,這些宏使標(biāo)頭解析器可以標(biāo)識要解析的函數(shù)。這些宏由對特定功能進(jìn)行編程的開發(fā)人員添加。簡而言之,開發(fā)人員決定哪些功能應(yīng)該擴(kuò)展到Python,哪些不應(yīng)該。這些宏的詳細(xì)信息將在下一個會話中給出。
因此頭解析器將返回已解析函數(shù)的最終大列表。我們的生成器腳本(gen2.py)將為標(biāo)頭解析器解析的所有函數(shù)/類/枚舉/結(jié)構(gòu)創(chuàng)建包裝函數(shù)(你可以在編譯期間在build/modules/python/文件夾中以pyopencv_genic_*.h文件找到這些標(biāo)頭文件)。但是可能會有一些基本的OpenCV數(shù)據(jù)類型,例如Mat,Vec4i,Size。它們需要手動擴(kuò)展。例如,Mat類型應(yīng)擴(kuò)展為Numpy數(shù)組,Size應(yīng)擴(kuò)展為兩個整數(shù)的元組,等等。類似地,可能會有一些復(fù)雜的結(jié)構(gòu)/類/函數(shù)等需要手動擴(kuò)展。所有這些手動包裝函數(shù)都放在modules/python/src2/cv2.cpp中。
所以現(xiàn)在剩下的就是這些包裝文件的編譯了,這給了我們cv2模塊。因此,當(dāng)你調(diào)用函數(shù)時,例如在Python中說res = equalizeHist(img1,img2),你將傳遞兩個numpy數(shù)組,并期望另一個numpy數(shù)組作為輸出。因此,將這些numpy數(shù)組轉(zhuǎn)換為cv::Mat,然后在C++中調(diào)用equalizeHist()函數(shù)。最終結(jié)果將res轉(zhuǎn)換回Numpy數(shù)組。簡而言之,幾乎所有操作都是在C++中完成的,這給了我們幾乎與C++相同的速度。
因此,這是OpenCV-Python bindings生成方式的基本形式。
如何擴(kuò)展新的模塊到Python?
頭解析器根據(jù)添加到函數(shù)聲明中的一些包裝宏來解析頭文件。 枚舉常量不需要任何包裝宏。 它們會自動包裝。 但是其余的函數(shù),類等需要包裝宏。
使用CV_EXPORTS_W宏擴(kuò)展功能。 一個例子如下所示。
CV_EXPORTS_W void equalizeHist( InputArray src, OutputArray dst );
標(biāo)頭解析器可以理解諸如InputArray,OutputArray等關(guān)鍵字的輸入和輸出參數(shù)。但是有時,我們可能需要對輸入和輸出進(jìn)行硬編碼。 為此,使用了CV_OUT,CV_IN_OUT等宏。
CV_EXPORTS_W void minEnclosingCircle( InputArray points,
CV_OUT Point2f& center, CV_OUT float& radius );
對于大類,也使用CV_EXPORTS_W。為了擴(kuò)展類方法,使用CV_WRAP。同樣,CV_PROP用于類字段。
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;
}
可以使用CV_EXPORTS_AS擴(kuò)展重載的函數(shù)。 但是我們需要傳遞一個新名稱,以便在Python中使用該名稱調(diào)用每個函數(shù)。 以下面的積分函數(shù)為例。 提供了三個函數(shù),因此每個函數(shù)在Python中都帶有一個后綴。 類似地,CV_WRAP_AS可用于包裝重載方法。
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進(jìn)行擴(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 index
CV_PROP_RW int trainIdx; // train descriptor index
CV_PROP_RW int imgIdx; // train image index
CV_PROP_RW float distance;
};
可以使用CV_EXPORTS_W_MAP導(dǎo)出其他一些小類/結(jié)構(gòu),并將其導(dǎo)出到Python本機(jī)字典中。Moments()就是一個例子。
class CV_EXPORTS_W_MAP Moments
{
public:
CV_PROP_RW double m00, m10, m01, m20, m11, m02, m30, m21, m12, m03;
CV_PROP_RW double mu20, mu11, mu02, mu30, mu21, mu12, mu03;
CV_PROP_RW double nu20, nu11, nu02, nu30, nu21, nu12, nu03;
};
因此,這些是OpenCV中可用的主要擴(kuò)展宏。通常,開發(fā)人員必須將適當(dāng)?shù)暮攴旁谶m當(dāng)?shù)奈恢谩F溆嗟挠缮善髂_本完成。有時,在某些特殊情況下,生成器腳本無法創(chuàng)建包裝。此類函數(shù)需要手動處理,為此,你需要編寫自己的pyopencv_*.hpp擴(kuò)展標(biāo)頭,并將其放入模塊的misc / python子目錄中。但是大多數(shù)時候,根據(jù)OpenCV編碼指南編寫的代碼將由生成器腳本自動包裝。
更高級的情況涉及為Python提供C ++接口中不存在的其他功能,例如額外的方法,類型映射或提供默認(rèn)參數(shù)。稍后,我們將以UMat數(shù)據(jù)類型為例。首先,要提供特定于Python的方法,CV_WRAP_PHANTOM的用法與CV_WRAP相似,不同之處在于它以方法標(biāo)頭作為參數(shù),并且你需要在自己的pyopencv_*.hpp擴(kuò)展名中提供方法主體。 UMat::queue()和UMat::context()是此類幻象方法的示例,這些幻象方法在C++接口中不存在,但在Python端處理OpenCL功能時需要使用。其次,如果一個已經(jīng)存在的數(shù)據(jù)類型可以映射到你的類,則最好使用CV_WRAP_MAPPABLE以源類型作為其參數(shù)來指示這種容量,而不是精心設(shè)計自己的綁定函數(shù)。從Mat映射的UMat就是這種情況。最后,如果需要默認(rèn)參數(shù),但本機(jī)C++接口中未提供,則可以在Python端將其作為CV_WRAP_DEFAULT的參數(shù)提供。按照下面的UMat::getMat示例:
class CV_EXPORTS_W UMat
{
public:
// 你需要提供 `static bool cv_mappable_to(const Ptr& src, Ptr& dst)`
CV_WRAP_MAPPABLE(Ptr);
/! returns the OpenCL queue used by OpenCV UMat.
// 你需要在資料夾代碼中提供方法主體
CV_WRAP_PHANTOM(static void* queue());
// 你需要在資料夾代碼中提供方法主體
CV_WRAP_PHANTOM(static void* context());
CV_WRAP_AS(get) Mat getMat(int flags CV_WRAP_DEFAULT(ACCESS_RW)) const;
};
總結(jié)
以上是生活随笔為你收集整理的python bindings_OpenCV-Python Bindings 如何工作 | 六十四的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 输卵管积液可以做试管婴儿吗
- 下一篇: python元组元素的提取比_Pytho