【小白】openCV入门杂记(2)---认识openCV

2018.09.03

【小白】openCV入门杂记(2)---认识openCV

  1. 什么 是 openCV

  • OpenCV于1999年由Intel建立,如今由Willow Garage提供支持。 OpenCV于1999年由Intel建立,如今由Willow Garage提供支持。

  • 最新版本: 2017年8月3日,发布OpenCV 3.3版(最重要的更新是把DNN模块从contrib里面提到主仓库)

  •                    2018年7月4日,OpenCV3.4.2版本发布。

     【小白】openCV入门杂记(2)---认识openCV_第1张图片

PS:BSD许可

   BSD许可证原先是用在加州大学柏克利分校发表的各个4.4BSD/4.4BSD-Lite版本上面(BSD是Berkly Software Distribution的简写)的,后来也就逐渐沿用下来。1979年加州大学伯克利分校发布了BSD Unix,被称为开放源代码的先驱,BSD许可证就是随着BSD Unix发展起来的。BSD许可证现在被Apache和BSD操作系统等开源软件所采纳。

 

【小白】openCV入门杂记(2)---认识openCV_第2张图片

 

2018.09.03

 

【以下内容从https://www.cnblogs.com/coderwjq/p/6264607.html OpenCV入门学习笔记》中提取,侵删】

 

2.图像的基本操作

  • 图像的表示

    • 对于图像显示来说,目前大部分设备都是用无符号8位整数(CV_8U)表示像素亮度

 

  • 在OpenCV中,RGB图像的通道顺序为BGR

  • Mat类

  • 旧版本使用IpLImage和CvMat数据结构来表示图像

  • 新加入的Mat类能够自动管理内存

   PS:IPLImage

【小白】openCV入门杂记(2)---认识openCV_第3张图片

3.创建Mat对象

可以用来创建和操作多维矩阵,有多种方法创建一个Mat对象

  • 构造函数方法

    • Mat M(3,2, CV_8UC3, Scalar(0,0,255));

    • 创建一个行数(高度)为3,列数(宽度)为2的图像

    • 图像元素是8位无符号整数类型,且有三个通道

    • 图像的所有像素值被初始化为(0,0,255)

    • 由于OpenCV中默认的颜色顺序为BGR,因此这是一个全红色的图像

    • Mat重新定义了<<操作符,使用这个操作符,可以方便地输出所有像素值,而不需要使用for循环逐个像素输出

    •  

    • 这些构造函数中,很多都涉及到类型type,type可以是

      • CV_8UC1

        • 8U表示8位无符号整数

      • CV_16SC1

        • 16S表示16位有符号整数

      • CV_64FC4

        • 64F表示64位浮点数(即double类型)

      • C后面表示通道数

        • 例如C1表示一个通道的图像,C4表示4个通道的图像,以此类推

        • 如果需要更多的通道数,需要使用宏CV_8UC(n)

4.像素值的读写

  • 很多时候,我们需要读取某个像素值,或者设置某个像素值

  • 在更多的时候,我们需要对整个图像里的所有像素进行遍历

  • OpenCV提供了多种方法来实现图像的遍历

    • at()函数

      • 函数at()来实现读取矩阵中的某个像素,或者对某个像素进行赋值操作

      • uchar value = grayim.at

      • grayim.at

      • 注意:如果要遍历图像,并不推荐使用at()函数

      • 使用这个函数的有优点是代码的可读性高,但是效率并不是很高

    • 使用迭代器

      • 如果你熟悉C++的STL库,那一定了解迭代器(iterator)的使用

      • 迭代器可以方便地遍历所有元素

      • Mat也增加了迭代器的支持,一般与矩阵元素的遍历

      • 但是由于使用了迭代器,而不是使用行数和列数来遍历,所以就没有了i和j变量

    • 使用数据指针

      • 使用IpLImage结构的时候,我们会经常使用数据指针来直接操作像素

      • 通过指针操作来访问像素是非常高效的,但是务必要十分小心

      • C/C++中的指针操作是不进行类型以及越界检查的,如果指针访问出错,程序运行时有时候可能看上去一切正常,有时候却突然弹出段错误(segment fault)

      • 对于不熟悉指针的编程者,不推荐使用指针操作

      • 如果你非常注重程序的运行速度,那么遍历像素时,建议使用指针

5、选取图像局部区域

  • Mat类提供了很多方便的方法来选择图像的局部区域

  • 使用这些方法时需要注意,这些方法并不进行内存的复制操作

  • 如果将局部区域赋值给新的Mat对象,新对象与原始对象共用相同的数据区域,不重新申请内存,因此这些方法的执行速度都比较快

                             【小白】openCV入门杂记(2)---认识openCV_第4张图片

    • 单行或单列的选择

      • 提取矩阵的一行或者一列可以使用函数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时,表示取主对角线上方的次对角线

            • PS:

      • 如同row()和col()函数,diag()函数也不进行内存复制操作,其复杂度也是O(1)

 

6.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)

 

7.Mat类的内存管理

  • Mat是一个类,由两个数据部分组成

    • 矩阵头(包含矩阵尺寸/存储方法/存储地址等信息)

    • 一个指向存储所有像素值的矩阵的指针

  • 为了解决矩阵数据的传递,OpenCV使用了引用计数

    • 思路:让每个Mat对象有自己的矩阵头信息,但多个Mat对象可以共享同一个矩阵数据

    • 让矩阵指针指向同一地址而实现这一目的(应该是元素相同的矩阵,比如矩阵的复制传递)

    • 很多函数以及很多操作(如函数参数传值)只复制矩阵头信息,而不复制矩阵数据

    • 有很多种方法创建Mat类,如果Mat类自己申请数据空间,那么该类会多申请4个字节(int类型),多出的4个字节存储数据被引用的次数

    • 引用次数存储于数据空间的后面,refcount指向这个位置

    • 当计数等于0时,则释放该空间

 

8.Mat与ImlImage和CvMat的转换

  • 如果要与以前的代码兼容,将会涉及到Mat与ImlImage和CvMat的转换

    • Mat转为ImlImage和CvMat格式(qiangzhi)

      • 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)

 

-----------------------------------------------------图片及视频的读写-----------------------------------------------------------

 

9.读写图像文件

  • 将图像文件读入内存,可以使用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

    • 文件的格式由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()函数不会进行提醒,将直接覆盖掉以前的文件

10.读写视频

  • 在介绍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)