OpenCV中 Mat 的使用方法

Mat 是 OpenCV 中的数据类型,储存矩阵形式的数据,构造 Mat 类型的方法有很多,都是通过 Mat 这个构造函数进行实现(Mat 也是构造 Mat 数据类型的函数)。


本文主要介绍 Mat 的构造方法和 Mat 类的访问以及相关的数据类型。

1 传统的函数构造

Mat matrix(10, 10, CV_8UC(1)); // 参数 10 分别表示矩阵的行数和列数,
							   // CV_8UC(1) 规定矩阵元素为单通道
							   // 8 位无符号整型.

如果想要初始化数据:

Mat matrix(10, 10, CV_8UC(1), Scalar(0));

使用 Scalar(0) 将所有元素初始化为零,如果为多通道,使用 Scalar::all()。


1.1 关于 CV_< bits >< type >C(< channels >) 的补充

该参数将数据类型、位计数和通道数统一到一个宏中,通常使用的宏模式为:

CV_< bits >< type >C(< channels >)

其中:
< bits > 可以替换为:

  • 8:无符号和有符号整数
  • 16:无符号和有符号整数
  • 32:无符号和有符号整数以及浮点数
  • 64:无符号和有符号浮点数

< type > 可以替换为:

  • U:用于无符号整数
  • S:用于有符号整数
  • F:用于有符号浮点数

< channels > 表示的是每一个元素的通道数,比如一个 3 x 3 的 Mat 类型的矩阵,通道数为 3,假如元素初始化为 1,那么这个矩阵可以写成 [(1, 1, 1), (1, 1,1), (1, 1, 1); (1, 1, 1), (1, 1,1), (1, 1, 1); (1, 1, 1), (1, 1,1), (1, 1, 1)],其中每一个元素都是一个向量,示例程序如下:

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main()
{
	Mat matrix(3, 3, CV_8UC3, Scalar::all(1));
	cout << matrix;
}

程序输出如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/a6b6a31fadf74d568c5a9e09b7592faf.png

可以看到,opencv 的输出风格并未给每一个元素加上小括号,这一点与 C++ 的矩阵风格输出不同,但是也应该要知道,这表示的是多通道的输出数据。

2 使用已知的向量构造 Mat 类型

创建一个立方体(三维数组),边长为 10, 类型为双精度(64位)的双通道元素,并用 1.0 初始化所有值:

int sizes[] = {10, 10, 10};
Mat cube(3, sizes, CV_64FC(2), Scalar::all(1.0));

实际上,计算机只能输出 2 维的矩阵数据,因此此处的 3 维数据虽然可以创建,但不能如上一个程序一样使用 cout 输出。

3 使用 create 来创建或更改大小和类型

Mat matrix(3, 3, CV_32S(2));
matrix.create(10, 10, CV_8UC(2)); // 更改大小和类型

4 创建 ROI(感兴趣区域)

创建一个 Mat 类,它是另一个 Mat 类的一部分,称之为感兴趣区域(Region of Interest, ROI)。当只需要访问图片的一部分区域时,可以使用这样的方式。比如需要对图像的一部分进行滤波,创建一个包含 50 x 50 像素的正方形 ROI Mat 类:

Mat roi(image, Rect(25, 25, 50, 50);

第一个参数是想要处理的图像,第二个参数是使用 Rect() 进行区域约束,前两个数据分别表示起点的横坐标和纵坐标(以原图象 image 的左上角为起点,向下和向右为正)。

需要注意的是使用上述方法创建的 Mat 类 roi,本质上使用的是原始 Mat 类的头和地址,因此如果对 roi 进行操作,那么原始图片也将被改变。如果想要复制 Mat 类,应该使用 clone() 函数。
Mat imageCopy = image. clone();

5 选择一个 Mat 中的行或列

选择一行或一列:

Mat r = image.row(0); // 第一行
Mat c = image.col(0); // 第一列

选择多行或多列:
下列示例程序将在图像中创建一个加号:

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;

int main()
{
	Mat logo = imread("D:\\w.jpg"); // 读取图片
	// 使用 rowRange() 和 colRange() 方法选取 ROI,此处选择了中心处宽度为 4 的像素区域
	Mat centralRows = logo.rowRange(logo.rows / 2 - 2, logo.rows / 2 + 2); 
	Mat centralCols = logo.colRange(logo.cols / 2 - 2, logo.cols / 2 + 2);
	// 将 ROI 的值设置为 0,这样其输出图像则为黑色
	centralRows = Scalar(0);
	centralCols = Scalar(0);
	// 创建窗口
	namedWindow("+", WINDOW_AUTOSIZE);
	// 显示图片
	imshow("+", logo);
	// 图片显示时间,以 ms 为单位,参数为 0 时,窗口在用户有键盘动作时销毁
	waitKey(0);
}

6 访问 Mat 类中的元素

6.1 at 方法

at 是一个函数模板,可以用来访问 Mat 类中的一个元素。
假设有一个标准的三通道彩色图像,加载到名为 image 的 Mat 类中(image 是 CV_8UC(3) 类型),可以编写以下代码段来访问 (X, Y) 位置处的像素,并将其颜色值设置为 C:

image.at< Vec3b >(X, Y) = C;

注意到此处使用了 vec 类,这是 OpenCV 提供以便于更容易地进行数据访问和处理的方法。实际上可以使用 typedef 创建并命名自己的 Vec 类型:typedef Vec< Type, C > NewType;

OpenCV 中创建的 Vec 类型如下(参考博文):

typedef Vec<uchar, 2> Vec2b;
typedef Vec<uchar, 3> Vec3b;
typedef Vec<uchar, 4> Vec4b;
 
typedef Vec<short, 2> Vec2s;
typedef Vec<short, 3> Vec3s;
typedef Vec<short, 4> Vec4s;
 
typedef Vec<ushort, 2> Vec2w;
typedef Vec<ushort, 3> Vec3w;
typedef Vec<ushort, 4> Vec4w;
 
typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;
typedef Vec<int, 6> Vec6i;
typedef Vec<int, 8> Vec8i;
 
typedef Vec<float, 2> Vec2f;
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;
 
typedef Vec<double, 2> Vec2d;
typedef Vec<double, 3> Vec3d;
typedef Vec<double, 4> Vec4d;
typedef Vec<double, 6> Vec6d;

6.2 begin/end 方法和 forEach 方法

针对这三种方法,使用以下代码实现相同目标,它们都通过将每个像素的值除以 5,使图像变得更暗。
at 函数:

for (int i = 0; i < image.rows; i++)
	{
		for (int j = 0; j < image.cols; j++)
		{
			image.at<Vec3b>(i, j) /= 5;
		}
	}

通过 begin 和 end 函数使用类似于 STL 的迭代器:

MatIterator_<Vec3b> it_begin = image.begin<Vec3b>(); // 得到迭代器的开始
MatIterator_<Vec3b> it_end = image.end<Vec3b>(); // 得到迭代器的结束
for (; it_begin != it_end; it_begin++)
{
	*it_begin /= 5; // 对迭代器进行循环
}

使用 forEach 函数(与 lambda)一起使用

image.forEach<Vec3b>([](Vec3b& p, const int*)
	{
		p /= 5;
	});
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值