一 介绍
Opencv是一个基于BSD (Berkeley Software Distribution,伯克利软件套件)的开源库。
它包含了多种计算机视觉算法。2.x版本是C++风格的API,而1.x版本是C风格的。目前还有3.x版本,具体几个版本的区别待日后有了了解再进行补充。
Opencv具有模块化的结构,因此它的包包含了一些共享的和静态的库。下面为一些可用的模块:
- core 该模块定义了基础的数据结构。包括多维阵列Mat和一些其他模块经常用到的基本函数。
- imgproc 图像处理模块,包括线性和非线性滤波,几何变换,色彩空间转换,直方图变换等。
- video 视频分析模块,包括运动估计,背景分离和物体跟踪算法。
- calib3D 基础的多视图几何算法,单独的和立体的相机标定,物体姿态检测,立体匹配算法和3-D重建元素。
- fetures2D 显著特征检测子,描述子和描述子匹配器。
- objdetect 对一些有着预定义类的物体进行检测,例如脸,眼睛,马克杯,人,车灯。
- highgui 提供一些方便的接口,如视频捕获,图像和视频编解码和简单的UI功能。
- gpu 提供不同模块的GPU加速算法。
- 还有一些其他的模块,例如flann,google test wrappers,python bindings。
其他章节对每个模块进行了详细的描述。
二 API概念
1.cv namespace
Opencv所有的类和函数都放置在cv namespace中。因此,为了使用函数,在函数前加上cv::
一些名称可能会与STL标准模板库命名空间发生冲突,这是要显示表明使用的命名空间以消除歧义:
2.自动内存管理
Opencv自动处理所有的内存。
首先,对于std::vector,Mat和其他的数据结构体有析构函数,在需要时对内存进行解除分配。这意味着对于Mat,析构函数不总是进行重新分配。他们考虑到数据共享的问题。析构函数减少矩阵内存对应的引用指针计数器。当计数器为0,时,该部分内存就被回收。同样,当一个Mat结构被复制,并没有产生新的数据,只是将引用指针指向该内存,同时引用指针计数器加1。
如果想对数据进行完全复制,需要使用Mat::clone函数,例如:
对于高级的没有自动内存管理的类,Opencv提供了一种模板类Ptr<>的方法。改模板类似于c++ RT1中 std::shared_ptr。
普通方法:
Opencv方法:
3.输出数据的自动分配
Opencv对内存自动地进行回收,大多数情况下,也根据输出函数参数自动地进行内存分配。所以一个函数有一个或多个输入阵列(cv:Mat)和一些输出阵列,那么输出阵列的参数取决于输入阵列。需要的话,函数使用参数来决定输出阵列的性质。
例:
这一技术关键的部分是Mat::create方法。它使用需要的大小和类型。如果一个阵列已经有了特定的大小和类型,那么它就什么都不做。否则,它会释放原先的内存,如果(这包括减少引用计数器并且与0相比),然后重新分配一个符合要求的大小。大多数函数调对每一个输出阵列调用Mat::create方法,所以自动输出内存分配得到执行。
一些例外值得注意,例如cv::mixChannels,cv::RNG::Fill和一些其他的函数和方法,他们不能自动分配输出内存,所以必须提前定义。
4.Saturation Arithmetics
作为一个计算机视觉库,Opencv通常处理的是使用8位或者16位的图像,因此数据会有一个范围。当对像素进行计算时,可能出现上溢或者下溢的情况,如果只储存最低的8位或者16位会影响后续的分析。Saturation Arithmetics可以解决该问题。例如对于一个8位的值,你会在0~255中找到一个与该值最近的整数进行替代,如下式:
![I(x,y)= \min ( \max (\textrm{round}(r), 0), 255)](https://i-blog.csdnimg.cn/blog_migrate/4cd186ed149ce7f0f48b83245f9aaa32.png)
相同的规则适用于8位和16位的signed和unsigned char类型的数据,该语法在该库的任意地方使用。在Opencv中,saturate_cast<>类似于标准c++中的cast操作,下面是上式的代码执行
注意Saturation不适用于32-bit的数据类型!
这里cv::uchar是8-bit的数据类型,
5.定点像素类型;模板的限制使用
模板是c++的一个重要特征,它能够让强大高效并且安全的结构体和算法得到应用。但是模板的广泛使用使得编译时间增加并且代码量巨大。对于基础的算法这种方法很好但是对于计算机视觉库,一个简单的算法可能会达到成千上万行的代码量。同时为了方便与其他语言的链接,例如python,java,matlab,这些语言是没有模板的,因此现在的Opencv基于多态和运行时调度(
polymorphism and runtime dispatching)。像素入口操作运行时调度非常慢,Ptr<>操作不能使用运行时调度,saturate_cast<>()使用运行时调度不方便。现在的应用建议小的模板类,方法和函数。模板类的使用在现在的版本中都受到限制。
对应的,Opencv对可处理的原始数据类型也有一定限制。阵列元素必须是以下类型之一:
- 8-bit unsigned integer (uchar)
- 8-bit signed integer (schar)
- 16-bit unsigned integer (ushort)
- 16-bit signed integer (short)
- 32-bit signed integer (int)
- 32-bit floating-point number (float)
- 64-bit floating-point number (double)
- 通道的最大数由常量 CV_CN_MAX 定义(现在被设置为512)
下面的枚举量分别代表对应的数据类型
多通道的类型可这样表示:
- CV_8UC1 ... CV_64FC4 适用于通道数1to4
- CV_8UC(n) ... CV_64FC(n) or CV_MAKETYPE(CV_8U, n) ... CV_MAKETYPE(CV_64F, n) 适用于通道数超过4或者在编译前通道数未知的情况
注意:
CV_32FC1==CV_32F
,
CV_32FC2==CV_32FC(2)==CV_MAKETYPE(CV_32F,2)
,and
CV_MAKETYPE(depth,n)==(depth&7)+((n-1)<<3)
这意味着类型由最低三位决定,通道数减一后左移3位。
例:
- 人脸识别算法只适用于8-bit的灰度图或者彩色图
- 线性代数函数和大多数机器学习 函数只适用于浮点数
- 基础函数适用于所有类型(如cv::add)
- 颜色空间转换支持8-bit unsigned,16-bit unsigned和32-bit 浮点数
子集的定义是从实际工作中得到,也会在未来的工作需求中得到拓展。
6.输入阵列和输出阵列
许多Opencv函数处理稠密的二维或者多维数值阵列。通常这些函数使用cpp::class:Mat作为参数,但是有时使用std::vector或者Matx<>更加方便,为了避免过多的重复,一种近似类被引入。基类InputArray被用于传递只读阵列作为函数的输入,衍生类 OutputArray被用于指定一个输出阵列类型。通常不需要考虑中间类型,即不需要显式声明变量和类型,这些工作会自动完成。可以认为你可以使用Mat,std::vector,MAtx<>,Vec<>和Scalar代替InputArray/OutputArray。当一个函数有一个可选择的输入或者输出阵列,那么使用cv::noArray()。
7.错误处理
Opencv使用exceptions来显示关键错误。当输入数据形式正确并且在特定范围内,但是由于某些原因算法不能成功(比如算法不收敛),它返回了一个特殊的错误代码(典型的是一个布尔变量)。
exceptions可以使cv::Exception类或者它的衍生类。反过来cv::Exception是std::exception的衍生类。所以,它能在使用标准c++库的代码上使用。使用
CV_Error(errcode,description)宏,CV_Error_(errcode,printf-spec,(printf-args))变体,CV_Assert(condition)宏将exception丢进错误区域。对于关键执行代码,存在一个只有Debug版本的配置
CV_DbgAssert(condition)。
因为自动内存管理,当突然的错误发生时,所有的中间缓存被自动释放。你只需要增加一个try块去捕获exceptions:
现在Opencv应用是完全可重入的。同样cv:Mat也是可以在不同线程中使用,因为引用计数操作为原子操作。