经过几个月的努力,粗犷的龙果终于完成了市场上第一本OpenCV 4入门书《从零学习OpenCV 4》。 为了让合作伙伴更快地知道最新版的OpenCV 4,粗犷的龙果与出版社进行了交流,部分刊登在了公众号上,请继续关注粗犷的龙果。
实际上,在最初的OpenCV 1.0版本中,图像是使用c语言结构IplImage存储的,因此,在旧的OpenCV版本的教程中经常会看到该图像。 但是,使用IplImage类型有用户必须手动释放内存的缺点,如果存在程序结束后未释放内存的IplImage变量,则会发生内存泄漏问题。 幸运的是,随着OpenCV版本的更新,OpenCV引入了c接口,提供了Mat类来存储数据,利用自动内存管理技术很好地解决了内存自动释放的问题,不再需要变量时很快释放内存。
Mat类保存矩阵类型的数据信息,如矢量、矩阵、灰度和彩色图像。 Mat类分为矩阵标头和存储数据的矩阵指针两部分。 矩阵的标头包括矩阵的大小、存储方法、地址和引用次数等。 矩阵头的大小是常数,不会随矩阵的大小而变化。 在大多数情况下,矩阵标头的大小远远小于矩阵中数据量的大小,因此复制和传输图像时的主要开销是存储矩阵数据。 为了解决这个问题,在OpenCV中复制和传递图像时,只复制了矩阵标头和指向存储的数据的指针,因此可以在创建Mat类之前创建矩阵标头并分配数据。 其方法如代码清单2-1所示。
代码清单2-1创建mat类
cv :马特a; 制作一个叫做//A的矩阵头
a=CV :3360 im读取“测试. JPG”); //A中代入图像数据,矩阵指针指向像素数据
cv:Mat b=a; //复制矩阵头,命名为b
在此代码中,首先创建一个名为a的矩阵头,然后读取图像,将a的矩阵指针指向该图像的像素数据,最后将a的矩阵头的内容复制到b的矩阵头。 虽然a、b有各自的矩阵标头,但其矩阵指针指向同一矩阵数据,通过在任一矩阵标头中修改矩阵中的数据,另一矩阵标头所指向的数据也将发生变化。 但是,删除a变量并不意味着b变量是空的数据,只有在同时删除了这两个变量时,才会释放矩阵数据。 矩阵开头的参照次数标记为参照某个矩阵数据的次数,因此只有在矩阵数据的参照次数为0时矩阵数据才被释放。
提示
使用参照次数释放存储内容是c中常见的方法,可以避免某个变量还在参照数据时删除该数据导致程序崩溃的问题,同时可以大幅减少程序运行时消耗的内存。
接下来,让我们看看可以存储在Mat类中的数据类型。 根据公式中所示的Mat类继承图,我们发现Mat类可以存储的数据类型包括double、float、uchar、unsigned char和自定义模板,如图2-2所示。
图2-2 Mat类继承关系图
可以使用代码列表2-2声明包含指定类型的Mat类变量。
代码清单2-2声明指定类型的Mat类
cv :3360 mata=mat _双精度3,3 ); 制作用于容纳double型数据的3*3矩阵
OpenCV提出的Mat类主要用于存储图像,最大像素值决定图像的质量,因此将16位图像存储为8位无符号整数会导致严重的图像颜色失真和数据错误。 另一方面,由于数据长度的定义因编译器而异,因此为了避免因环境不同而导致的程序执行的问题,OpenCV根据数值变量存储位长来定义数据类型,表2-1中表示的是OpenCV中的数据类型
表2-1 OpenCV的数据类型和可能值的范围
仅数据类型是不够的,还需要定义图像数据的通道数,例如灰度图像数据为单通道,彩色图像数据为3通道或4通道的数据等。 因此,针对这种情况,OpenCV还定义了频道数标识,C1、C2、C3、C4分别表示单通道、双通道、三通道、四通道。 由于每个数据类型都有多个通道,因此将数据类型和通道数的表示形式结合起来,可以得到OpenCV中图像数据类型的完整定义。 例如,CV_8UC1表示8位单通道数据,8位灰度,CV_8UC3表示8位三通道数据,8位彩色图表。 可以在代码列表2-3中创建声明通道数和数据类型的Mat类。
代码清单2-3根据opencv数据类型创建Mat类
创建一个用于存储cv:mata640、480、CV_8UC3) /彩色图像的640*480三通道矩阵
创建cv:mata3、3、CV_8UC1 )3*3的8位无符号整数单通道矩阵
可以省略对cv : mata 3,3,CV_8U ) /单通道矩阵C1标记的创建的注意
在64位编辑器中,uchar和CV_8U都表示8位无符号整数,但两者有严格的定义,只能在Mat类中的方法中使用CV_8U。 例如,如果使用mat _ cv _ 8u 3,3 )和mata ) 3,3和uchar,系统会提示您创建错误。