7.Mat类构造与赋值

发布时间 2023-04-07 17:16:11作者: 夏蝉沐雪

一、Mat类的构造

1、利用默认构造函数

  通过代码清单2-4,利用默认构造函数构造了一个Mat类,这种构造方式不需要输入任何的参数,在后续给变量赋值的时候会自动判断矩阵的类型与大小,实现灵活的存储,常用于存储读取的图像数据和某个函数运算输出结果。

代码清单2-4 默认构造函数使用方式

cv::Mat::Mat();

2、根据输入矩阵的尺寸和类型构造

代码清单2-5 利用矩阵尺寸和类型参数构造Mat类

cv::Mat::Mat( int  rows,

           int  cols,

           int  type

          )
  • rows:构造矩阵的行数

  • cols:矩阵的列数

  • type:矩阵中存储的数据类型。此处除了CV_8UC1、CV_64FC4等从1到4通道以外,还提供了更多通道的参数,通过CV_8UC(n)中的n来构建多通道矩阵,其中n最大可以取到512. 

这种构造方法我们前文也见过,通过输入矩阵的行、列以及存储数据类型实现构造。这种定义方式清晰、直观、易于阅读,常用在明确需要存储数据尺寸和数据类型的情况下,例如相机的内参矩阵、物体的旋转矩阵等。利用输入矩阵尺寸和数据类型构造Mat类的方法存在一种变形,通过将行和列组成一个Size()结构进行赋值,代码清单2-6中给出了这种构造方法的原型。

代码清单2-6 用Size()结构构造Mat类

cv::Mat::Mat(Size size(), int  type)
  • size:2D数组变量尺寸,通过Size(cols, rows)进行赋值。

  • type:与代码清单2-5中的参数一致

利用这种方式构造Mat类时要格外注意,在Size()结构里矩阵的行和列的顺序与代码清单2-5中的方法相反,使用Size()时,列在前、行在后。如果不注意同样会构造成功Mat类,但是当我们需要查看某个元素时,我们并不知道行与列颠倒,就会出现数组越界的错误。使用该种方法构造函数如下:

代码清单2-7 用Size()结构构造Mat示例

cv::Mat a(Size(480, 640), CV_8UC1); //构造一个行为640,列为480的单通道矩阵

cv::Mat b(Size(480, 640), CV_32FC3); //构造一个行为640,列为480的3通道矩

3、利用已有矩阵构造

代码清单2-8 利用已有矩阵构造Mat类

cv::Mat::Mat( const Mat & m);

m:已经构建完成的Mat类矩阵数据。

这种构造方式非常简单,可以构造出与已有的Mat类变量存储内容一样的变量。注意这种构造方式只是复制了Mat类的矩阵头,矩阵指针指向的是同一个地址,因此如果通过某一个Mat类变量修改了矩阵中的数据,另一个变量中的数据也会发生改变。如果想复制两个一模一样的Mat类而彼此之间不会受影响,可以使用m=a.clone()实现。如果需要构造的矩阵尺寸比已有矩阵小,并且存储的是已有矩阵的子内容,那么可以用代码清单2-9中的方法进行构建:

代码清单2-9 构造已有Mat类的子类

cv::Mat::Mat(const Mat & m,

               const Range & rowRange,

               const Range & colRange = Range::all()

               )
  • m:已经构建完成的Mat类矩阵数据。

  • rowRange:在已有矩阵中需要截取的行数范围,是一个Range变量,例如从第2行到第5行可以表示为Range(2,5)。

  • colRange:在已有矩阵中需要截取的列数范围,是一个Range变量,例如从第2列到第5列可以表示为Range(2,5),当不输入任何值时表示所有列都会被截取。

这种方式主要用于在原图中截图使用,不过需要注意的是,通过这种方式构造的Mat类与已有Mat类享有共同的数据,即如果两个Mat类中有一个数据发生更改,另一个也会随之更改。

二、Mat类的赋值

1、构造时赋值

代码清单2-11 在构造时赋值的方法

cv::Mat::Mat(int  rows,

               int  cols,

               int  type,

               const Scalar & s

               )
  • rows:矩阵的行数

  • cols:矩阵的列数

  • type:存储数据的类型

  • s:给矩阵中每个像素赋值的参数变量,例如Scalar(0, 0, 255)。

该种方式是在构造的同时进行赋值,将每个元素想要赋予的值放入Scalar结构中即可,这里需要注意的是,用此方法会将图像中的每个元素赋值相同的数值,例如Scalar(0, 0, 255)会将每个像素的三个通道值分别赋值0,0,255。我们可以使用如下的形式构造一个已赋值的Mat类

代码清单2-12 在构造时赋值示例

cv::Mat a(2, 2, CV_8UC3, cv::Scalar(0,0,255));//创建一个3通道矩阵,每个像素都是0,0,255

cv::Mat b(2, 2, CV_8UC2, cv::Scalar(0,255));//创建一个2通道矩阵,每个像素都是0,255

cv::Mat c(2, 2, CV_8UC1, cv::Scalar(255)); //创建一个单通道矩阵,每个像素都是255

Scalar结构中变量的个数一定要与定义中的通道数相对应,如果Scalar结构中变量个数大于通道数,则位置大于通道数之后的数值将不会被读取,例如执行a(2, 2, CV_8UC2, Scalar(0,0,255))后,每个像素值都将是(0,0),而255不会被读取。如果Scalar结构中变量数小于通道数,则会以0补充。

2、枚举赋值法

这种赋值方式是将矩阵中所有的元素都一一枚举出,并用数据流的形式赋值给Mat类。具体赋值形式如代码清单2-13所示。

代码清单2-13 利用枚举法赋值示例

cv::Mat a = (cv::Mat_<int>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);

cv::Mat b = (cv::Mat_<double>(2, 3) << 1.0, 2.1, 3.2, 4.0, 5.1, 6.2);

 3、循环赋值

与通过枚举法赋值方法相类似,循环法赋值也是对矩阵中的每一位元素进行赋值,但是可以不在声明变量的时候进行赋值,而且可以对矩阵中的任意部分进行赋值。具体赋值形式如代码清单2-14所示。

代码清单2-14 利用枚举法赋值示例

cv::Mat c = cv::Mat_<int>(3, 3); //定义一个3*3的矩阵
for (int i = 0; i < c.rows; i++) //矩阵行数循环
{
  for (int j = 0; j < c.cols; j++) //矩阵列数循环
  {
    c.at<int>(i, j) = i+j;
  } }