OPENCV做爲一種開源的計算機視覺庫,咱們有必要去了解這個庫的一些編碼格式及文件結構。html
必須將全部功能放入一個或多個.cpp和.hpp文件到OpenCV的相應模塊中,或者若是貢獻的功能是至關大的代碼,或者若是它不適合任何現有代碼,則應建立新模塊模塊。python
opencv/modules/<module_name>/src
,接口被添加到頭文件中opencv/modules/<module_name>/include/opencv2/<module_name>
。沒有必要明確地將文件添加到模塊,只需從新運行CMake,它將自動添加文件。opencv/samples/cpp
(具備異常gpu
和ocl
模塊,有專門的樣本目錄),在Python示例代碼-到opencv/samples/python2
。opencv/modules/<module_name>/doc
放入其中一個現有文件中或在其中添加新文件/章節。在後一種狀況下,文件名應列在TOC(內容表)文件中opencv/modules/<module_name>/doc/<module_name>.rst
opencv/modules/<module_name>/test
。添加新的測試源文件後,從新運行CMake。若是測試須要一些數據,請將其放在單獨的頂級目錄中。對於ml
模塊,測試數據被放到opencv_extra/testdata/ml
子目錄中,對於其餘模塊 - 放到opencv_extra/testdata/cv
子目錄中。請將測試數據文件的大小限制在幾兆字節內,越小越好。若是某些現有測試數據可用於您的測試(如lena.jpg
等),請從新使用它。標頭和實現文件的其餘規則包括:ios
cv
命名空間,或者可能放入一些嵌套的命名空間,例如cv::vslam
#ifndef OPENCV_module_name_header_name_HPP #define OPENCV_module_name_header_name_HPP namespace cv { namespace mynamespace { ... }} #endif
precomp.hpp
其餘標頭以前的標頭,以便使Visual C ++中的預編譯標頭機制正常工做。cv::Sobel()
,在這種狀況下,它們能夠以大寫字母開頭。CV_EXPORTS
,不然Windows上將存在連接錯誤。對於要在Python,Java等中公開的函數/類,您應該使用CV_EXPORTS_W
而不是CV_EXPORTS
,但請注意重載的函數和方法,非標準參數類型等。設計函數接口是一種重要的方式,與庫的其他部分一致。功能界面的元素包括:git
名稱應基本反映功能目的。OpenCV中有一些常見的命名模式:程序員
<actionName><Object><Modifiers>
例如calibrateCamera
,calcOpticalFlowPyrLK
。Sobel
,Canny
,Rodrigues
,sqrt
,goodFeaturesToTrack
。應該選擇它來簡化功能使用。一般,建立/計算值的函數應該返回它。對於返回標量值的函數,這是一個好習慣。可是,在圖像處理功能的狀況下,這將致使大內存塊的頻繁分配/釋放。圖像處理功能一般修改輸出圖像,輸出圖像做爲參數(經過引用)傳遞,而不是建立和返回結果圖像。github
函數不該該使用返回值來表示關鍵錯誤,例如空指針,除零,不良參數範圍,不支持的圖像格式等。相反,它們應該拋出異常,實例cv::Exception
或其衍生類。另外一方面,建議使用返回值來報告在正常工做的系統中可能發生的很是正常的運行時狀況(例如,跟蹤的對象在圖像以外等)算法
參數類型最好由已經存在的組的OpenCV類型的選擇:Mat
對於光柵圖像和矩陣,vector<Mat>
進行圖像採集,vector<Point>
,vector<Point2f>
,vector<Point3f>
,vector<KeyPoint>
對點集,輪廓或關鍵點的集合,Scalar
爲1〜4元數值的元組(如顏色,四元數等。)不建議使用普通指針和計數器,由於它使接口更低級,意味着更可能的輸入錯誤,內存泄漏等。對於將複雜對象傳遞給函數,方法,請考慮Ptr<>
智能指針模板類。
數組
一致的參數順序很重要,由於它更容易記住順序,它有助於程序員避免錯誤,鏈接錯誤的參數順序。一般的順序是:<輸入參數>,<輸出參數>,<標誌和可選參數>。框架
輸入參數一般具備const
限定符。大對象一般經過常量引用傳遞; 原始類型和小結構(int, double, Point, Rect
)按值傳遞。機器學習
可選參數一般簡化了函數使用。由於C ++僅在參數列表的末尾容許可選參數,因此它也可能影響參數順序的決策 - 最重要的標誌首先出現,而不過重要。
在某些狀況下,您可能但願將算法表示爲類,而不是函數。例如,算法能夠包括隨時間更新的特定狀態(例如,具備其背景統計的背景/前景減法器)。或者算法可能具備太多參數以將它們放入單個調用中。一些算法可能包括幾個步驟(例如,機器學習方法中的訓練和預測)等。
若是您決定將算法設爲類,則應遵循OpenCV算法概念。
爲了實現這些目標,opencv將C ++類的接口和實現部分分開。也就是說,opencv只公開接口,即沒有構造函數的類,沒有數據成員和全部純虛方法。實際的實現被放入從這些接口派生的類中。類的實際構造由外露函數(一般是靜態create
方法)完成。它返回指向接口的智能指針。能夠有多個「構造函數」或「工廠」函數,不必定放在同一個模塊中。用戶能夠添加本身的相同接口的實現,並提供相應的構造函數。(能夠參閱calib3d
模塊的寫法)。
StereoMatcher
若是您添加另外一個立體聲對應算法。... namespace cv { namespace mynamespace { class MyStereoMatcher : public StereoMatcher { public: virtual void setLambda(double lambda) = 0; virtual double getLambda() const = 0; ... // static method to construct the algorithm instance as a smart pointer to the interface class. // there can be several constructors static Ptr<MyStereoMatcher> create(<params> ...); }; }}
也就是說,放置算法將具備的額外方法和屬性。「Getters」應以「get」字樣開頭,「Setters」 - 以「set」開頭。你並不須要重複的虛擬方法的聲明StereoMatcher
,Algorithm
等等,由於你會在你的實際類反正實現它們。
/* <OpenCV license with your copyright added> */ #include "precomp.hpp" namespace cv { namespace mynamespace { class MyStereoMatcherImpl : MyStereoMatcher { MyStereoMatcherImpl(...) { ... } virtual ~MyStereoMatcherImpl() { ... } ... double getLambda() const { return lambda; } // implement getters and setters void setLambda(double l) const { CV_Assert(l >= 0); lambda = l; } void compute(InputArray _left, InputArray _right, OutputArray _disp) // implement necessary methods from StereoMatcher, Algorithm etc. { Mat left = _left.getMat(), right = _right.getMat(); _disp.create(left.size(), CV_16S); Mat disp = _disp.getMat(); ... } ... double lambda; }; Ptr<MyStereoMatcher> MyStereoMatcher::create(<args>) { return makePtr<MyStereoMatcherImpl>(<args>); } }}
如此一來,你的類就建立完畢了
好比說,您爲OpenCV貢獻了新算法,如上所示實現,而且它已經集成。
而後,稍後您想要修改它。你應該作以下幾步:
namespace cv { namespace mynamespace { class MyStereoMatcher : public StereoMatcher {...}; CV_EXPORTS Ptr<MyStereoMatcher> createMyStereoMatcher(<params...>); // new extended interface class MyPyrStereoMatcher : public MyStereoMatcher { public: // more properties ... virtual void setNPyramidLevels(int nlevels) = 0; virtual double getNPyramidLevels() const = 0; ... }; // new contractor(s) CV_EXPORTS Ptr<MyPyrStereoMatcher> createMyPyrStereoMatcher(<new_params...>); }}
而後修改實現類,使其重新類派生(若是你想擁有一個實現類,而不是兩個),例如MyStereoMatcherImpl
將派生自MyPyrStereoMatcher
。其餘一切都保持不變,多虧了一些Ptr<>的方法
,你能夠經過你通過的Ptr<MyPyrStereoMatcher>
任何地方Ptr<MyStereoMatcher>
。
OpenCV中有一個嚴格的編碼指南:每一個單個文件必須使用一致的格式化樣式。
目前在OpenCV中使用並推薦格式化樣式以下:
if( a > 5 ) { int b = a*a; c = c > b ? c : b + 1; } else if( abs(a) < 5 ) { c--; } else { printf( "a=%d is far to negative\n", a ); }
若是僅知足上述規則,也能夠接受其餘樣式。也就是說,若是被其餘代碼改寫了,他應該使用相同的編碼風格。
形式上,代碼必須符合C ++ 98標準。建議不要在實現級別使用C ++ 11或TR1擴展,而且禁止在外部頭文件中使用它。
應該擺脫依賴於編譯器或平臺的構造和系統調用,例如:
__stdcall
,__inline
,__int64
。相反,分別使用CV_INLINE
(或簡單inline
的C ++代碼),CV_STDCALL
(儘量避免使用)int64
。bcopy
,readdir
,CreateFile
,WaitForSingleObject
等。sizeof(int)
而不是4),字節順序(*(int*)"\x1\x2\x3\x4"
0×01020304或者0×04030201或者是什麼?),簡單char
的代替signed char
或unsigned char
任何地方,除了文本字符串。使用短形式uchar
爲unsigned char
和schar
的符號字符。使用預處理程序指令來處理不可移植的代碼片斷。貢獻函數的文檔使用內聯Doxygen註釋編寫。該文檔每晚構建,並上傳到docs.opencv.org。
使用現有文檔做爲示例。您也能夠經過圖片,代碼示例等提供大型描述性文本塊的教程。
test_precomp.hpp
首先包含。opencv_test
命名空間中。TEST(<module_name> _ <tested_class_or_function>,<test_type>){<test_body>}
例如:
TEST(Imgproc_Watershed, regression) { ... }
cvtest::TS::ptr()->get_data_path()
方法。例如,若是您將測試文件放入,則opencv_extra/testdata/cv/myfacetracker/clip.avi
可使用cvtest::TS::ptr()->get_data_path() + "myfacetracker/clip.avi"
獲取文件的完整路徑。要使其正常工做,請將環境變量設置OPENCV_TEST_DATA_PATH
爲<your_local_copy_of_opencv_extra>/testdata
vector
,list
,map
,limits
,iostream
,等。using namespace std
。std::
必要時使用(將經常使用類型導入opencv_test
命名空間)。std::tr1
命名空間。core
/ imgproc
/的OpenCV標頭highgui
。這些標題包括在內ts.hpp
。"precomp.hpp"
第一個標頭。CV_EXPORTS
在外部函數和類聲明中使用宏。使用CV_EXPORTS_W
了Python-和Java的可包裝的API。CV_Error
報告有關不正確的參數和/或使用CV_Assert
來驗證一些條件,例如CV_Assert(inputImage.type() == CV_8UC3)
。cv::Mat
,std::vector
,std::map
,cv::AutoBuffer
,cv::Ptr
來代替。這些類自動處理內存。