opencv源码编写规则

OPENCV做为一种开源的计算机视觉库,咱们有必要去了解这个库的一些编码格式及文件结构。html

一、文档命名规则

  必须将全部功能放入一个或多个.cpp.hpp文件到OpenCV的相应模块中,或者若是贡献的功能是至关大的代码,或者若是它不适合任何现有代码,则应建立新模块模块。python

  • 全部文件名都以小写字母书写,以便更好地兼容POSIX和Windows。
  • C ++接口头的扩展名为.hpp
  • 实现文件的扩展名为.cpp
  • 实现被放到opencv/modules/<module_name>/src,接口被添加到头文件中opencv/modules/<module_name>/include/opencv2/<module_name>没有必要明确地将文件添加到模块,只需从新运行CMake,它将自动添加文件。
  • 样本代码在C ++中,若是有的话,被投入opencv/samples/cpp(具备异常gpuocl模块,有专门的样本目录),在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
  • 代码行不该该很长。一般,它们应限制为100个字符。
  • 不该使用制表。将编辑器设置为使用空格。
  • 缩进是4个空格。
  • 容许使用英文文本(ASCII)。不要将注释或字符串文字放在其余语言中。
  • 头文件必须使用保护宏,保护文件不被重复包含:
#ifndef OPENCV_module_name_header_name_HPP
#define OPENCV_module_name_header_name_HPP
namespace cv { namespace mynamespace {
...
}}
#endif
  • 源文件必须包含precomp.hpp其余标头以前的标头,以便使Visual C ++中的预编译标头机制正常工做。

三、命名约定

  • OpenCV对外部函数,类型和类方法使用混合大小写样式标识符。
  • 班级名称以大写字母开头。
  • 方法'和函数'的名称以小后者开头,除非它们以算法的做者命名,例如cv::Sobel(),在这种状况下,它们能够以大写字母开头。
  • 宏和枚举常量用全都大写字母书写。单词由下划线分隔。
  • 必须使用全部外部函数和类CV_EXPORTS,不然Windows上将存在连接错误。对于要在Python,Java等中公开的函数/类,您应该使用CV_EXPORTS_W而不是CV_EXPORTS,但请注意重载的函数和方法,非标准参数类型等。

四、设计功能和类接口

设计函数接口是一种重要的方式,与库的其他部分一致。功能界面的元素包括:git

  • 功能:功能必须定义良好且非冗余。该功能应易于嵌入到使用其余OpenCV功能的不一样处理流水线中。
  • 名称

    名称应基本反映功能目的。OpenCV中有一些常见的命名模式:程序员

    • 函数名的多数具备形式:<actionName><Object><Modifiers>例如calibrateCameracalcOpticalFlowPyrLK
    • 有时功能能够由它实现它产生的算法名或结果对象的名称,例如被称为SobelCannyRodriguessqrtgoodFeaturesToTrack
  • 返回值

      应该选择它来简化功能使用。一般,建立/计算值的函数应该返回它。对于返回标量值的函数,这是一个好习惯。可是,在图像处理功能的状况下,这将致使大内存块的频繁分配/释放。图像处理功能一般修改输出图像,输出图像做为参数(经过引用)传递,而不是建立和返回结果图像。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 ++仅在参数列表的末尾容许可选参数,因此它也可能影响参数顺序的决策 - 最重要的标志首先出现,而不过重要。

  • 论证顺序
  • 某些参数的默认值

五、高级C ++接口算法

  在某些状况下,您可能但愿将算法表示为类,而不是函数。例如,算法能够包括随时间更新的特定状态(例如,具备其背景统计的背景/前景减法器)。或者算法可能具备太多参数以将它们放入单个调用中。一些算法可能包括几个步骤(例如,机器学习方法中的训练和预测)等。

若是您决定将算法设为类,则应遵循OpenCV算法概念。

基于算法的设计的基本原理和原理以下

  • API在实现更改时保持稳定。
  • 不只要保留源级兼容性,还要保留二进制级兼容性。
  • 保持头文件的清洁,并轻松跟踪API中的更改
  • 保持咱们的工具可以简单而强大地解析OpenCV标头(例如doc checker和自动包装生成器)
  • 但愿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”开头。并不须要重复的虚拟方法的声明StereoMatcherAlgorithm等等,由于你会在你的实际类反正实现它们。

  • .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 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贡献了新算法,如上所示实现,而且它已经集成。
而后,稍后您想要修改它。你应该作以下几步:

  1. 只要您不修改标题,全部更改均可以。只需将它们做为拉取请求提交。
  2. 若是你想为算法添加一些新的属性或修改方法的签名,而且尚未包含你的代码的官方OpenCV版本 - 那也不要紧; 进行修改并提交拉取请求。
  3. 若是您的算法的先前变体已经发布,那么您实际上没法修改接口。建立从前一个派生的新界面,在那里添加更多属性并添加新的构造函数:
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扩展,而且禁止在外部头文件中使用它。

应该摆脱依赖于编译器或平台的构造和系统调用,例如:

  • 编译器pragma的
  • 特定的关键字,例如__stdcall__inline__int64相反,分别使用CV_INLINE(或简单inline的C ++代码),CV_STDCALL(尽量避免使用)int64
  • 编译器扩展,例如min和max的特殊宏,重载宏等。
  • 内联汇编
  • Unix或Win32的特定呼叫,如bcopyreaddirCreateFileWaitForSingleObject等。
  • 具体数据的大小,而不是@ @的sizeof的(sizeof(int)而不是4),字节顺序(*(int*)"\x1\x2\x3\x4"0×01020304或者0×04030201或者是什么?),简单char的代替signed charunsigned char任何地方,除了文本字符串。使用短形式ucharunsigned charschar的符号字符。使用预处理程序指令来处理不可移植的代码片断。

九、编写有关函数的文档

  贡献函数的文档使用内联Doxygen注释编写。该文档每晚构建,并上传到docs.opencv.org。

  使用现有文档做为示例。您也能够经过图片,代码示例等提供大型描述性文本块的教程。

十、实施测试

  • 对于测试,opencv使用GoogleTest框架。请检查项目现场的文档。
  • 每一个测试源文件应test_precomp.hpp首先包含
  • 全部测试代码都放在opencv_test命名空间中。
  • 声明你的Google测试以下:
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
  • 避免包括C ++标准库头同样vectorlistmaplimitsiostream,等。
  • 避免using namespace stdstd::必要时使用(将经常使用类型导入opencv_test命名空间)。
  • 不要使用std::tr1命名空间。
  • 不要包含coreimgproc/的OpenCV标头highgui这些标题包括在内ts.hpp

十一、总结

  • 文件名以小写字母书写。
  • 标题扩展名为.hpp
  • 实现文件的扩展名为.cpp
  • 每一个文件在开始时都包含与BSD兼容的许可证
  • 不要使用制表。缩进是4个空格。行不该超过~100个字符。
  • 在每一个特定的源文件中保持一致的格式化样式(特别是若是您修改现有代码)。
  • 每一个源文件都包含"precomp.hpp"第一个标头。
  • 代码放入cv或嵌套命名空间(cv :: vslam等)。测试代码放入opencv_test命名空间。
  • 在评论和字符串文字中只应使用英语。
  • 外部函数名称和数据类型名称以大小写混合写入。类以大写字母开头,函数以小写字母开头。外部宏以大写形式写入。
  • CV_EXPORTS在外部函数和类声明中使用宏。使用CV_EXPORTS_W了Python-和Java的可包装的API
  • 不要在标头中使用条件编译。
  • 保持外部接口尽量紧凑。不要导出非必要且可隐藏的内部使用类或函数。
  • 暴露的类应该是抽象的,派生自算法。实际的实现必须隐藏在.cpp中。
  • 尝试使您的代码易于为Python,Java等包装。也就是说,尽可能不要引入新类型。限制回调的使用。
  • 考虑将InputArray / InputOutputArray / OutputArray用于数组参数。
  • 使用CV_Error报告有关不正确的参数和/或使用CV_Assert来验证一些条件,例如CV_Assert(inputImage.type() == CV_8UC3)
  • 符合C / C ++标准。避免依赖于编译器,依赖于操做系统和依赖于平台的构造。不要使用C ++ 11和/或TR1扩展。
  • 尽可能不要使用malloc / free,new / delete。使用cv::Matstd::vectorstd::mapcv::AutoBuffercv::Ptr来代替。这些类自动处理内存。
  • 为您的代码提供基于GTest的测试。
  • 为Doxygen评论提供代码文档。欢迎使用教程。

 

 

 参考资料:

一、OpenCV官方手册

二、Coding_Style_Guide

相关文章
相关标签/搜索