1.1 OpenCV介绍与安装

目前使用版本:OpenCV 3.4.13

  OpenCV(开源机器视觉库)是一个开源的BSD许可的库,包括数百种机器视觉算法。此文档描述了基于C++的OpenCV 2.x API。自从OpenCV2.4版本以后,C的API已经弃用,不再使用c编译器进行测试。
        OpenCV具有模块化结构,意味着其包含着几个共享库或静态库。以下模块可供选择:

  • Core(核心功能):一个紧凑的模块,定义基本数据结构,包括密集的多维数组MAT并且包含供其他模块使用的基本功能。
  • Imgproc(图像处理):图像处理模块,包括线性和非线性图像滤波,图像几何转换(调整大小,仿射变换和透视扭曲,基于表格的重映射),颜色空间转换,直方图等功能
  • Video(视频分析):视频分析模块包括了运动估计,背景消除,目标跟踪算法。
  • CALIB3D(相机校准和三维重建):基本多视图几何算法,单一立体相机校准,姿态估计,立体对应算法以及三维重建元素。
  • features2D(2D特性框架):特征检测器,特征描述器,特征匹配器。
  • objdetct(对象检测):预定义类对象实例的检测(例如:脸,眼睛,马克杯,人,汽车等)
  • higggui(高级GUI):简单易用的UI接口
  • videoio(视频IO):简单易用的视频捕获,编码模块
  • 其他模块:FLANN,谷歌测试封装器,Python绑定等等

本文档后面的章节描述了每个模块的功能。但是首先需要确保熟悉库中广泛使用的通用API.

API概念

cv命名空间

    所有OpenCV类和函数都放在cv命名空间中。因此代码中要使用这些功能,需要使用cv:: 说明符或者使用using namespace cv指令;

#include "opencv2/core.hpp"
cv::Mat H = cv::findHomography(points1, points2, cv::RANSAC, 5);
///或
#include "opencv2/core.hpp"
using namespace cv;
Mat H = findHomography(points1, points2, RANSAC, 5 );

一些现在或者未来会有的外部名称可能和STL或者其他库冲突。在这些情况下要显式的使用命名空间说明符来解决名称冲突的问题

Mat a(100, 100, CV_32F);
randu(a, Scalar::all(1), Scalar::all(std::rand()));
cv::log(a, a);
a /= std::log(2.);

自动内存管理

    OpenCV自动处理所有内存。

    首先函数或方法使用std::vector,cv::Mat和其他数据结构都有对应的析构函数,在需要的时候彻底释放底层内存缓冲区。这意味着析构函数并不像Mat那样释放缓冲区。他们考虑了可能存在的数据共享的情况。析构函数使与矩阵数据缓冲区相关联的引用计数器递减。当且仅当引用计数器到0时,即没有其他结构引用同一缓冲区时,缓冲区才会被释放。类似的,当复制Mat实例时并不是真正的复制实际数据,相反引用计数器会递增,以记住相同数据还有另外一个使用者。还有一个Mat::clone方法可以创建矩阵数据的完整副本,例如:

Mat A(1000, 1000, CV_64F);///创建8Mb矩阵
Mat B = A;//为同一个矩阵创建另外的引用;这是一个即时操作,并不用考虑矩阵的大小
Mat C = B.row(3);///为矩阵A的第三行创建另外一个矩阵C,同样并不存在数据复制
Mat D = B.clone();//创建一个单独的矩阵副本
B.row(5).copyTo(C);///复制B的第5行到C,也就是复制A的第5行到A的第三行
A = D;///让A和D共享数据,之后A的修改版本仍然可被B或C引用
B.release();//现在让B成为一个空矩阵(没有引用内存缓冲区),但是修改后的A仍然可被C引用,尽管C只是原始A的一行
C = C.clone();//复制C的完整副本。结果大的修改矩阵即将被释放,因为它没有被任何人引用

  可以看到Mat和基本数据结构的使用是比较简单的。但是在没有考虑自动内存管理的情况下,创建高级类甚至高级用户数据类型会如何呢?对于这种情况,OpenCV提供了cv::Ptr模板类,类似于C++ 11里的std::shared_ptr.所以不用使用普通指针。

T* ptr=new T(...);
///等价于
Ptr<T> ptr(new T(...));
///等价于
Ptr<T> ptr makePtr<T>(...);

 Ptr<T> 封装了一个指向T实例的指针和一个与该指针相关的引用计数器。详情可见cv::Ptr描述。

自动分配输出数据

    OpenCV会自动释放内存,并且大多数情况下会自动为输出函数分配内存。因此如果函数有一个或多个输入数组(cv::Mat实例)和一些输出数组,则会子自动分配会重新分配输出数组。输出数组的大小和类型由输入数组的大小和类型决定。如果需要的话,函数会接受额外的参数,以帮助确定输出数组的属性。

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
using namespace cv;
int main(int, char**)
{
    VideoCapture cap(0);
    if(!cap.isOpened()) return -1;
    Mat frame, edges;
    namedWindow("edges", WINDOW_AUTOSIZE);
    for(;;)
    {
        cap >> frame;
        cvtColor(frame, edges, COLOR_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矩阵数组会由>>根据视频捕获模块已知的视频分辨率和位深参数来自动分配内存。edge矩阵数组由cvtColor()函数自动分配内存,其和输入的frame数据具有相同的大小和位深。由于颜色转换码是cv::COLOR_BGR2GRAY所以通道数是1,同时也意味着是彩色到灰度的转换。注意frame和edge只在第一次循环体执行的时候分配一次空间。因为所有的下一帧视频具有相同的分辨率。如果你以某种方式改变了视频分辨率,阵列会自动重新分配。

     此项技术的关键组成部分是Mat::create()方法。此方法接受所需数组的大小和类型。如果此数组已经指定了大小和类型则此函数不执行任何操作。否则它会释放之前粉喷的数据(如果有的话,这里涉及减少引用计数器并将其与0比较),然后分配一个所需大小的空间。大多数函数为每个输出数组调用了Mat::create()方法,因此实现了自动输出数据分配。

    上述例子中还有一些值得注意的函数:cv::mixChannels(),cv::RNG::Fill()以及其他一些函数和方法,他们不需要分配输出数组,所以你必须提前分配好空间。   

饱和算法

    作为一个计算机视觉库,OpenCV处理了大量的图像像素,这些像素通常以紧凑的,每个通道8位或16位的形式编码,因此有一个有限的值范围。另外图像处理上的某些操作,色彩空间转化,亮度/对比度调整,锐化,复杂插值(双立方,Lanczos)能够产生超出可用范围的值。如果仅仅存储最低8(16)位数据,会导致视觉伪影,并可能影响进一步的图像分析。为了解决这个问题,使用了所谓的饱和算法。例如要哦将操作的结果r存储到8位图像,您需要在0--255的范围:

I ( x , y ) = min ( max ( round ( r ) , 0 ) , 255 )

类似的规则适用于8位有符号,16位有符号和无符号数据类型。这个算法在库中随处可见,在C++代码中,它总是使用cv::saturate_cast<>函数来完成的,这些函数类似于c++的强制类型转换。下面是 上述公式的执行情况: I.at< uchar >(y, x) =  saturate_cast<uchar> (r);

    cv::uchar是OpenCV的8位无符号整数类型,在优化后的SIMD代码中,使用了paddusb,packuswb等SSE2指令,他们帮助实现与c++代码完全形同的行为

注意:当结果是32位整数时,不能应用饱和算法。

固定像素类型以及模板的有限使用

    模板是C++的一个伟大特性,它可以实现非常强大,高效安全的数据结构和算法。但是大量使用模板会显著增加编译代码的时间和编译后的代码大小。此外如果只是用模板,很难将接口和实现分离开。这对于基本算法来说是很好的,但是对于计算机视觉库来说就不太好了,因为一个算法可能会跨越数千行代码。正因为如此,并且简化了其他语言(Python,Java,Matlab)的绑定开发,这些语言根本没有模板或者模板功能有限,目前OpencCV就是基于模板实现的多态性和运行调度。运行时调度速度太慢或者有些不能实现或者实现不方便例如(像素访问操作太慢,cv::Ptr<>()实现不方便)。OpenCV当前实现中引入了小的模板类,方法,和函数。当前版本的OpenCV仅仅使用有限的模板操作。

    因此库可以操作的基本数据类型是有限的,固定的。也就是说数组元素应该具有一下类型之一:

  • 8位无符号整数 uchar
  • 8位有符号整数 schar
  • 16位无符号整数 ushort
  • 16位有符号整数 short
  • 32位有符号整数 int
  • 32位浮点数  float
  • 64位浮点数  double
  • 由多个相同元素组成的元素是元组.元素为元组的数组是多通道数组,与元素为标量值的单通道数组相反.通道的最大可能数量是由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 };

多通道类型(n-channel)可通过以下选项指定:

  • CV_8UC1 ... CV_64FC4常量(常用于从1--4的多个通道)
  • CV_8UC(n)...cv_64FC(n)或者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)另外CV_MAKETYPE(depth,n) == (depth &7) + ( ( n-1 )<<3).这意味着常量类型由depth组成,取最低的3位,通道数减1,取下一个log2(CV_CB_MAX)位.

例如:

Mat mtx(3, 3, CV_32F); //创建一个3x3的浮点矩阵
Mat cmtx(10, 1, CV_64FC2); //创建一个10x1 2通道的浮点矩阵(10元复向量)
Mat img(Size(1920, 1080), CV_8UC3); //创建一个3通道(颜色)图片,
Mat grayscale(image.size(), CV_MAKETYPE(image.depth(), 1));//创建一个单通道(灰度)图片大小和图片大小保持一致.

具有更复杂元素的数组不能使用OpenCV构造或处理.而且每个函数或方法只能处理所有可能数组类型的子集.通常算法越复杂,支持的格式的子集就越小.下面是典型的限制示例:

  • 人脸检测算法仅适用于8位灰度或者彩色图像
  • 线性代数和大多数机器学习算法只适用于浮点数组
  • 基本功能.例如cv:add支持所有的类型
  • 颜色控件转换函数支持8位无符号,16位无符号,和32位浮点数

每个函数的支持类型子集是从实际需要中定义的,可以根据未来用户需求进行扩展.

输入数组和输出数组

    许多OpenCV函数处理密集的二维或者多维数值数组.通常这种函数以cppMat作为参数,在某些情况下,使用std::vector<>(对于点集)或cv::Matx<>(对于3x3单应性矩阵)更为方便.为了避免API中有许多的重复,引入了特殊的proxy类.proxy类的基类是cv:InputArray.它用于传入只读数组数据.对应的派生类cv::OutArray用于为函数指定数据输出数组.通常你不需要关系这些数据的中间类型(你也不必要显示的声明这些类型的变量)它会自动工作.你可以使用Mat,std::vector<>,cv::Matx<>,cv::Vec<>或者cv::Scalar来代替InputArray/OutArray.当一个函数有一个可选的输入或输出数组,而你没有对于的数组的时候,可传递一个cv::noArray().

错误处理

     OpenCV使用异常来表示严重错误.当输入数据具有正确的格式且在指定的值范围内,但是算法由于某些原因无法成功(优化算法没有收敛),它将返回一个特殊的错误代码(通常是一个布尔变量).异常是cv::Exception类或其派生类.反过来cv::Exception是std::Exception的衍生物.因此你可以使用其他标准c++库组件的代码来优雅的处理.

    通常可以使用CV_Error(错误码,错误描述信息)宏或者其他类似printf的CV_Error_(errcode,printf_spec,printf_args)变量抛出异常,或者使用CV_Assert(condition)宏检查条件并在不满足是抛出异常.对于性能关键的代码,有CV_DbgAssert(condtion)它只在调试配置中保留.由于自动内存管理,在突然发生错误的情况下,所有中间缓冲区都会自动就那个释放.你只需添加一个try语句来捕捉异常即可:

try
{
    ... // call OpenCV
}
catch (const cv::Exception& e)
{
    const char* err_msg = e.what();
    std::cout << "exception caught: " << err_msg << std::endl;
}

多线程和可重入

    当前版本的OpenCV是完全可重入的.即可以在不同的线程调用不同类的实例的相同函数或方法.同样一个Mat可以在不同线程中使用,因为引用计数器操作使用特定于体系架构的原子操作.

OpenCV概论

    这里你可以了解到如何设置计算机来使用OpenCV库的教程.此外还可找到非常基本的示例源码来慢慢了解OpenCV.

Linux上安装OpenCV

   包依赖

  • GCC版本高于 4.4.x
  • CMake 高于2.8.7
  • Git
  • GTK+高于2.x 包括头文件(libgtk2.0-dev)
  • pkg-config
  • Python高于2.6 Numpy高于1.5 附带 (Python-dev Python-Numpy)
  • ffmpeg或者libav开发包:libavcodec-dev,libavformat-dev,libswscale-dev
  • [可选]libtbb2 libtbb2-dev 
  • [可选]libdc1394 2.x
  • [可选]libjpeg-dev,libpng-dev,libtiff-dev,libjasper-dev,libdc1394-22-dev
  • [可选]CUDA工具链 高于6.5版本
    [compiler] sudo apt-get install build-essential
    [required] sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev
    [optional] sudo apt-get install python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev

    使用CMake从源码编译OpenCV

        1.创建临时目录用于存储cmake生成的Makefile,项目文件,目标文件,二进制文件等文件目录
    cd opencv
    mkdir build
    cd build

     2.配置opencv 使用cmake <可选参数> <opencv源码路径>

    cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local ..
    cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/opt/opencv -D BUILD_DOCS=on -D BUILD_EXAMPLES=on .. 

     3.部分参数说明

    • build type:CMAKE_BUILD_TYPE=Release/Debug
    • BUILD_DOCS:编译文档
    • BUILD_EXAMPLES:编译实例    
    • OPENCV_EXTRA_MODULES_PATH:从opencv_contrib模块构建
     4. 构建
    make -j7 

    5.安装

    sudo make install

    6.运行测试demo

         git clone https://github.com/opencv/opencv_extra.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值