目录[隐藏]
|
本文档是对OpenCV中代码风格的简短说明,因为OpenCV的核心库(cv,cvaux)是用C和C++编写的,所以本文档仅对用C和C++代码的编写有效。
所有cv和cvaux库文件的命名必须服从于以下规则:
每个文件以BSD兼容的许可声明(模板在Contributors_BSD_Licsense.htm文件中可以找到)开头;其它头文件和实现文件的规则包括:
头文件必须使用保护宏,防止文件被重复包含。混合C/C++接口头文件用extern “C” { } 包含C语言定义。为了使预编译头机制在Visual C++中工作正常,源文件必须在其它头文件前包含precomp.h头文件。 同时,请参见头文件和实现文件的示例。
OpenCV中使用大小写混合样式来标识外部函数、数据类型和类方法。宏全部使用大写字符,词间用下划线分隔。
所有的外部或内部名称,若在多个文件中可见,则必须含有前缀:
为了保持库的一致性,以如下方式设计接口非常重要。函数接口元素包括:
函数功能必须定义良好并保持精简。函数应该容易镶入到使用其它OpenCV和IPL函数的不同处理过程。函数名称应该简单并能体现函数的功能。以下是OpenCV中的一些基本命名模式:
返回值应该选择能简化功能的用法。通常一个函数创建一个对象并返回该对象。对于函数,处理动态数据结构或标量,这是一个好的方法。然而在图片处理函数中会经常分配和回收大内存块,所以图片处理函数不能创建和返回图像结果而是修改输出一个作为参数传入的图像。
函数不应该用关于严重错误(例如空指针,0除数,错误参数范围,不支持的图像格式等)的信号作为返回值。在这种情况下,可以用一种类似于IPL中特殊的错误处理机制。相反,使用期望的运行时情况信号作为返回值比较好。(例如,跟踪图像移动到屏幕外)。
参数类型选择已经存在于OpenCV中的类型更适宜:IplImage用于光栅图像,CvMat用于矩阵,CvSeq用于轮廓线等。建议不使用简单指针和计数,因为有许多函数参数,它降低了库接口并使程序更难读。
一个一致的参数顺序很重要,因为它使参数易于记住顺序并且帮助程序员避免错误和使用错误的参数顺序联接函数。
输入参数经常用const修饰符。可选参数经常简化函数用法。因为C++允许在参数列表后跟随可选参数,它也可能影响决定以参数顺序,最重要的是标记位于最前,次重要的随后。在函数声明中用CV_DEFAULT宏指定可选参数的默认值.它使声明与C相兼容。
示例函数声明请参见cvexample.h和cv.h、cvaux.h.
本节主要关注以下几点:
如前面所说,OpenCV函数广泛使用高级数据类型传送和返回参数。它简化了函数的使用,但是增加了使用错误的参数组合调用函数的可能性(例如浮点图像代替位图,或两个不同大小的图像)。检查标准类型参数存在标准的方法。
IplImage图像能通过CV_CHECK_IMAGE宏被检查。该宏检查传给IplImage的指针和潜在的图像数据指针不为空,图像有像素顺序,没有ROI掩码或冗馀信息。
CV_CHECK_MASK_IMAGE用于检查掩码图像,二值图和灰度图。除了CV_CHECK_IMAGE检查的条件外,它还能确保图像有8位深度和单通道。并且,所有的输入和输出图像在进行深度\通道数和尺寸组合前应该被检查。随后,应该在调用cvGetImageRawData函数返回的图像ROI尺寸后应该被检查。输入等高线和其它动态数据结构能够用CV_IS_CONTOUR和相关的宏进行检查。
任何时候,当传入一个错误的参数或在函数执行时发生其它严重错误时,应该通过cvError函数抛出一个错误. OpenCV中与几乎所有标准的低级C库的IPL类似,有一个错误处理机制.那就是存在一个全局错误状态代替返回错误码:可以通过以下实现:
除了设置错误状态和指定值外,cvError还能进行额外的操作,依据错误处理模式而不同,错误处理模式可以通过cvSetErrorMode调整.在silent模式或parent模式下cvError立即返回.在子模式下,它打印出错误消息并终止应用程序。
为了更方便使用. 可以通过使用如下宏来代替以上函数:
CV_*宏需要在函数中定义”FuncName”字符串变量和”exit”标签,OPENCV_*宏则不需要。
当程序运行出内存泛围时,cvAlloc抛出一个错误.函数能够调用能由用户赋予完全控制内存分配的低级函数.因此强烈建议使用这些函数.以上描述仅对简单缓存有效.临时图像,内存存储和其它结构使用cvCreate<Object>和cvRelease<Object>的方式分配和回收.
如果错误发生,并且CV_ERROR或CV_CALL宏被调用,控制转到exit标签处.同在在程序流中可以通过EXIT宏跳转控制.标签可以通过手动或__BEGIN__和宏被定义.此标签引入是为了资源回收.尽管执行分支,当程序流进行时,还是经常发生内存泄漏.这种情况通常发生在分支语句中使用返回语句.
使用库中的技术,可以帮助程序员避免大多数内存泄漏.在函数开始所有的指针被清除(通常在初始化中).在”exit”标签后,对每个指针调用cvFree函数.cvFree函数可以安全处理空指针.在函数内部,返回语句用EXIT宏代替.这样,我们可以确保内存的回收.当然,我们可能忘记对某些块调用cvFree函数,函数执行时,仅仅只是内存泄漏发发生,并且易于捕捉.
OpenCV中的低级函数象IPP中那样主要是是C语言实现原始操作的.它们不同于前面讨论的接口级高级函数(他们使用简单的指针和数值,几乎不用结构体)和错误处理方法(它们返回错误代码而不是全局错误状态).方便并安全的调用这些函数的方法是使用IPPI_CALL宏.
函数实现示例请参见cvexample.cpp文件。
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 ); }
bool <TestName>( const char* inputfile, const char* output file );
使用这种设计可以实现检查在一个函数上检查几个特殊的数据集和测式比较函数在武断的数据上的输出和标准输出。在这种情况下输出文件能以不同的两个输出和从前面段的标准结果将全部是零。
//创建一个测试体文件: // // 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.
某些OpenCV函数使用小结构体作为输入.使结构体的名称类型为CvSomething.然后cvSomething通常是内联的,从参数列表构成对象.使用这些内联的构造器,使代码更容易写和读.
使用cvRound,cvFloor和cvCeil快速转换浮点数为相近的整形数或到负无穷在或正无穷大.在x86架构上,这些函数比简单转换操作运行更快.在C9X标准中有几个标准的函数做相同的事情,但是它们现在很少被支持.当情况发生时,以上函数将转换为内联宏.
本文不是完整的样式参考,更多详细和写得更好相关主题列表如下:
OPENCVAPI <returnType> <functionName>( arguments );
CV_IMPL <returnType> <functionName>( arguments ) { CV_FUNCNAME("<functionName>"); ... __BEGIN__; ... __END__; // cleanup ... return ...; }
本文是hzy ([email protected]) 02:09 2006年8月17日 (CST)根据OpenCV Coding Style Guide翻译。