OpenCV入门学习笔记

OpenCV入门学习笔记

一.简介

  • OpenCV(Open Source Computer Vision),开源计算机视觉库
  • 提供了不少函数,实现了不少计算机视觉算法,算法从最基本的滤波到高级的物体检测皆有涵盖
  • 学习OpenCV所须要的基本知识
    • C/C++编程基础(编程能力)
    • 了解算法原理(理论基础知识)
  • 提高理论基础知识,所要了解的课程
    • 数字图像处理
    • 计算机视觉
    • 模式识别
  • OpenCV知识一个算法库,咱们并不须要彻底精通算法原理以后才去使用,只须要了解它的功能,就能够动手操做了

二.预备知识

1.编程的流程

  • 编辑(edit)
    • 编辑即编写代码,是编程的第一步
    • 可使用任意编辑器编写代码,可是为了方便,推荐使用功能丰富的编辑器
  • 编译(compile)
    • 编译是将某种编程语言写成的源代码,转换成目标文件
    • 目标文件包含
      • 机器代码(能够直接被计算机CPU执行)
      • 代码在运行时使用的数据
    • 编译器(compiler)是实现这一目的的软件
      • Windows下的有cl.exe
      • Linux下有gcc或g++
  • 链接(link)
    • 链接是将多个目标文件,以及库文件生成可执行文件(或静态库/动态库)的过程
    • 链接器(linker)是实现这一目的的软件
    • 经常使用的链接器有
      • Windows下的link.exe
      • Linux下的ld等
  • 运行(run)
  • Visual C++(IDE)
    • 集成开发环境(Integrated Development Environment)能够帮助开发者对项目进行管理
  • 头文件
    • 在存在多个源文件的状况下,一个源文件中的函数要想调用另一个源文件中的函数
    • 在编译阶段,因为编译器是对单个文件进行编译,编译器也不知道是否存在那个函数能够调用,以及调用的方式是否正确,所以就须要借助头文件中的函数声明来判断
  • 库文件
    • 库文件中包含一系列的子程序,库文件是二进制的,在库文件中是看不到原始的源代码的
    • 库文件和可执行文件的区别是,库不是独立程序,是向其余程序提供服务的代码
    • 使用库文件的好处
      • 对源代码进行保密
      • 减小重复编译的时间,加强程序的模块化
    • 将库文件链接到程序中的两种方式
      • 静态连接库
      • 动态连接库

2.OpenCV是什么

  • OpenCV其实就是一堆C和C++语言的源代码文件,这些源代码文件中实现了许多经常使用的计算机视觉算法

3.其余知识

  • 命令行参数
    • int main(int argc, char** argv)
    • int main(int argc, char* argv[])
      • argc表示命令行输入参数的个数(以空白符分隔)
      • argv存储了全部的命令行参数
    • 例如:hello.exe Jiaqi Wang
      • argc的值是3
      • argv[0]是"hello.exe"
      • argv[1]是"Jiaqi"
      • argv[2]是"Wang"
  • 常见编译错误
    • 出现编译错误后,须要作的第一件事就是阅读出错信息
    • 出错信息虽然看似凌乱,可是可以提供不少有价值的信息,帮助开发者解决问题
  • 常见编译错误1:找不到头文件
    • 找不到头文件通常有两个缘由
      • 头文件的文件名拼写错误
      • 未将头文件所在的路径添加到开发环境中
    • 若是文件名拼写正确,编译器仍是找不到头文件,则须要将头文件所在路径添加到相应的变量中
  • 常见编译错误2:拼写错误
    • 在编程中,拼写错误也是一类常见错误
    • 若是检查后发现不是拼写错误,可能的缘由是声明函数的头文件未使用include语句包含到源文件中
    • 若是源代码不符合语法规则,也会形成编译错误
  • 常见连接错误
    • 若是代码符合语法规则,则会经过编译过程
    • 编译完全部源代码以后,下一步是链接目标文件,以造成可执行文件
    • 编译经过之后,在链接时出错,多是没有导入依赖的库文件,故找不到对应的方法实现
    • 须要将依赖的库文件添加到项目设置中
  • 运行时错误
    • 通过编译和链接的过程,生成了可执行文件,在运行这个可执行文件所产生的错误是运行时错误
    • 比较常见的运行时错误是内存错误
    • 在程序编写中,对于数组和指针等,要特别的当心
    • 由于对于空指针以及数组越界等问题,编译器没法在编译时给出错误提示
    • 这类错误一旦在运行时发生,排除起来很是困难

三.OpenCV介绍

  • OpenCV的全称是Open Source Computer Vision Library,是一个开放源代码的计算机视觉库
  • OpenCV最初由英特尔公司发起并开发,以BSD许可证受权发行,能够在商业和研究领域中无偿使用,如今美国Willow Garage为OpenCV提供主要的支持
  • OpenCV可用于开发实时的图像处理,计算机视觉以及模式识别程序,目前在工业界以及科研领域普遍采用

1.OpenCV的来源

  • 诞生于Intel
    • 初衷:提供一个计算机视觉库,使之充分发掘CPU的计算能力,促进Intel产品的销售
    • 最初开发是由Intel在俄罗斯的团队实现
  • 2008年Willow Garage开始大力支持OpenCV
    • Willow Garage是一家机器人公司
    • 致力于为我的机器人开发开放的硬件平台和软件
    • 现已开发PR2机器人,并支持ROS/OpenCV/PCL等软件
    • ROS(Robot Operating System)是用于机器人的操做系统,是一个开发源代码的软件,OpenCV做为ROS的视觉模块嵌入

2.OpenCV的协议

  • OpenCV采用BSD协议,这是一个很是宽松的协议
    • 用户能够修改OpenCV的源代码
    • 能够将OpenCV嵌入到本身的软件中
    • 能够将包含OpenCV的软件销售,能够用于商业产品
    • 也能够用于科研领域
    • BSD协议并不具备"传染性"
      • 若是在软件中使用OpenCV,你不须要公开代码
      • 你能够对OpenCV作任何操做
    • 协议对用户的惟一约束就是
      • 要在软件的文档或者说明中注明使用OpenCV,并附上OpenCV的协议
  • OpenCV的协议保证了计算机视觉技术快速的传播,让更多的人从OpenCV受益

四.图像的基本操做

1.图像的表示

  • 基本知识
    • 人眼看到的图像,在计算机看来,只是一堆亮度各异的点
    • 一副尺寸为M × N的图像,能够用一个M × N的矩阵来表示
    • 矩阵元素的值表示这个位置上的像素的亮度
    • 通常来讲,像素值越大表示该点越亮
  • 灰度图和彩色图像
    • 通常来讲,灰度图用2维矩阵表示(M × N)
    • 彩色(多通道)图像用3维矩阵表示(M × N × 3)
    • 对于图像显示来讲,目前大部分设备都是用无符号8位整数(CV_8U)表示像素亮度
    • 图像数据在计算机内存中的存储顺序为以图像最左上点(也可能最左下点)开始
    • 若是是多通道图像,好比RGB图像,则每一个像素用三个字节表示
    • 在OpenCV中,RGB图像的通道顺序为BGR

2.Mat类

  • 早期的OpenCV
    • 使用IpLImage和CvMat数据结构来表示图像
    • IpLImage和CvMat都是C语言的结构
    • 使用这两个结构的问题是内训须要手动管理
      • 开发者必须清楚什么时候须要申请内存,什么时候须要释放内存
      • 为开发者带来了必定的负担,开发者应该将更多精力用于算法设计
      • 所以,新版本的OpenCV中引入了Mat类
  • 新版的OpenCV
    • 新加入的Mat类可以自动管理内存
    • 优势:
      • 不须要花费大量精力在内存管理上
      • 代码会变得很简洁,代码行数会变少
    • 缺点:
      • 使用C++接口在一些嵌入式开发系统中可能只支持C语言
      • 若是开发平台支持C++,彻底不必再用IpLImage和CvMat
    • 在新版本的OpenCV中,开发者依然可使用IpLImage和CvMat
    • 可是一些新增的函数只提供了Mat接口
class CV_EXPORTS Mat
{
   public:
    //一系列函数 ...
    /* flag参数中包含许多关于矩阵的信息,如:
        -Mat 的标识
        -数据是否连续
        -深度
        -通道数目
    */
    int flags;
    //矩阵的维数,取值应该大于或等于 2
    int dims;
    //矩阵的行数和列数,若是矩阵超过 2 维,这两个变量的值都为-1 
    int rows, cols;
    //指向数据的指针
    uchar* data;
    //指向引用计数的指针
    //若是数据是由用户分配的,则为 NULL
    int* refcount;
    //其余成员变量和成员函数
    ...
};

3.建立Mat对象

  • Mat是一个很是优秀的图像类,它同时也是一个通用的矩阵类
  • 能够用来建立和操做多维矩阵,有多种方法建立一个Mat对象
    • 构造函数方法
      • Mat M(3,2, CV_8UC3, Scalar(0,0,255));
      • 建立一个行数(高度)为3,列数(宽度)为2的图像
      • 图像元素是8位无符号整数类型,且有三个通道
      • 图像的全部像素值被初始化为(0,0,255)
      • 因为OpenCV中默认的颜色顺序为BGR,所以这是一个全红色的图像
      • Mat从新定义了<<操做符,使用这个操做符,能够方便地输出全部像素值,而不须要使用for循环逐个像素输出
    • 经常使用的构造方法有("::"表示做用域和所属关系)
      • Mat::Mat()
        • 无参数构造方法
      • Mat::Mat(int rows, int cols, int type)
        • 建立行数为rows,列数为cols,类型为type的图像
      • Mat::Mat(Size size, int type)
        • 建立大小为size,类型为type的图像
      • Mat::Mat(int rows, int cols, int type, const Scalar& s)
        • 建立行数为rows,列数为cols,类型为type的图像,并将全部元素初始化为值s
      • Mat::Mat(Size size, int type, const Scalar& s)
        • 建立大小为size,类型为type的图像,并将全部元素初始化为值s
      • Mat::Mat(const Mat& m)
        • 将m赋值给新建立的对象,此处不会对图像数据进行复制,m和新对象公用图像数据
      • Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
        • 建立行数为rows,列数为cols,类型为type的图像,此构造函数不建立图像数据所需内存,而是直接使用data所指内存,图像的行步长由step指定
      • Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)
        • 建立大小为size,类型为type的图像,此构造函数不建立图像数据所需内存,而是直接使用data所指内存,图像的行步长由step指定
      • Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange)
        • 建立新图像为m的一部分,具体的范围由rowRange和colRange指定,此构造函数也不进行图像数据的复制操做,新图像与m公用图像数据
      • Mat::Mat(const Mat& m, const Rect& roi)
        • 建立的新图像为m的一部分,具体的范围由roi指定,此构造函数也不进行图像数据的复制操做,新图像与m公用图像数据
    • 这些构造函数中,不少都涉及到类型type,type能够是
      • CV_8UC1
        • 8U表示8位无符号整数
      • CV_16SC1
        • 16S表示16位有符号整数
      • CV_64FC4
        • 64F表示64位浮点数(即double类型)
      • C后面表示通道数
        • 例如C1表示一个通道的图像,C4表示4个通道的图像,以此类推
        • 若是须要更多的通道数,须要使用宏CV_8UC(n)

4.使用create()函数建立对象

  • 除了在构造函数中能够建立图像,也可使用Mat类的create()函数建立图像
  • 若是create()函数指定的参数与图像以前的参数相同,则不进行是指的内存申请操做
  • 若是参数不一样,则减小原始数据内存的索引,并从新申请内存
  • 须要注意的是,使用create()函数没法设置图像像素的初始值

5.Matlab风格的建立对象方法

  • OpenCV2中提供了Matlab风格的函数
  • 如zeros(),ones(),eyes()
  • 这种方法使得代码很是简洁,使用起来也很是方便
  • 使用这些函数须要指定图像的大小和类型
    • Mat Z = Mat::zeros(2,3, CV_8UC1);
    • Mat O = Mat::ones(2, 3, CV_32F);
    • Mat E = Mat::eye(2, 3, CV_64F);
    • 该代码中,有些type参数如CV_32F未注明通道数目,这种状况下它表示单通道

6.矩阵的基本元素表达

  • 单通道图像
    • 对于单通道图像,其元素类型通常为8U(即8位无符号整数)
    • 固然也能够是16S/32F等,这些类型能够直接用uchar/short/float等C/C++语言中的基本数据类型表达
  • 多通道图像
    • 对于多通道图像,如RGB彩色图像,须要用三个通道来表示
    • 在这种状况下,若是依然将图像视做一个二维矩阵,那么矩阵的元素再也不是基本的数据类型
  • OpenCV中有模板类Vec,能够表示一个向量
  • OpenCV中使用Vec类预约义了一些小向量,能够将之用于矩阵元素的表达
    • typedef Vec<uchar, 2> Vec2b;
    • typedef Vec<short, 2> Vec2s;
    • typedef Vec<int, 2> Vec2i;
    • typedef Vec<float, 2> Vec2f;
    • typedef Vec<double, 2> Vec2d;
  • 例如8U类型的RGB彩色图像可使用Vec3b,三通道float类型的矩阵可使用Vec3f
  • 对于Vec对象,可使用[]符号如操做数组般读写其元素,如:
    • Vec3b color; // color变量描述一种RGB颜色
    • color[0] = 255; // B份量
    • color[1] = 0; // G份量
    • color[2] = 0; //R份量

7.像素值的读写

  • 不少时候,咱们须要读取某个像素值,或者设置某个像素值
  • 在更多的时候,咱们须要对整个图像里的全部像素进行遍历
  • OpenCV提供了多种方法来实现图像的遍历
    • at()函数
      • 函数at()来实现读取矩阵中的某个像素,或者对某个像素进行赋值操做
      • uchar value = grayim.at (i,j);//读出第i行第j列像素值
      • grayim.at (i,j)=128; //将第i行第j列像素值设置为128
      • 注意:若是要遍历图像,并不推荐使用at()函数
      • 使用这个函数的有优势是代码的可读性高,可是效率并非很高
    • 使用迭代器
      • 若是你熟悉C++的STL库,那必定了解迭代器(iterator)的使用
      • 迭代器能够方便地遍历全部元素
      • Mat也增长了迭代器的支持,通常与矩阵元素的遍历
      • 可是因为使用了迭代器,而不是使用行数和列数来遍历,因此就没有了i和j变量
    • 使用数据指针
      • 使用IpLImage结构的时候,咱们会常用数据指针来直接操做像素
      • 经过指针操做来访问像素是很是高效的,可是务必要十分当心
      • C/C++中的指针操做是不进行类型以及越界检查的,若是指针访问出错,程序运行时有时候可能看上去一切正常,有时候却忽然弹出段错误(segment fault)
      • 对于不熟悉指针的编程者,不推荐使用指针操做
      • 若是你很是注重程序的运行速度,那么遍历像素时,建议使用指针

8.选取图像局部区域

  • Mat类提供了不少方便的方法来选择图像的局部区域
  • 使用这些方法时须要注意,这些方法并不进行内存的复制操做
  • 若是将局部区域赋值给新的Mat对象,新对象与原始对象共用相同的数据区域,不从新申请内存,所以这些方法的执行速度都比较快
    • 单行或单列的选择
      • 提取矩阵的一行或者一列可使用函数row()或col()
      • 函数的声明以下
        • Mat Mat::row(int i) const
        • Mat Mat::col(int j) const
    • 用Range选择多行或多列
      • Range是OpenCV中新增的类,该类有两个关键变量start和end
      • Range对象能够用来表示矩阵的多个连续的行或者多个连续的列
      • 其表示范围从start到end,包含start,但不包含end
      • Range类还提供了一个静态方法all(),这个方法的做用如同Matlab中的":",表示全部的行或者全部的列
      • // 建立一个单位阵
      • Mat A = Mat::eye(10, 10, CV_32S);
      • // 提取第 1 到 3 列(不包括 3)
      • Mat B = A(Range::all(), Range(1, 3));
      • // 提取 B 的第 5 至 9 行(不包括 9)
      • // 其实等价于 C = A(Range(5, 9), Range(1, 3)) Mat
      • C = B(Range(5, 9), Range::all());
    • 感兴趣区域
      • 从图像中提取感兴趣区域(Region of interest)有两种方法
        • 使用构造函数
          • // 建立宽度为 320,高度为 240 的 3 通道图像
          • Mat img(Size(320,240),CV_8UC3);
          • // roi 是表示 img 中 Rect(10,10,100,100)区域的对象
          • Mat roi(img, Rect(10,10,100,100));
        • 使用括号运算符
          • Mat roi2 = img(Rect(10,10,100,100));
        • 也可使用Range对象来定义感兴趣区域
          • // 使用括号运算符
          • Mat roi3 = img(Range(10,100),Range(10,100));
          • // 使用构造函数
          • Mat roi4(img, Range(10,100),Range(10,100));
      • 取对角线元素
        • 矩阵的对角线元素可使用Mat类的diag()函数获取
        • 该函数的定义以下
          • Mat Mat::diag(int d) const
          • 当参数d=0时,表示取主对角线
          • 当参数d>0时,表示取主对角线下方的次对角线
          • 当参数d=1时,表示取主对角线下方,且紧贴主对角线的元素
          • 当参数d<0时,表示取主对角线上方的次对角线
        • 如同row()和col()函数,diag()函数也不进行内存复制操做,其复杂度也是O(1)

9.Mat表达式

  • 利用C++中的运算符重载,OpenCV2中引入了Mat运算表达式
  • 下面给出Mat表达式所支持的运算(下面列表中使用A和B表示Mat类型的对象,使用s表示Scalar对象,alpha表示double值)
    • 加法,减法,取负:A+B,A-B,A+s,A-s,s+A,s-A,-A
    • 缩放取值范围:A*alpha
    • 矩阵对应元素的乘法和除法: A.mul(B),A/B,alpha/A
    • 矩阵乘法:A*B (注意此处是矩阵乘法,而不是矩阵对应元素相乘)
    • 矩阵转置:A.t()
    • 矩阵求逆和求伪逆:A.inv()
    • 矩阵比较运算:A cmpop B,A cmpop alpha,alpha cmpop A。此处 cmpop 能够是>,>=,==,!=,<=,<。若是条件成立,则结果矩阵(8U 类型矩 阵)的对应元素被置为 255;不然置 0。
    • 矩阵位逻辑运算:A logicop B,A logicop s,s logicop A,~A,此处 logicop 能够是&,|和^。
    • 矩阵对应元素的最大值和最小值:min(A, B),min(A, alpha),max(A, B), max(A, alpha)。
    • 矩阵中元素的绝对值:abs(A)
    • 叉积和点积:A.cross(B),A.dot(B)

10.Mat_类

  • Mat_类是对Mat类的一个包装
    • 在包装中,只定义了几个方法,没有定义新的属性
    • 若是使用Mat_类,能够在变量声明时肯定元素的类型,访问元素时再也不须要指定元素类型,既能使代码简洁,又能减小出错的可能性

11.Mat类的内存管理

  • 虽然使用Mat类时,内存管理变得简单,可是若是清楚了解Mat类的内存管理,会更清楚一些函数到底操做了哪些数据
  • Mat是一个类,由两个数据部分组成
    • 矩阵头(包含矩阵尺寸/存储方法/存储地址等信息)
    • 一个指向存储全部像素值的矩阵的指针
  • 矩阵头的尺寸是常数值,但矩阵自己的尺寸会依图像的不一样而不一样,一般比矩阵头的尺寸大数个数量级
  • 复制矩阵数据每每花费较多时间,所以除非有必要,不要复制大的矩阵
  • 为了解决矩阵数据的传递,OpenCV使用了引用计数
    • 思路:让每一个Mat对象有本身的矩阵头信息,但多个Mat对象能够共享同一个矩阵数据
    • 让矩阵指针指向同一地址而实现这一目的
    • 不少函数以及不少操做(如函数参数传值)只复制矩阵头信息,而不复制矩阵数据
    • 有不少种方法建立Mat类,若是Mat类本身申请数据空间,那么该类会多申请4个字节(int类型),多出的4个字节存储数据被引用的次数
    • 引用次数存储于数据空间的后面,refcount指向这个位置
    • 当计数等于0时,则释放该空间

12.输出

  • Mat类重载了<<操做符,能够方便的使用流操做来输出矩阵的内容
  • 默认状况下,输出的格式是相似Matlab中矩阵的输出格式
  • 除了默认格式,Mat也支持其余的输出格式
    • Python格式
    • csv格式(以逗号分隔)
    • numpy格式
    • C语言格式
  • 除了Mat对象可使用<<符号输出,其余的不少类型也支持<<输出
    • 二维点
    • 三维点

13.Mat与ImlImage和CvMat的转换

  • 虽然OpenCV2引入了方便的Mat类,出于兼容性考虑,OpenCV依然是支持C语言接口的ImlImage和CvMat结构
  • 若是要与之前的代码兼容,将会涉及到Mat与ImlImage和CvMat的转换
    • Mat转为ImlImage和CvMat格式
      • IplImage iplimg = img; //转为IplImage结构
      • CvMat cvimg = img; //转为CvMat结构
      • 注意:类型转换后,ImlImage和CvMat与Mat公用同一矩阵数据,而ImlImage和CvMat没有引用计数功能,若是img中数据被释放,ImlImage和CvMat也就失去了数据,所以要牢记不可将Mat对象提早释放
    • ImlImage和CvMat格式转为Mat
      • Mat 类有两个构造函数,能够实现 IplImage 和 CvMat 到 Mat 的转换。
      • 这两 个函数都有一个参数 copyData。
        • 若是 copyData 的值是 false,那么 Mat 将与 IplImage 或 CvMat 共用同一矩阵数据;
        • 若是值是 true,Mat 会新申请内存,而后将 IplImage 或 CvMat 的数据复制到 Mat 的数据区。
      • 若是共用数据,Mat 也将不会使用引用计数来管理内存,须要开发者本身来管理。
      • 建议作此转换是将参数置为 true,这样内存管理变得简单。
      • Mat::Mat(const CvMat* m, bool copyData=false)
      • Mat::Mat(const IplImage* img, bool copyData=false)

五.数据获取与存储

1.读写图像文件

  • 将图像文件读入内存,可使用imread()函数
  • 将Mat对象以图像文件格式写入内存,可使用imwrite()函数
  • 读图像文件
    • imread()函数返回的是Mat对象
      • 若是读取文件失败,则会返回一个空矩阵,即Mat::data的值是NULL
      • 执行imread()以后,须要检查文件是否成功读入,可使用Mat::empty()函数进行检查
    • imread()函数的声明以下
      • Mat imread(const string& filename, int flags=1 )
      • filename是被读取或保存的图像文件名
      • 在imread()函数中,flag参数值有三种状况
        • flag>0,该函数返回3通道图像,若是磁盘上的图像文件是单通道的灰度图像,则会被强制转为3通道
        • flag=0,该函数返回单通道图像,若是磁盘的图像文件是多通道图像,则会被强制转为单通道
        • flag<0,该函数不会对图像进行通道转换
    • imread()函数支持多种文件格式,且该函数是根据图像文件的内容来肯定文件格式,而不是根据文件的扩展名来肯定
    • 所支持的格式文件以下:
      • Windows 位图文件 - BMP, DIB;
      • JPEG 文件 - JPEG, JPG, JPE;
      • 便携式网络图片 - PNG;
      • 便携式图像格式 - PBM,PGM,PPM;
      • Sun rasters - SR,RAS;
      • TIFF 文件 - TIFF,TIF;
      • TIFF 文件 - TIFF,TIF;
      • JPEG 2000 图片- jp2。
    • 所安装的OpenCV并不必定能支持上述全部格式,文件格式的支持须要特定的库,只有在编译OpenCV添加了响应的文件格式库,才可支持其格式
  • 写图像文件
    • 将图像写入文件,可使用imwrite()函数,该函数的声明以下:
      • bool imwrite(const string& filename, InputArray image, const vector & params=vector ())
    • 文件的格式由filename参数指定的文件扩展名肯定
      • 推荐使用PNG文件格式
      • BMP格式是无损格式,可是通常不进行压缩,文件尺寸很是大
      • JPEG格式的文件较小,可是JPEG是有损压缩,会丢失一些信息
      • PNG是无损压缩格式,推荐使用
    • imwrite()函数的第三个参数params能够指定文件格式的一些细节信息,这个参数里面的数值是跟文件格式相关的:
      • JPEG:表示图像的质量,取值范围从0到100,数值越大表示图像质量越高,固然文件也越大,默认值是95
      • PNG:表示压缩级别,取值范围是从0到9,数值越大表示文件越小,可是压缩花费的时间也越长,默认值是3
      • PPM/PGM/PBM:表示文件是以二进制仍是纯文本方式存储,取值为0或1,若是取值为1,则表示以二进制方式存储,默认值是1
    • 并非全部Mat对象均可以村委图像文件,目前支持的格式只有8U类型的单通道和3通道(颜色顺序为BGR)矩阵
    • 若是须要保存16U格式图像,只能使用PNG/JPEG2000/TIFF格式
    • 若是但愿将其余格式的矩阵保存为图像文件,能够现用Mat::convertTo()函数或者cvtColor()函数将矩阵转为能够保存的格式
    • 另外须要注意的是,在保存文件时,若是文件已经存在,imwrite()函数不会进行提醒,将直接覆盖掉之前的文件

2.读写视频

  • 在介绍OpenCV读写视频以前,先介绍一下编解码器(codec)
    • 若是是图像文件,咱们能够根据文件扩展名得知图像的格式,可是此经验不能推广到视频文件中
    • 视频的格式主要由压缩算法决定,压缩算法称之为编码器(coder),解压缩算法称之为解码器(decoder),编解码算法能够统称为编解码器(codec)
    • 视频文件能读或写,关键看是否有相应的编解码器,编解码器的种类很是多,经常使用的有MJPG/XVID/DIVX等,完整列表请参考FOURCC网站,所以视频文件的扩展名每每只能表示这是一个视频文件
    • OpenCV2中提供了两个类来实现视频的读写
      • 读视频的类是VideoCapture
      • 写视频的类是VideoWriter
  • 读视频
    • VideoCapture既能够从视频文件读取图像,也能够从摄像头读取图像
    • 可使用该类的构造函数打开视频文件或者摄像头
    • 若是VideoCapture对象已经建立,也可使用VideoCapture::open()打开,VideoCapture::open()函数会自动调用VideoCapture::release()函数,先释放已经打开的视频,而后再打开新视频
    • 若是要读一帧,可使用VideoCapture::read()函数
      • VideoCapture类重载了>>操做符,实现了读视频帧的功能
  • 写视频
    • 使用OpenCV建立视频也很是简单,与读视频不一样的是,须要在建立视频时设置一系列参数,包括:
      • 文件名
      • 编解码器:编解码器使用四个字符表示
        • CV_FOURCC('M','J','P','G')
        • CV_FOURCC('X','V','I','D')
        • CV_FOURCC('D','I','V','X')
        • 若是使用某种编解码器没法建立视频文件,请尝试其余的编解码器
      • 帧率
      • 宽度
      • 高度
    • 将图像写入视频可使用VideoWriter:write()函数,VideoWrite类中也重载了<<操做符,使用起来很是方便
    • 另外须要注意:待写入的图像尺寸必须与建立视频时指定的尺寸一致
相关文章
相关标签/搜索