一,opencv2.x介绍

一,opencv概览

opencv(open source computer vision library:opencv官网是一个开源的计算机视觉库,该库包含了几百个计算机视觉处理算法。opencv库现阶段已经更新到3.x版本,但由于其不稳定性,故多采用opencv2.x版本。opencv1.x版本是基于c语言撰写的API,而opencv2.x是基于c++撰写的API。
opencv采用模块结构,这代表着该库包含了一些分享的或者是静态的库。下面就是各个主要模块的介绍:

  • core-核心模块,该模块定义了opencv里面的基本的数据结构,包含了多维数组Mat和一些被其他模块使用的基本函数。
  • imgproc-一个图像处理模块,该模块包含了线性和非线性的图像处理滤波器,几何图像转换(缩放,仿射变换,视图弯曲,映射),彩色空间变换,直方图等等。
  • video-视频分析模块,该模块包含运动估计,背景去除和目标追踪算法。
  • calib3d-3d校准模块,该模块包含基本的多视图几何算法,单一和立体摄像机校准,目标姿势估计,立体一致性算法和3d重建元素等。
  • features2d-2d特征模块,改模块包含了显著特征检测器,描述符和描述符匹配器。
  • objdetect-目标检测模块,该模块包含了目标检测和预定义类的实例(例如faces,eyes,mugs,people,cars等等)。
  • highgui-该模块包含了一些处理视频捕获,图像和视频编码,以及一些简单的用户界面能力的易于使用的接口。
  • gpu-该模块包含了为不同的opencv模块使用的GPU加速算法。
  • 其他-其他一些模块为帮助模块,例如FLANN和Google测试包装器,Python绑定等等。

">

二,opencv API特性简介

  • cv命名空间
    所有的opencv类和函数都被放在cv namespace内了。
    访问opencv示例:

    1
    2
    3
    4
    #include "opencv2/core/core.hpp"
    ...
    cv::Mat H = cv::findHomography(points1, points2, CV_RANSAC, 5);
    ...

    或者

    1
    2
    3
    4
    5
    #include "opencv2/core/core.hpp"
    using namespace cv;
    ...
    Mat H = findHomography(points1, points2, CV_RANSAC, 5 );
    ...

    一些现有的或者是未来的opencv外部命名可能会与STL或者其他库中的命名冲突,所有最好显示的使用opencv命名空间来解决冲突问题。

  • 自动内存管理
    opencv自动处理所有的内存问题。
    首先,std::vector, Mat和其他的数据结构都有析构函数来解除已经分配出去的内存区域。但对于cv::Mat来说,这并不意味着析构函数并总是解除内存分配,因为opencv将数据共享考虑在内了。一个析构器将会减少与Mat数据内存相关的引用指针数量,当引用数量降到0时意味着没有变量再使用这块内存,从而这块内存会被释放。相似的,当一个Mat实例被复制的时候,其实并没有真的开辟一块新的内存空间来存储数据,而是引用数量+1,而该引用是这个数据块的另一个拥有者。但是可以使用Mat::clone()函数来创造一个新的内存区域存储数据。
    如下代码会帮助你更好的理解其中的内涵:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // create a big 8Mb matrix
    Mat A(1000, 1000, CV_64F);

    // create another header for the same matrix;
    // this is an instant operation, regardless of the matrix size.
    Mat B = A;
    // create another header for the 3-rd row of A; no data is copied either
    Mat C = B.row(3);
    // now create a separate copy of the matrix
    Mat D = B.clone();
    // copy the 5-th row of B to C, that is, copy the 5-th row of A
    // to the 3-rd row of A.
    B.row(5).copyTo(C);
    // now let A and D share the data; after that the modified version
    // of A is still referenced by B and C.
    A = D;
    // now make B an empty matrix (which references no memory buffers),
    // but the modified version of A will still be referenced by C,
    // despite that C is just a single row of the original A
    B.release();

    // finally, make a full copy of C. As a result, the big modified
    // matrix will be deallocated, since it is not referenced by anyone
    C = C.clone();

    因此,使用Mat和其他一些基础的数据结构是非常容易的。但是还有其他一些高水平数据或者是用户自定义的数据类型(没有自动内存管理功能)呢?opencv为此提供了Ptr<>这个模板类来解决问题,该类与std::shared_ptr类似。
    所以在使用其他数据类型时:不用T* ptr = new T(…); 而是使用Ptr ptr = new T(…);
    详情请看Ptr<>解析

  • 输出数据的自动内存分配
    opencv自动解除内存分配,同样在大多数时候也会自动为输出函数参数自动分配内存。因此如果一个函数拥有一个或者多个输入数组(cv::Mat instances) 和一些输出数组,输出数组会自动的分配或者重新分配内存。输出数组的大小和类型都由输入数组的大小和类型决定。如果需要的话,函数还会采用额外的参数来帮助计算输出数组的性质。如以下例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    #include "cv.h"
    #include "highgui.h"
    using namespace cv;
    int main(int, char**)
    {

    VideoCapture cap(0);
    if(!cap.isOpened()) return -1;

    Mat frame, edges;
    namedWindow("edges",1);
    for(;;)
    {
    cap >> frame;
    cvtColor(frame, edges, CV_BGR2GRAY);
    GaussianBlur(edges, edges, Size(7,7), 1.5, 1.5);
    Canny(edges, edges, 0, 30, 3);
    imshow("edges", edges);
    if(waitKey(30) >= 0) break;
    }
    return 0;
    }

    其中数组frame是由>>操作符自动分配内存的。因为对于视频捕获模块来说视频frame的分辨率和位深度都是未知的。数组edges是由cvtColor函数自动分配内存的。edges和输入数组frame有相同的大小和位深度。而因为彩色变换码CV_BGR2GRAY被传递到函数中所以edges的通道数为1。同时应该注意到frame和edges只有在循环中的第一次执行时才被分配内存,因为从此以后的所有视频frame都有相同的分辨率。如果你改变了视频分辨率,那数组frame则会再次自动重新分配内存。
    这个技术的关键部分就是Mat::create 函数,该函数将需要的数组大小和类型都利用了,如果数组已经有了特定的大小和类型,该方法什么都不会做。相反,他会释放已经分配的内存然后重新分配一块要求大小的新的内存。大多数的函数都会为输出数组调用Mat::create方法,然后就实现了为输出数组自动分配内存。
    来自于这个方案的一些值得注意的异常就是cv::minChannels, cv::RNG::fill以及一些其他的函数和方法。他们并不能为输出数组自动分配内存,所以你必须自己提前完成这个工作。

  • 饱和算法
    opencv作为一个计算机视觉库,它经常处理一些图像像素,这些像素常被编码为每通道8或者16比特因此他们得数值范围有限制。例如对图像进行特性操作,如彩色空间变换,亮度加强,对齐,锐化以及复杂的插值操作都会产生超出允许范围的数值。如果你仅仅用最低的8位或者16位来存储结果,这些结果可能会影响进一步的图像分析。为了解决这个问题,被称作饱和算法的东西就出现了。例如,为了存储r(一个操作的结果数据)到8位深度的图像里,你发现在0~255内最近的值:
    I(x,y)= min ( max (round(r), 0), 255);
    相似的法则被应用到有符号8位,有符号16位以及无符号数据类型中了。这种语义在opencv库中到处都有使用。在c++中,会采用saturate_cast<>函数(类似于标准c++的转换操作),如以下例子:
    I.at(y, x) = saturate_cast(r);
    cv::uchar是opencv的8位无符号整型数据类型。
    注意:饱和算法并不会作用于32位的整型结果。
  • 固定像素类型,模板的限制使用
    模板是c++的一个重大特性,该特性提供了非常强有力的高效的而且安全的数据结构和算法的实现。然而,模板的扩展使可能会增加编译时间和代码量。除此之外,当模板被独立使用的时候接口和实现将会很难分离。对于基本的算法来说这样很好,但是对于一个算法可能包含几千行代码的计算机视觉库来说这样并不友好。为了简化与其他语言(如Python,java,matlab这些可能没有模板或只有有限的模板能力的语言)的绑定发展,现今的opencv实现是基于多态和运行时分配而非模板。在那些运行时分配可能会很慢的地方(如像素访问运算符),或者不可能(如通用的Ptr<>的实现)或者非常不方便(如saturate_cast<>)现在的实现介绍了一些小型的模板类,方法和函数。而opencv中任何其他地方使用模板都是收到限制的。
    因此,只有有限的固定的原始数据类型可以被opencv库操作。如下:
    • 8-bit unsigned integer (uchar)
    • 8-bit signed integer (schar)
    • 8-bit signed integer (schar)
    • 16-bit signed integer (short)
    • 32-bit signed integer (int)
    • 32-bit floating-point number (float)
    • 64-bit floating-point number (double)
    • 一些元素(像素点)包含多个相同数据类型的元素被称为多通道数组,与单通道数组相对应。opencv中最大的可能的通道数被CV_CN_MAX常量设置为512.
      对于这些基本类型,会应用以下枚举常量:
      enum { CV_8U=0, CV_8S=1, CV_16U=2, CV_16S=3, CV_32S=4, CV_32F=5, CV_64F=6 };
      而多通道类型可以用以下选择方式决定:
    • CV_8UC1 … CV_64FC4 常量(通道数为1-4)
    • CV_8UC(n) … CV_64FC(n) or CV_MAKETYPE(CV_8U, n) … CV_MAKETYPE(CV_64F, n) 宏,当通道数超过4或者在编译时无法确定
      范例:
      1
      2
      3
      4
      5
      6
      7
      8
      Mat mtx(3, 3, CV_32F); // make a 3x3 floating-point matrix
      Mat cmtx(10, 1, CV_64FC2); // make a 10x1 2-channel floating-point
      // matrix (10-element complex vector)
      Mat img(Size(1920, 1080), CV_8UC3); // make a 3-channel (color) image
      // of 1920 columns and 1080 rows.
      Mat grayscale(image.size(), CV_MAKETYPE(image.depth(), 1)); // make a 1-channel image of
      // the same size and same
      // channel type as img

而拥有更复杂元素的数组是无法被opencv库构造和处理的。另外,每个函数和方法都只能够处理所有可能的数组类型中的一部分。通常情况下算法越复杂,能够支持的数据格式越少。
以下是一些典型的例子:

* 人脸检测算法只能处理8位的灰度图或者彩色图
* 线性代数函数和大多数的机器学习算法都只能处理浮点类型的数组。
* 基本的函数如cv::add,支持所有的数据类型
* 彩色空间转换函数支持8位无符号整数,16位无符号整数和32位浮点类型。

每个函数的支持类型的子集都已经从实践需要中预定义了,而且可能在未来因为新的需要而扩展。

  • 输入数组和输出数组
    许多opencv的函数处理2维和多维的数字数组。通常,一些函数采用cpp::class:Mat 作为参数,但是在一些情况下,使用std::vector(例如一个指针集合) 或者Matx<>(例如一个3*3矩阵)是更方便的。为了避免API中的过多的复制,特殊的“代理proxy”类便出现了。最基本的“代理”类是InputArray,在一个函数输入中,他被用于传递只读数组,从InputArrray派生出来的OutputArray类被用于为一个函数确定一个输出数组。通常,你不需要关注其内在类型,它将会自动工作。你可以假设不是使用InputArray和OutputArray,而是使用Mat, std::vector<>, Matx<>, 或者是标量。当一个函数有可选的输入或者输出数组时,你没有该数组,可以传递cv::noArray()。
  • 错误处理
    opencv使用exceptions来标识重要的错误。当输入数据有一个正确的格式而且属于特定的值范围,但是因为某些原因(如最优化算法没有收敛)算法不能成功执行,它会返回一个特殊的错误码(典型情况是一个boolean值)。
    exceptions可以是cv::Exception 类或者是它的派生类,而cv::Exception 是std::Exception的派生类。所以它可以在代码中使用标准c++库中的部分优雅的处理。
    exception 是典型的使用CV_Error(errcode, description)宏,或者它的CV_Error_(errcode, printf-spec, (printf-args)),或者CV_Assert(condition)宏。对于性能至关重要的代码,使用CV_DbgAssert(condition)。因为自动内存分配,为了防止突然间的错误所有的内部的内存区都是自动解除分配:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    try
    {
    ... // call OpenCV
    }
    catch( cv::Exception& e )
    {
    const char* err_msg = e.what();
    std::cout << "exception caught: " << err_msg << std::endl;
    }
  • 多线程
    当前opencv的实现是 re-enterable ,就是说,相同的函数,一个类实例的相同的常量方法,或者不同的类的相同的非常量方法都可以在不同的线程中被调用。相同的cv::Mat可以在不同的线程中被使用,因为引用计数操作使用了the architecture-specific atomic instructions.

文章目录
  1. 1. 一,opencv概览
  2. 2. ">
  3. 3. 二,opencv API特性简介
,