【opencv学习笔记】第二篇:详解基本图像容器Mat以及常用数据结构和函数

1. Mat简介

在Opencv1代的时候,是使用lplImage 和 CvMat 数据结构来表示图像的,他们都是C语言的结构,申请的内存需要自己手动管理,特别是采用 lplImage 会直接暴露内存,如果忘记释放内存,就会造成内存泄漏。为此,OpenCV在2.0版本中引入了一个新的C++接口,利用自动内存管理给出了解决问题的新方法。使用这个方法,你不需要纠结在管理内存上,而且你的代码会变得简洁。Mat的优点如下:

  • 不需要手动申请一块内存;
  • 在不需要时不用再手动释放内存
  • 赋值运算符和拷贝构造函数只复制信息头;但可以通过函数clone()或者copyTo()来复制一幅图像的矩阵。
  • 可以通过类的封装,方便的获取到数据的相关信息。

Mat 是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是不同的维数)的指针。
【opencv学习笔记】第二篇:详解基本图像容器Mat以及常用数据结构和函数_第1张图片

class CV_EXPORTS Mat
{
public/*..很多方法..*/
/*............*/
 
int flags;/*flags指定图像的颜色空间  
            flags > 0 3通道的彩色图像
            flags = 0 灰度图像
            flags < 0 不作改变
          */
int dims;  /*矩阵的维数*/
int rows,cols; /*行和列的数量;矩阵的维数超过2维时为(-1,-1)*/
uchar *data;   /*指向数据*/
int * refcount;   /*指针的引用计数器; 阵列指向用户分配的数据时,指针为 NULL
/* 其他成员 */ 
...
};

2. Mat常用成员及含义

2.1 成员变量

Mat的常用成员变量列表如下:

变量 解释说明
rows
cols
dims 矩阵的维度,二维矩阵dims=2,三维矩阵dims=3
data 指是指向矩阵数据的uchar类指针,用*解引用后再强转为int可以读到第一个像素数据。

说明:

2.2 成员函数

Mat常用成员函数列表如下:

成员函数 解释说明
channels() 矩阵元素拥有的通道数,例如常见的彩色图像,每一个像素由RGB三部分组成,则image.channels() = 3
size() 该方法返回一个矩阵大小:size(cols, rows)。矩阵超过 2 维时返回大小为(-1,-1)。
type() 矩阵中元素的类型以及矩阵的通道个数,它是一系列的预定义的常量,其命名规则为CV_(位数)+(数据类型)+(通道数)。
depth() 矩阵中元素的一个通道的数据类型,这个值和type是相关的。例如 type为 CV_16SC2,一个2通道的16位的有符号整数。那么,depth则是CV_16S
empty() 如果数组有没有 elemens,则返回 true。
isContinuous() 判断矩阵是否是连续存储的
elemSize() 矩阵一个元素占用的字节数,例如:type是CV_16SC3,那么elemSize = 3 * 16 / 8 = 6 bytes,即每个元素占6个字节。
elemSize1() 单个元素在1个通道中的大小。elemSize1() = sizeof(数据类型) = elemSize() / channels()。
step[i] step是一个数组,定义了矩阵的布局,以二维矩阵为例,step[0]表示第一维度的单位长度占用字节数(即每行),step[1]表示第二维度的单位长度占用字节数(即每个元素)
step1(i) step1也是一个数组,但是是使用()访问的,这个数组在step[i]的基础上/elemSize1()

说明:

  • 将type的预定义值去掉通道信息就是depth值

3. cv::Mat类对象的创建方法

3.1 构造函数

	cv::Mat M1(3, 3, CV_8UC4, cv::Scalar(0, 0, 0, 255));
	std::cout << "M1 = " << std::endl << M1 << std::endl;

这里使用了构造函数对Mat进行定义,分别指定行数、列数、4通道的矩阵,每个点的颜色为(0, 0, 0, 255)。该例的输出结果为:

M1 =
[ 0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255;
0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255;
0, 0, 0, 255, 0, 0, 0, 255, 0, 0, 0, 255]

3.2 通过数组初始化矩阵维数

	int sz[2] = { 3, 3 };//矩阵维数3*3
	cv::Mat M2(2, sz, CV_8UC1, cv::Scalar::all(0));
	std::cout << "M2 = " << std::endl << M2 << std::endl;

M2的分别指定了2维、sz指定了3行3列、单通道8位无符号和全部元素都为0,输出结果如下:

M2 =
[ 0, 0, 0;
0, 0, 0;
0, 0, 0]

3.3 通过create函数来初始化

 cv::Mat M3;
 M3.create(4, 4, CV_8UC(1));
 std::cout << "M3 = " << std::endl << M3 << std::endl;

create构造了4*4的单通道8位无符号,并且矩阵中的数据位随机值。如下:

[205, 205, 205, 205;
205, 205, 205, 205;
205, 205, 205, 205;
205, 205, 205, 205]

3.4 通过opencv提供的类似于matlab的函数创建

 cv::Mat mat_eye= cv::Mat::eye(4, 4, CV_64F);//对角
 std::cout << "mat_eye= " << std::endl << mat_eye<< std::endl;

 cv::Mat mat_ones= cv::Mat::ones(4, 4, CV_64F);//全1
 std::cout << "mat_ones= " << std::endl << mat_ones<< std::endl;

 cv::Mat mat_zeros= cv::Mat::zeros(4, 4, CV_64F);//全0
 std::cout << "mat_zeros= " << std::endl << mat_zeros<< std::endl;

eye函数表示的是单位矩阵,ones为全1的矩阵,zeros表示全是0的矩阵。输出如下:

mat_eye=
[1, 0, 0, 0;
0, 1, 0, 0;
0, 0, 1, 0;
0, 0, 0, 1]
mat_ones=
[1, 1, 1, 1;
1, 1, 1, 1;
1, 1, 1, 1;
1, 1, 1, 1]
mat_zeros=
[0, 0, 0, 0;
0, 0, 0, 0;
0, 0, 0, 0;
0, 0, 0, 0]

3.5 数据自定义矩阵Mat创建

 cv::Mat M4 = (cv::Mat_<double>(3, 3) << 0, -1, 0, -1, 0, 0, 0, 0, 1);
 std::cout << "M4 = " << std::endl << M4 << std::endl;

创建M4的矩阵如下所示:

M4 =
[0, -1, 0;
-1, 0, 0;
0, 0, 1]

3.6 通过clone或者copyTo() 函数创建不同的Mat

 cv::Mat M5 = M4.row(1).clone();//拷贝第一行
 std::cout << "M5 = " << std::endl << M5 << std::endl;

Mat F = A.clone();//拷贝A矩阵

Mat G;
A.copyTo(G);//拷贝A矩阵

clone为深拷贝,输出结果如下:

M5 =
[-1, 0, 0]

除了clone和copyTo函数,类似于拷贝构造函数、赋值运算符的运算等都是通过采用引用的方式,即多个Mat对象共享同一个矩阵数据,这里使用的原理类似c++11中的共享指针,也就是说它们共享矩阵数据,而不共享矩阵头。

3.7 创建感兴趣区域(ROI)

创建感兴趣区域也是共享矩阵。

Mat D(A, Rect(10, 10, 100, 100));//使用矩阵界定
Mat E(Range:all(), Range(1, 3));//使用行和列来界定

R

3.8 为已存在的IplImage指针创建信息头

IplImage* iplImg = cvLoadImage("greatwave.jpg", 1);
Mat mtx(iplImg);

4. 像素值的存储方法

存储像素值需要指定颜色空间和数据类型,最简单的颜色空间为灰度级空间,只处理黑色和白色,对它们进行组合便可以产生不同的灰色。

RGB颜色空间是最常用的颜色空间,这归功于它也是人眼内部构成颜色的方式。有时为了表示透明颜色也会加入第4个元素alpha(A)。

颜色系统有很多,具体如下:

  • RGB是最常见的,这是因为人眼采用相似的工作机制,它也被显示设备所采用的。
  • HSV和HLS把颜色分解成色调、饱和度和亮度/明度。这是描述颜色更自然的方式,比如可以通过抛弃最后一个元素,使算法对输入图像的光照条件不敏感。
  • YCrCb在JPEG图像格式中被广泛使用。
  • CIE Lab*是一种在感知上均匀的颜色空间,它适合用来度量两个颜色之间的距离。

每个组成元素都有其定义域,定义域取决于其数据类型。如最小的数据类型是char,占一个字节,可以是有符号类型(0到255之间)或无符号类型(-127到+127之间)。尽管使用三个char可以表示1600万种颜色,但是使用float或者double则能给出更加精细的颜色分辨能力。

5. 点的表示:Point类

Point类数据结构表示了二维坐标系下的点。

Point point;
point.x = 10;
point.y = 8;

或者

Point point = Point(10, 8);

关于Point有如下定义:

typedef Point_<int> Point2i;
typedef Point2i Point;//Point类型
typedef Point_<float> Point2f;//float类型,同理还有Point3f

5. 颜色的表示:Scalar类

Scalar()表示具有4个元素的数组,在OpenCV中被大量用于传递像素值,如RGB颜色值。

Scalar(a, b, c);//红色:c 绿色:b 蓝色:a

Scalar类的源头为Scalar_类,而Scalar_类是Vec4x的一个变种,我们常用的Scalar其实就是Scalar_。这就解释了为什么很多函数的参数输入可以是Mat,也可以是Scalar。

Vec是Matx的一个派生类,Matx是一个轻量级的Mat,Matx必须在使用前规定好大小。

6. 尺寸的表示:Size类

Size的定义如下:

typedef Size_<int> Size2i;
typedef Size2i Size;

Size_是一个模板类,使用频率最高的是下面的这个构造函数:

Size_ (_Tp _width, _Tp -height);

下面给出示例:

Size(5, 5);//宽度和高度都是5

7. 矩阵的表示:Rect类

Rect的成员变量有x,y,width,height,分别为左上角点的坐标和矩阵的宽和高。常用的成员函数有:

  • Size():返回值为Size;
  • area();返回矩阵的面积;
  • contains(Point):判断点是否在矩形内;
  • inside(Rect):判断矩形是否在该矩形内;
  • tl():返回左上角点坐标;
  • br():返回右下角坐标;

求矩阵的常用操作:

Rect rect = rect1 & rect2;//交集
Rect rect = rect1 | rect2;//并集
Rect rectShift = rect + point;//矩阵平移
Rect rectScale = rect + size;//矩阵缩放

9. 颜色空间转换:cvtColor函数

关于cvtColor可以参考这篇文章。

你可能感兴趣的:(OpenCV_CPP,Mat,Point类,Scalar类,Rect类,OpenCV)