文章目录
- Mat类
- Mat类数据类型读取
- Mat类支持的运算
- OpenCV Mat数据类型指针ptr的使用
- 多维矩阵创建
Mat类
Mat类数据类型读取
S = 有符号整型
U = 无符号整型
F = 浮点型
CV_8U - 8位无符号整数(0…255)
CV_8S - 8位有符号整数(-128…127)
CV_16U - 16位无符号整数(0…65535)
CV_16S - 16位有符号整数(-32768…32767)
CV_32S - 32位有符号整数(-2147483648…2147483647)
CV_32F - 32位浮点数(-FLT_MAX…FLT_MAX,INF,NAN)
CV_64F - 64位浮点数(-DBL_MAX…DBL_MAX,INF,NAN)
CV_8UC3而后面的C1、C2、C3是什么意思呢?
这里的1、2、3代表的是通道数,比如RGB就是3通道,颜色表示最大为255,所以可以用CV_8UC3这个数据类型来表示;灰度图就是C1,只有一个通道;而带alph通道的PNG图像就是C4,是4通道图片。
如果矩阵是类型:CV_8U 则使用 : Mat.at<uchar>(y,x)
如果矩阵是类型:CV_8S 则使用 : Mat.at<schar>(y,x)
如果矩阵是类型:CV_16U 则使用 : Mat.at<ushort>(y,x)
如果矩阵是类型:CV_16S 则使用 : Mat.at<short>(y,x)
如果矩阵是类型:CV_32S 则使用 : Mat.at<int>(y,x)
如果矩阵是类型:CV_32F 则使用 : Mat.at<float>(y,x)
如果矩阵是类型:CV_64F 则使用 : Mat.at<double>(y,x)
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;
Mat类支持的运算
设为A,B为Mat类型,s是Scalar类型,a是一个实数。下面列出关于Mat的常用运算:
矩阵加减: A+B,A-B,A+s,A-s,s+A,s-A,-A.
矩阵乘以实数: A*a,a*A
逐元素乘除: A.mul(B),A/B,a/A
矩阵乘法: A*BmaxVal; Point minPos,m
矩阵倒置: A.t()
矩阵的逆: A.inv()
矩阵比较: A comp B,A comp a,a comp A。这里comp包括 >, >=,==,!=,<=,<。得出的结果是一个单通道8位的矩阵,元素的值为255或0。
矩阵位操作: A logic B, A logic s,s logic A。这里logic包括:&,|,^
向量的差乘和内积: A.cross(B),A.dot(B);
OpenCV是一个广泛使用的计算机视觉库,它提供了许多用于图像处理和计算机视觉任务的函数和工具。在OpenCV中,Mat是一个重要的数据结构,用于表示图像和矩阵。Mat类提供了一种方便的方式来处理图像和矩阵数据。
下面是使用OpenCV的Mat类创建各种类型的图像和矩阵的介绍:
- 创建空白图像:
cv::Mat image; // 创建一个空的图像对象
cv::Mat image(rows, cols, type); // 创建指定行数、列数和类型的空白图像
- 从已有数据创建图像:
cv::Mat image(rows, cols, type, data); // 使用指定的数据创建图像,data是指向数据的指针
- 创建单通道图像:
cv::Mat image(rows, cols, CV_8UC1); // 创建一个8位单通道图像
- 创建三通道彩色图像:
cv::Mat image(rows, cols, CV_8UC3); // 创建一个8位三通道彩色图像
- 创建带有初始值的图像:
cv::Mat image(rows, cols, type, cv::Scalar(value)); // 使用指定的初始值创建图像
- 从文件加载图像:
cv::Mat image = cv::imread("image.jpg"); // 从文件加载图像
- 创建矩阵:
cv::Mat matrix(rows, cols, CV_32FC1); // 创建一个32位浮点型单通道矩阵
- 创建具有初始值的矩阵:
cv::Mat matrix = cv::Mat::eye(rows, cols, CV_32FC1); // 创建一个单位矩阵
这些只是创建不同类型的图像和矩阵的一些示例。OpenCV的Mat类还提供了许多其他方法和操作,可以对图像和矩阵进行各种处理和分析。你可以参考OpenCV的官方文档和教程,以获取更详细的信息和示例代码。
OpenCV(Open Source Computer Vision Library)是一个广泛使用的开源计算机视觉库,提供了许多用于图像和视频处理的功能。在OpenCV中,Mat类是用于表示图像和矩阵的主要数据结构。Mat类提供了许多方法和操作符,用于访问和处理图像的像素值和矩阵的元素。
下面是一些常见的Mat类的访问和操作方法:
- 创建Mat对象:
使用默认构造函数创建空的Mat对象:
cv::Mat image;
使用图像文件路径创建Mat对象:
cv::Mat image = cv::imread("image.jpg");
使用矩阵数据创建Mat对象:
cv::Mat matrix = cv::Mat::ones(3, 3, CV_32F);
- 访问像素值:
使用at
方法访问像素值:
cv::Vec3b pixel = image.at<cv::Vec3b>(row, col);
使用ptr
方法访问像素值:
uchar* pixelPtr = image.ptr(row);
uchar blue = pixelPtr[col * image.channels()];
uchar green = pixelPtr[col * image.channels() + 1];
uchar red = pixelPtr[col * image.channels() + 2];
- 修改像素值:
使用at
方法修改像素值:
image.at<cv::Vec3b>(row, col) = cv::Vec3b(255, 0, 0); // 设置像素为蓝色
使用ptr
方法修改像素值:
uchar* pixelPtr = image.ptr(row);
pixelPtr[col * image.channels()] = blue;
pixelPtr[col * image.channels() + 1] = green;
pixelPtr[col * image.channels() + 2] = red;
- 获取图像属性:
获取图像的宽度和高度:
int width = image.cols;
int height = image.rows;
获取图像的通道数:
int channels = image.channels();
获取图像的数据类型:
int type = image.type();
- 图像操作:
图像的拷贝:
cv::Mat imageCopy = image.clone();
图像的裁剪:
cv::Mat croppedImage = image(cv::Rect(x, y, width, height));
图像的缩放:
cv::Mat resizedImage;
cv::resize(image, resizedImage, cv::Size(newWidth, newHeight));
这只是一些Mat类的常见访问和操作方法示例。Mat类还提供了许多其他方法和操作符,用于在图像处理中进行各种操作。你可以参考OpenCV的官方文档和示例代码,了解更多关于Mat类的详细信息和用法。
cv::Mat
是 OpenCV 中用于表示图像和矩阵的类。它是 OpenCV 中最基本的数据结构之一,提供了许多功能和操作,使得图像处理和计算变得更加方便。下面是一些关于 cv::Mat
类的常见修改方法的介绍:
- 构造函数:
cv::Mat
提供了多种构造函数来创建矩阵对象。可以使用默认构造函数创建一个空的矩阵,也可以使用指定行数、列数、数据类型的构造函数创建具有特定尺寸和数据类型的矩阵。例如:
cv::Mat emptyMat; // 创建一个空矩阵
cv::Mat mat(3, 3, CV_8UC1); // 创建一个3x3的无符号8位单通道矩阵
- 复制和赋值:可以使用
=
运算符将一个cv::Mat
对象复制给另一个对象,从而进行矩阵的复制。复制操作将复制矩阵的数据和属性。例如:
cv::Mat srcMat = cv::imread("image.jpg"); // 从文件读取图像
- 修改矩阵元素:可以使用
at()
成员函数来访问和修改矩阵的像素值。该函数接受行索引和列索引作为参数,并返回相应位置的像素值的引用。例如,修改单通道图像的像素值:
cv::Mat image = cv::imread("image.jpg", cv::IMREAD_GRAYSCALE); // 以灰度模式读取图像
image.at<uchar>(y, x) = 255; // 将位置 (x, y) 的像素值设置为255
- 修改矩阵属性:
cv::Mat
类提供了一些成员函数来修改矩阵的属性,如行数、列数和数据类型。可以使用rows
、cols
和type
成员函数分别获取矩阵的行数、列数和数据类型,并使用reshape()
函数来改变矩阵的尺寸。例如:
cv::Mat mat(3, 3, CV_8UC1); // 创建一个3x3的无符号8位单通道矩阵
int rows = mat.rows; // 获取行数
int cols = mat.cols; // 获取列数
int type = mat.type(); // 获取数据类型
mat = mat.reshape(1, 1); // 转换为1行1列的矩阵
- 修改矩阵数据:可以使用
data
成员变量来直接访问和修改矩阵的数据。数据以行优先顺序存储,并且可以通过指针算术运行访问和修改数据。例如,将矩阵的所有像素值乘以2:
cv::Mat mat(3, 3, CV_8UC1); // 创建一个3x3的无符号8位单通道矩阵
for (int i = 0; i < mat.rows; i++) {
for (int j = 0; j < mat.cols; j++) {
mat.data[i * mat.cols + j] *= 2; // 修改像素值
}
}
- 修改矩阵尺寸:可以使用
resize()
成员函数来修改矩阵的尺寸。可以指定新的行数、列数和插值方法。例如,将矩阵的尺寸调整为2倍:
cv::Mat mat(3, 3, CV_8UC1); // 创建一个3x3的无符号8位单通道矩阵
cv::resize(mat, mat, cv::Size(), 2.0, 2.0, cv::INTER_LINEAR); // 将矩阵尺寸调整为原来的2倍
- 修改矩阵数据类型:可以使用
convertTo()
成员函数来修改矩阵的数据类型。可以指定新的数据类型和比例因子。例如,将矩阵的数据类型转换为浮点型:
cv::Mat mat(3, 3, CV_8UC1); // 创建一个3x3的无符号8位单通道矩阵
cv::Mat floatMat;
mat.convertTo(floatMat, CV_32FC1); // 将矩阵数据类型转换为单精度浮点型
这些是 cv::Mat
类的一些常见修改方法的介绍。使用这些方法,可以对矩阵进行各种操作,包括访问和修改像素值、修改矩阵属性、调整尺寸和数据类型转换等。
Mat_
和Mat
是OpenCV中用于表示矩阵和图像的两个类。它们之间有一些区别和关联。
-
数据存储方式:
Mat
类是OpenCV中通用的矩阵和图像数据结构,可以存储不同维度、通道数和数据类型的数据。它使用一个连续的内存块来存储数据。而Mat_
类是Mat
类的模板化版本,用于表示多维数组。它也使用连续的内存块存储数据,但在编译时期就确定了矩阵的大小和数据类型。 -
类型安全性:由于
Mat_
类在编译时期确定了矩阵的大小和数据类型,因此它提供了更高的类型安全性。在使用Mat_
类时,编译器可以在编译时进行类型检查,从而避免一些潜在的错误。 -
成员函数和操作符重载:
Mat
类和Mat_
类提供了一些相似的成员函数和操作符重载,用于访问和操作矩阵的元素。例如,两者都支持使用括号运算符重载来访问和修改矩阵中的元素。此外,它们都提供了一些常见的函数,如转置、求逆、行列式计算等。 -
兼容性:
Mat_
类是Mat
类的子类,因此可以将Mat_
对象赋值给Mat
对象,反之亦然。这使得在使用OpenCV的函数和算法时可以无缝切换使用这两个类。
综上所述,Mat
类是OpenCV中通用的矩阵和图像数据结构,可以适用于各种维度、通道数和数据类型的数据。而Mat_
类是Mat
类的模板化版本,提供了更高的类型安全性,并在编译时确定了矩阵的大小和数据类型。根据实际需求,可以选择适合的类来表示和处理矩阵和图像数据。
使用Mat_
类可以按照以下步骤进行:
- 包含头文件:首先,需要包含OpenCV库的头文件。
#include <opencv2/opencv.hpp>
- 创建
Mat_
对象:使用Mat_
类的模板形式来创建矩阵对象,并指定矩阵的大小和数据类型。
cv::Mat_<float> mat(3, 3); // 创建一个3x3的单通道浮点型矩阵
- 访问和修改元素:可以使用括号运算符重载来访问和修改矩阵中的元素。根据需要,可以使用行列索引或线性索引来访问元素。
mat(0, 0) = 1.0f; // 设置第一行第一列的元素为1.0
float value = mat(1, 2); // 获取第二行第三列的元素值
- 使用迭代器遍历矩阵元素:通过获取矩阵的迭代器,可以遍历矩阵中的元素。
for (auto it = mat.begin(); it != mat.end(); ++it)
{
float value = *it; // 访问迭代器当前位置的元素
// 进行处理
}
- 使用循环遍历矩阵元素:通过嵌套循环可以遍历矩阵中的元素。
for (int i = 0; i < mat.rows(); ++i)
{
for (int j = 0; j < mat.cols(); ++j)
{
float value = mat(i, j); // 获取(i, j)位置的元素值
// 进行处理
}
}
- 其他操作:
Mat_
类还提供了一些常见的操作,如获取矩阵尺寸、转置、求逆、行列式计算等。可以使用相应的成员函数来执行这些操作。
cv::Size size = mat.size(); // 获取矩阵的尺寸
cv::Mat_<float> matT = mat.t(); // 对矩阵进行转置操作
cv::Mat_<float> matInv = mat.inv(); // 求矩阵的逆矩阵
float det = cv::determinant(mat); // 计算矩阵的行列式
这些是Mat_
类的基本使用方法,您可以根据需要进行进一步的矩阵操作和图像处理。请参考OpenCV的文档和示例代码以了解更多关于Mat_
类的详细信息和用法。
OpenCV Mat数据类型指针ptr的使用
cv::Mat image = cv::Mat(400, 600, CV_8UC1); //宽400,长600
uchar * data00 = image.ptr<uchar>(0);
uchar * data10 = image.ptr<uchar>(1);
uchar * data01 = image.ptr<uchar>(0)[1];
解释:
定义了一个Mat变量image。
data00是指向image第一行第一个元素的指针。
data10是指向image第二行第一个元素的指针。
data01是指向image第一行第二个元素的指针。
注意:
如果你的程序使用来image.ptr指针,并且出现了下面这样的错误:(假设你使用的软件是Visual Studio 201x)
某某.exe中的 0x75065b68 处有未经处理的异常:Microsoft C++ 异常; 内存位置0x85e790处的cv::Exception。
这可能是因为你不理解image.ptr这个指针,犯了这样的错误:image.ptr(1);指的不是image中第二个像素,而是第一行第二个像素的指针。
使用上面的代码举例:image有400行,有400*600个像素。假设现在你想得到第3行第42个像素的指针,如果你写成:
uchar * data = image.ptr<uchar>(3*image.cols + 41);
这样写是错误的,会出现上面的错误。你得到的不是第3行第42个像素的指针,而是第(3×image.cols + 41)行第0个像素的指针,因为没有(3×image.cols + 41)行,所以没有这个指针,所以错误。
正确的写法:
uchar * data = image.ptr<uchar>(3)[41];
所以要注意这一点:如果程序可以正常编译,但是运行时出错,很有可能是你给指针赋值的时候,索引值溢出指定范围,指针乱指,导致程序跑偏,所以只有在运行时才能发现错误。
多维矩阵创建
Mat(int ndims, const int* sizes, int type);
该函数定义了一个名为"Mat"的构造函数,用于创建一个矩阵对象。参数说明如下:
ndims
:整数类型,表示矩阵的维数(即矩阵的阶数)。( 如果是3 就是三维,对应的sizes 就是三个数)sizes
:指向整数数组的指针,包含了每个维度的大小。数组的长度应与ndims
相等,每个元素表示对应维度的大小。type
:整数类型,表示矩阵元素的数据类型。
用于表示图像或任意维度的多维数组。
比如:
//最后面的两个数:(行,列),确定了一个面
//是一个依次降维的过程
//8,10组成了面,5个面,组成了立方体
int matSize[] = {5,8,10};//每一维元素的个数:8:行,10:列
// 3 表示三维 对应三个数{5,8,10}
Mat mat1(3,matSize, CV_16UC3, Scalar::all(0));
int main()
{
system("color F0");
//Demo1(2维矩阵)///
printf("//Demo1(2维矩阵)\n");
//创建16位无符号整形,每个元素三个通道为1,5,6
Mat f(4, 5, CV_16UC3, Scalar(1, 5, 6, 9));
std::cout << f << std::endl;
/*
[1, 5, 6, 1, 5, 6, 1, 5, 6, 1, 5, 6, 1, 5, 6;
1, 5, 6, 1, 5, 6, 1, 5, 6, 1, 5, 6, 1, 5, 6;
1, 5, 6, 1, 5, 6, 1, 5, 6, 1, 5, 6, 1, 5, 6;
1, 5, 6, 1, 5, 6, 1, 5, 6, 1, 5, 6, 1, 5, 6]
*/
/*
step的几个类别区分:
step:矩阵第一行元素的字节数
step[0] : 矩阵第一行元素的字节数
step[1] : 矩阵中一个元素的字节数
step1(0) : 矩阵中一行有几个通道数
step1(1) : 一个元素有几个通道数(channel())
*/
//获取列数
std::cout << f.cols << std::endl;//5
//获取行数
std::cout << f.rows << std::endl;//4
//获取一行的最大字节数 CV_16UC3 = 2*3=6 6*5=30
std::cout << f.step << std::endl;//30
//矩阵中一行元素的字节数
std::cout << f.step[0] << std::endl;//30
//矩阵中一个元素的字节数
std::cout << f.step[1] << std::endl;//6
//矩阵中一行有几个通道数 元素个数*通道数 = 5*3=15
std::cout << f.step1() << std::endl;//15
//矩阵中一行有几个通道数 元素个数*通道数 = 5*3=15
std::cout << f.step1(0) << std::endl;//15
//一个元素有几个通道数(channel())
std::cout << f.step1(1) << std::endl;//3
//获取每个元素的字节数 CV_16UC3 = 2*3=6
std::cout << f.elemSize() << std::endl;//6
//获取矩阵中元素的个数 4*5 = 20
std::cout << f.total() << std::endl;//20
//获取通道数
std::cout << f.channels() << std::endl;//3
//Vec3s 获取(0,0)为第一个元素,为3通道
cv::Vec3s vc3 = f.at<cv::Vec3s>(0, 0);
//获取元素中第一个通道值
std::cout << (int)vc3.val[0] << std::endl;//1
/*用数据流指针获取通道值
f.data:数据存储的起始地址 (uchar*类型);
*/
//通道1: 1
std::cout << "通道1:" << (int)*(f.data + f.step[0] * 2 + f.step[1] * 2 + 0) << std::endl;
//通道2: 5
std::cout << "通道2:" << (int)*(f.data + f.step[0] * 2 + f.step[1] * 2 + 2) << std::endl;
//通道3: 6
std::cout << "通道3:" << (int)*(f.data + f.step[0] * 2 + f.step[1] * 2 + 4) << std::endl;
//创建一行5列单通道数据 枚举
Mat a = (cv::Mat_<int>(1, 5) << 1, 2, 3, 4, 5);
std::cout << a << std::endl;//[1, 2, 3, 4, 5]
//向量生成对角线矩阵
Mat b = Mat::diag(a);
std::cout << b << std::endl;
/*
[1, 0, 0, 0, 0;
0, 2, 0, 0, 0;
0, 0, 3, 0, 0;
0, 0, 0, 4, 0;
0, 0, 0, 0, 5]
*/
//抠图或者裁剪 获取第2,3行和第3,4列相交 的数据
//按照矩阵中的元素抠图
Mat c = Mat(b, Range(2, 4), Range(3, 5));
std::cout << c << std::endl;
/*
[0, 0;
4, 0]
*/
//生成5行6列,对角线为1的数据,每个元素为16位
Mat d = Mat::eye(5, 6, CV_16U);
std::cout << d << std::endl;
/*
[1, 0, 0, 0, 0, 0;
0, 1, 0, 0, 0, 0;
0, 0, 1, 0, 0, 0;
0, 0, 0, 1, 0, 0;
0, 0, 0, 0, 1, 0]
*/
//生成5行6列,全部元素为1,每个元素为16位
Mat e = Mat::ones(5, 6, CV_16U);
std::cout << e << std::endl;
/*
[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, 1, 1]
*/
//生成5行6列,全部元素为0,每个元素为16位
Mat g = Mat::zeros(5, 6, CV_16U);
std::cout << g << std::endl;
/*
[0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0;
0, 0, 0, 0, 0, 0]
*/
//Demo1(3维矩阵)///
printf("//Demo1(3维矩阵)\n");
//最后面的两个数:(行,列),确定了一个面
//是一个依次降维的过程
//8,10组成了面,5个面,组成了立方体
int matSize[] = { 5,8,10 };//每一维元素的个数:8:行,10:列
Mat mat1(3, matSize, CV_16UC3, Scalar::all(0));
//求step[i]的大小:每一维元素的大小(单位字节)
std::cout << "step[i]的大小" << std::endl;
std::cout << "step[0]:" << mat1.step[0] << std::endl;//CV_16UC3 (2*3)*8*10 = 480:面的大小(第一维)
std::cout << "step[1]:" << mat1.step[1] << std::endl;//(2*3)*10 = 60:线的大小(第二维)
std::cout << "step[2]:" << mat1.step[2] << std::endl;//(2*3) = 6:点的大小(第三维)
//求size[i]:每一维元素的个数
std::cout << "size[i]的大小" << std::endl;
std::cout << "size[0]:" << mat1.size[0] << std::endl;//5:面
std::cout << "size[1]:" << mat1.size[1] << std::endl;//8:线 (行) 8行
std::cout << "size[2]:" << mat1.size[2] << std::endl;//10:列 10列
//求step1(i):每一维元素的通道数
std::cout << "step1(i)的大小" << std::endl;
std::cout << "step1(0):" << mat1.step1(0) << std::endl;//channels(3)*8*10 = 240:面 (step1 = step / elemSize1 = 480 /2 = 240)
std::cout << "step1(1):" << mat1.step1(1) << std::endl;//(3)*10 = 30: 线 (行)
std::cout << "step1(2):" << mat1.step1(2) << std::endl;//3:点
//求elemSize:每个元素的大小(单位字节) CV_16UC3 16位 两个字节,三个通道 2*3 =6
std::cout << "elemSize的大小" << std::endl;
std::cout << "elemSize:" << mat1.elemSize() << std::endl;//6:每个元素的大小
//求elemSize1:每个通道的大小(单位字节) CV_16UC3 16位 两个字节
std::cout << "elemSize1的大小" << std::endl;
std::cout << "elemSize1:" << mat1.elemSize1() << std::endl;//2:每个通道的大小
waitKey(0);
return 0;
}
在OpenCV中,cv::Mat
类代表一个矩阵或多维数组。如果你有一个四维的cv::Mat
对象,你可以使用以下方法来查看其内容:
- 使用
cv::Mat::dims()
方法获取矩阵的维度数量。
int dimensions = mat.dims();
- 使用
cv::Mat::size
方法获取每个维度的大小。
int size1 = mat.size[0]; // 第一维度的大小
int size2 = mat.size[1]; // 第二维度的大小
int size3 = mat.size[2]; // 第三维度的大小
int size4 = mat.size[3]; // 第四维度的大小
- 使用下标操作符
[]
来访问矩阵中的元素。对于四维矩阵,你需要提供四个下标来访问元素的值。
// 访问四维矩阵中某个位置的元素值
int value = mat.at<int>(index1, index2, index3, index4);
请注意,上述代码中的int
类型是一个示例,你需要根据实际情况来确定矩阵中元素的数据类型,并将其替换为正确的类型。
希望这可以帮助你查看和访问四维矩阵的内容!