PS:本文是对OpenCV中代码风格的简短说明,因为OpenCV的核心库是用C和C++编写的,所以本文仅对用C和C++编写的程序有效。
所有cv和cvaux库文件的命名必须服从于以下规则:
每个文件以BSD兼容的许可声明开头;其它头文件和实现文件的规则包括:
混合C/C++接口头文件用extern “C” { } 包含C语言定义。为了使预编译头机制在Visual C++中工作正常,源文件必须在其它头文件前包含precomp.h头文件。同时,请参见头文件和实现文件的示例。
OpenCV中使用大小写混合样式来标识外部函数、数据类型和类方法。宏全部使用大写字符,词间用下划线分隔。
所有的外部或内部名称,若在多个文件中可见,则必须含有前缀:
为了保持库的一致性,以如下方式设计接口非常重要。函数接口元素包括:
**函数功能必须定义良好并保持精简。**函数应该容易镶入到使用其它OpenCV和IPL函数的不同处理过程。函数名称应该简单并能体现函数的功能。
以下是OpenCV中的一些基本命名模式:
参数类型选择已经存在于OpenCV中的类型更适宜:IplImage用于光栅图像,CvMat用于矩阵,CvSeq用于轮廓线等。建议不使用简单指针和计数,因为有许多函数参数,它降低了库接口并使程序更难读。
一个一致的参数顺序很重要,因为它使参数易于记住顺序并且帮助程序员避免错误和使用错误的参数顺序联接函数。
对于简单过程函数(在命名模式列表中的第一种和第二种类型)
典型的顺序是: 输入参数,输出参数,标记或可选参数。
对于容器元素方法,顺序是:容器,元素位置,标记或可选参数。
输入参数经常用const修饰符。可选参数经常简化函数用法。因为C++允许在参数列表后跟随可选参数,它也可能影响决定以参数顺序,最重要的是标记位于最前,次重要的随后。在函数声明中用CV_DEFAULT宏指定可选参数的默认值.它使声明与C相兼容。
示例函数声明请参见cvexample.h和cv.h、cvaux.h.
本节主要关注以下几点:
任何时候,当传入一个错误的参数或在函数执行时发生其它严重错误时,应该通过cvError函数抛出一个错误. OpenCV中与几乎所有标准的低级C库的IPL类似,有一个错误处理机制.那就是存在一个全局错误状态代替返回错误码:
可以通过以下实现:
使用cvError函数设置
为了更方便使用. 可以通过使用如下宏来代替以上函数:
CV_ERROR和 OPENCV_ERROR代替cvError
CV_CALL和OPENCV_CALL代替调用函数和检查状态
CV_*宏需要在函数中定义”FuncName”字符串变量和”exit”标签,OPENCV_*宏则不需要。
在OpenCV中临时缓存用cvAlloc和cvFree函数分配和回收.函数应注意适当对齐,对未释放的内存保持跟踪,检查溢出。
当程序运行出内存泛围时,cvAlloc抛出一个错误.函数能够调用能由用户赋予完全控制内存分配的低级函数.因此强烈建议使用这些函数.以上描述仅对简单缓存有效.临时图像,内存存储和其它结构使用cvCreate和cvRelease的方式分配和回收.
如果错误发生,并且CV_ERROR或CV_CALL宏被调用,控制转到exit标签处.同在在程序流中可以通过EXIT宏跳转控制.标签可以通过手动或__BEGIN__和宏被定义.此标签引入是为了资源回收.尽管执行分支,当程序流进行时,还是经常发生内存泄漏.这种情况通常发生在分支语句中使用返回语句.
使用库中的技术,可以帮助程序员避免大多数内存泄漏.在函数开始所有的指针被清除(通常在初始化中).在”exit”标签后,对每个指针调用cvFree函数.cvFree函数可以安全处理空指针.在函数内部,返回语句用EXIT宏代替.这样,我们可以确保内存的回收.当然,我们可能忘记对某些块调用cvFree函数,函数执行时,仅仅只是内存泄漏发发生,并且易于捕捉.
OpenCV中的低级函数象IPP中那样主要是是C语言实现原始操作的.它们不同于前面讨论的接口级高级函数(他们使用简单的指针和数值,几乎不用结构体)和错误处理方法(它们返回错误代码而不是全局错误状态).方便并安全的调用这些函数的方法是使用IPPI_CALL宏.
函数实现示例请参见cvexample.cpp文件。
在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 );
}
在符合以上样式的前提下其它样式也可能接受。也就是说,如果一个人修改别人的代码,他应该使用相同的代码样式。
所要代码必须符合以下标准:
ANSI C 第一个语言标准ISO/IEC 9899:1990
C9X (1999年修订的新标准) – ISO/IEC 9899.
C++ 标准 – ISO/IEC 14882-1998.
你应该去除编译器依赖或平台依赖和系统调用,例如:
编译器: pragma’s
特定关键字: __stdcall, __inline, __int64(or long long).使用CV_INLINE, CV_STDCALL, int64分别代替。
编译器扩展,例如?<和 内联汇编
Unix或Win32调用,如:bcopy, readdir, CreateFile, WaitForSingleObject 等。
用sizeof代替具体的数据大小(如sizeof(int)而不是4),字节顺序((int)"\x1\x2\x3\x4"是0x01020304或0x04030201或什么?),用简单的字符有符号字符或无符号字符处理数据(不是字符串)。使用短形式,uchar表示unsigned char和schar表示signed char。使用预处理指令包含非可移植性代码片段。不要试图使用标准元素,主要编译器制造商几乎不支持这些。
每个测试实现为从文本文件输入和输出结果到另外一个文本文件的C/C++函数。这样,函数就有如下接口:
bool ( const char* inputfile, const char* output file );
输入输出文件的格式没有定义。然而,如果测试系统函数用于从文件中读取或写入高级数据(矩阵,文件名,轮廓等),那么文件的格式应与函数兼容。
外部或主测试函数执行所有或选择的测试并且与标准结果相比较,它可以由其它程序或以前执行的相同测试创建。
使用这种设计可以实现检查在一个函数上检查几个特殊的数据集和测式比较函数在武断的数据上的输出和标准输出。在这种情况下输出文件能以不同的两个输出和从前面段的标准结果将全部是零。
测试系统API使测试更容易,它包括:
很多功能实现在OpenCV和HighGUI API上的瘦层.
以下是一步一步描述怎样实现测试的示例:
//创建一个测试体文件:
//
// skeleton_test.cpp
//
#include "opencv_tst.h"
// 测试函数
bool test_skeleton( const char* input, const char* output )
{
// 从文本文件中加载一个图片.
IplImage* img = tstLoadImage( input );
// 运行函数(参见cvexample.cpp)
cvRasterSkeleton( img, CV_SKEL_PAVLIDIS );
//保存结果
tstSaveImage( output );
}
// 注册测试
OPENCV_REGISTER_TEST( test_skeleton, "cvRasterSkeleton" )
// 以上宏扩展了如下代码:
//
// static CvTstReg( test_skeleton, "cvRasterSkeleton",
// "test_skeleton", "skeleton_test.cpp",
// 20 /* code line number */,
// "test_skeleton.in" /* input data file name */
// "test_skeleton.out"/* output data file name */
// "test_skeleton.0" /* etalon data file name */
// );
//
// The line calls constructor of CvTstReg class that links the test to
// the master test list.
将测试文件加入到测试系统项目中.有main()函数的主测试已经包含在项目中,所以不需要再编写执行代码.
创建输入和标准数据.首先你可能需要手工创建.标准数据能够通过用-c(–create-etalon)传输测命令行选项标识生成。并且,这种情况可以在测试函数中用tstIsEtalonMode()函数(在这种情况下,给定可靠值给另外一个算法的变量)处理。
随后,测试系统将在不同的模式下执行.
提示
某些OpenCV函数使用小结构体作为输入.使结构体的名称类型为CvSomething.然后cvSomething通常是内联的,从参数列表构成对象.使用这些内联的构造器,使代码更容易写和读.
使用cvRound,cvFloor和cvCeil快速转换浮点数为相近的整形数或到负无穷在或正无穷大.在x86架构上,这些函数比简单转换操作运行更快.在C9X标准中有几个标准的函数做相同的事情,但是它们现在很少被支持.当情况发生时,以上函数将转换为内联宏.
附录A: 规则简述列表
函数声明形式为:
OPENCVAPI ( arguments );
函数实现形式为:
CV_IMPL
( arguments )
{
CV_FUNCNAME("");
...
__BEGIN__;
...
__END__;
// cleanup ...
return ...;
}