OpenCV入门学习笔记
OpenCV入门学习笔记
- 参照OpenCV中文论坛相关文档(http://www.opencv.org.cn/)
一.简介
- 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公用图像数据
- Mat::Mat()
- 这些构造函数中,很多都涉及到类型type,type可以是
- CV_8UC1
- 8U表示8位无符号整数
- CV_16SC1
- 16S表示16位有符号整数
- CV_64FC4
- 64F表示64位浮点数(即double类型)
- C后面表示通道数
- 例如C1表示一个通道的图像,C4表示4个通道的图像,以此类推
- 如果需要更多的通道数,需要使用宏CV_8UC(n)
- CV_8UC1
- 构造函数方法
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)
- 对于不熟悉指针的编程者,不推荐使用指针操作
- 如果你非常注重程序的运行速度,那么遍历像素时,建议使用指针
- at()函数
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)
- 从图像中提取感兴趣区域(Region of interest)有两种方法
- 单行或单列的选择
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)
- Mat转为ImlImage和CvMat格式
五.数据获取与存储
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添加了响应的文件格式库,才可支持其格式
- imread()函数返回的是Mat对象
- 写图像文件
- 将图像写入文件,可以使用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()函数不会进行提醒,将直接覆盖掉以前的文件
- 将图像写入文件,可以使用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类中也重载了<<操作符,使用起来非常方便
- 另外需要注意:待写入的图像尺寸必须与创建视频时指定的尺寸一致
- 使用OpenCV创建视频也非常简单,与读视频不同的是,需要在创建视频时设置一系列参数,包括:
OpenCV入门学习笔记的更多相关文章
- Android NDK开发及OpenCV初步学习笔记
https://www.jianshu.com/p/c29bb20908da Android NDK开发及OpenCV初步学习笔记 Super_圣代 关注 2017.08.19 00:55* 字数 6 ...
- OpenCV图像处理学习笔记-Day1
OpenCV图像处理学习笔记-Day1 目录 OpenCV图像处理学习笔记-Day1 第1课:图像读入.显示和保存 1. 读入图像 2. 显示图像 3. 保存图像 第2课:图像处理入门基础 1. 基本 ...
- Hadoop入门学习笔记---part4
紧接着<Hadoop入门学习笔记---part3>中的继续了解如何用java在程序中操作HDFS. 众所周知,对文件的操作无非是创建,查看,下载,删除.下面我们就开始应用java程序进行操 ...
- Hadoop入门学习笔记---part3
2015年元旦,好好学习,天天向上.良好的开端是成功的一半,任何学习都不能中断,只有坚持才会出结果.继续学习Hadoop.冰冻三尺,非一日之寒! 经过Hadoop的伪分布集群环境的搭建,基本对Hado ...
- PyQt4入门学习笔记(三)
# PyQt4入门学习笔记(三) PyQt4内的布局 布局方式是我们控制我们的GUI页面内各个控件的排放位置的.我们可以通过两种基本方式来控制: 1.绝对位置 2.layout类 绝对位置 这种方式要 ...
- PyQt4入门学习笔记(一)
PyQt4入门学习笔记(一) 一直没有找到什么好的pyqt4的教程,偶然在google上搜到一篇不错的入门文档,翻译过来,留以后再复习. 原始链接如下: http://zetcode.com/gui/ ...
- Hadoop入门学习笔记---part2
在<Hadoop入门学习笔记---part1>中感觉自己虽然总结的比较详细,但是始终感觉有点凌乱.不够系统化,不够简洁.经过自己的推敲和总结,现在在此处概括性的总结一下,认为在准备搭建ha ...
- Hadoop入门学习笔记---part1
随着毕业设计的进行,大学四年正式进入尾声.任你玩四年的大学的最后一次作业最后在激烈的选题中尘埃落定.无论选择了怎样的选题,无论最后的结果是怎样的,对于大学里面的这最后一份作业,也希望自己能够尽心尽力, ...
- Scala入门学习笔记三--数组使用
前言 本篇主要讲Scala的Array.BufferArray.List,更多教程请参考:Scala教程 本篇知识点概括 若长度固定则使用Array,若长度可能有 变化则使用ArrayBuffer 提 ...
随机推荐
- *IntelliJ IDEA使用Hibernate连接数据库
在IntelliJ IDEA中配置MySQL Database.
- 浩顺AC671指纹考勤机二次开发(demo)
关于考勤机 AC671,是新换的机器,以前的那部机器,通过网络死活连接不上,换了AC671网络连接是好用了.但是,我要吐槽 浩顺的考勤机应该是卖了很多了吧,可是自带的软件太不给力,最后分析出来的数据一 ...
- poj 3274 Gold Balanced Lineup(哈希 )
题目:http://poj.org/problem?id=3274 #include <iostream> #include<cstdio> #include<cstri ...
- BZOJ1758: [Wc2010]重建计划
题解: 这题我居然做了一星期?... 平均值的极值其实也可以算是一种分数规划,只不过分母上b[i]=1 然后我们就可以二分这个值.类似与 HNOI最小圈 如果没有 链的长度的限制的话,我们直接两遍df ...
- 关闭 VS的实时调试器
可以这样关闭: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug\Debugger HKEY_LOCAL_ ...
- 【转】跟着开涛学SpringMVC
跟着开涛学SpringMVC 第一章源代码下载 博客分类: 跟开涛学SpringMVC 跟开涛学SpringMVC 源代码请到附件中下载. 其他下载: 跟着开涛学SpringMVC 第一章源代码下载 ...
- 基于MongoDB分布式存储进行MapReduce并行查询
中介绍了如何基于Mongodb进行关系型数据的分布式存储,有了存储就会牵扯到查询.虽然用普通的方式也可以进行查询,但今天要介绍的是如何使用MONGODB中提供的MapReduce功能进行查询. ...
- ASP.NET MVC中默认Model Binder绑定Action参数为List、Dictionary等集合的实例
在实际的ASP.NET mvc项目开发中,有时会遇到一个参数是一个List.Dictionary等集合类型的情况,默认的情况ASP.NET MVC框架是怎么为我们绑定ASP.NET MVC的Actio ...
- MVC的EF编辑,不用查询直接修改
EF中会为每个 管理的 实体对象 创建一个代理包装类对象,其中会跟踪 实体对象 的状态和每个属性的状态: 一.通常使用EF更新的方式,先查询出要修改的数据,然后再修改新的值:实体对象被修改的属性 在 ...
- 《深入Java虚拟机学习笔记》- 第19章 方法的调用与返回
<深入Java虚拟机学习笔记>- 第19章 方法的调用与返回