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
来代替。这些类自动处理内存。