这份文档主要针对C++代码。
目录和文件
所有的代码必须写在一个或多个.cpp和.hpp文件中,并放入适当的OpenCV模块目录,如果提交的代码很多,或当前没有适当的模块,可以新建一个模块。
所有文件名小写。
l C++接口头文件应该是.hpp的。
l 实现文件应该是.cpp的。
l 实现文件放在opencv/modules/<module_name>/src,头文件放置opencv/modules/<module_name>/include/opencv2/<module_name>.
l C++的样例代码写在opencv/samples/cpp,Python代码写在opencv/samples/python2
l 文档写在opencv/modules/<module_name>/doc
l 测试文件放在opencv/modules/<module_name>/test,ml模块的测试数据放在opencv_extra/testdata/ml
文件结构
l 所有的代码放在namespace cv,或者,嵌套的namespace,例如cv::vslam
l 一行代码不应太长,最好限制在100字符内。
l 不要使用tab,用空格代替。
l 只能使用英文文本,不要使用其他语言的文本在任何地方。(注,汉语不要使用拼音格式)
l 缩进4字符。
l 头文件必须使用宏进行控制,如下
#ifndef _OPENCV_your_header_name_HPP_
#define _OPENCV_your_header_name_HPP_
#ifdef __cplusplus
namespace cv { namespace mynamespace { ...}}
#endif
#endif
l 源文件必须包含precomp.hpp头文件。
命名惯例
l OpenCV中的函数,自定义类型,类函数使用大小写混合的表示符。
l 类名以大写字母开头
l 函数和方法名以小写字母开头,除非是以算法作者命名的函数,例如cv::Sobel()。
l 宏和枚举全部大写,以下划线分割。
l 所有的函数和类必须用CV_EXPORTS,否则在windows上会有链接错误,如果函数或类有Python,Java的接口,则要用CV_EXPORTS_W代替CV_EXPORTS
l 函数,函数被定义良好,没有多余内容。
命名
函数名应该反映函数目的,大多数函数有这样的格式:<actionName><Object><Modifiers>,例如:calibrateCamera,calcOpticalFlowPyrLK,或者是算法名或函数的返回值得名,例如Sobel,Canny,Rodirgues,sqrt,goodFeaturesToTrack.
返回值
函数最好返回标量类型,为了减少内存的分配释放,最好将返回的图像用引用形式传入,函数内修改后作为一个输出参数返回。不要因为空指针,除0这样的错误而返回值,应该使用cv::Exception抛出一个异常。
参数类型
最好使用OpenCV中已经存在的类型,Mat代表图像或矩阵,vector<Mat>代表一组图像,vector<Point>,vector<Point2f>,vector<Point3f>,vector<KeyPoint>代表点集,连通域,或一组关键点。Scalar代表1到4个元素的数值元组。最好不要使用指针,可以考虑用Ptr<>代替。
参数顺序:<input parameters>,<output parameters> <flags & optional parameters>
入参添加const,大的数据类型通常使用引用传递,原类型和小的结构(int,double,Point,Rect。。。)可以传值。
高级C++接口,Algorithms
Algorithm的设计规范
l 我们希望当算法实现改变的时候接口依然保持稳定。
l 我们希望不但要保持源文件级别的兼容性,也要保持二进制级别的兼容性。
l 我们希望保持头文件的整洁,并且容易跟踪接口的变化。
l 我们希望我们的工具解析OpenCV的头文件是简单和鲁棒的。
l 我们希望构建OpenCV能够很快。
为了达到以上目的,我们物理的将C++类的接口和实现分开。
我们只暴露类的接口,没有类的构造函数,没有成员数据,只有虚函数。真正的实现方式是用类去继承接口。类的构造方式是统过暴露一个函数,这个函数返回一个指向接口的智能指针。
一步步写自己的类
1. 继承Algorithm类或它的子类,作为抽象基类。
... namespace cv { namespace mynamespace {
class MyStereoMatcher :public StereoMatcher {
public: virtual voidsetLambda(double lambda) = 0;
virtual double getLambda()const = 0;
...
};
}}
2. 写一个构造函数返回抽象基类的智能指针。
CV_EXPORTS Ptr<MyStereoMatcher>createMyStereoMatcher(<params...>);
3.在.cpp文件中实现你的接口和构造函数。
/* <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 gettersand 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> createMyStereoMatcher(<args>) {return new MyStereoMatcherImpl(<args>); } }}
扩展修改算法
代码格式
1 if( a > 5 )
2 {
3 int b = a*a;
4 c = c > b ? c : b + 1;
5 }
6 else if( abs(a) < 5 )
7 {
8 c--;
9 }
10 else
11 {
12 printf( "a=%d is farto negative\n", a );
13 }
可移植性,扩展的依赖性
l 代码使用C++98标准,不建议使用C++11或TR1扩展。
l 避免使用依赖编译器或平台的指令,例如
l 编译器的pragma
l 特定的关键字,例如__stdcall, __inline,__int64(或 long long),使用CV_INLINE,CV_STDCALL,int64代替
l 编译器扩展,一些宏像min,max
l 内嵌汇编
l Unix或Win32的特定调用,像, bcopy, readdir, CreateFile, WaitForSingleObject()
l 用sizeof(int)代替4
写函数文档
用ReStructured Text编写文档
每个函数应该包括如下内容:
l C/C++的函数声明
l 简短的描述
l 解释所有的参数
l 用一个简短的例子详细描述函数使用
实现测试代码
OpenCV使用GoogleTest框架。
所有的源文件都要保护precomp.hpp文件
所有的测试代码都在cvtest命名空间内。
照一下样式声明测试代码
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
附录
。。。