1.opencv Mat类型定义
cv::Mat a(cv::Size(w,h),CV_8UC1); // 单通道
cv::Mat b = cv::Mat(cv::Size(w,h),CV_8UC3); //3通道每个矩阵元素包含3个uchar值
对于维数较小的Mat类型,直接定义赋值方法如下:
Mat mtest = (Mat_<float>(4, 1) << -0.055818, -0.734866, -0.675912, 0.506045);
2.定义特殊矩阵
cv::Mat mz = cv::Mat::zeros(cv::Size(w,h),CV_8UC1); // 全零矩阵
【或者:Mat tmpdata = Mat::zeros(h, w, CV_8UC1);//h行w列的全0矩阵】
cv::Mat mo = cv::Mat::ones(cv::Size(w,h),CV_8UC1); // 全1矩阵
【或者:Mat tmpdata = Mat::ones(h, w, CV_8UC1);//h行w列的全1矩阵】
cv::Mat me = cv::Mat::eye(cv::Size(w,h),CV_32FC1); // 对角线为1的对角矩阵
【或者:Mat tmpdata = Mat::eye(h, w, CV_32FC1);//h行w列的对角矩阵】
3.Mat类型遍历与赋值
1)拷贝赋值
浅层拷贝:Mat B=A;B就是浅层拷贝A,B只拷贝了A的的头部和地址,当B被操作后A也随之改变。
深层拷贝:Mat A=imread("x.jpg"); Mat B=A.clone();B是开辟了新的内存完全的复制了A的内容,操作B不会对A造成影响。
2)将数据类型为U16的dataU16赋值给数据类型为u8的dataU8(数据类型转换).
Mat dataU16 = Mat(Size(w, h), CV_16UC1);
Mat dataU8 = Mat(Size(w, h), CV_8UC1);
U16* pxvecU16 = dataU16.ptr<U16>(0);
U8* pxvecU8 = dataU8.ptr<U8>(0);
for (int i = 0; i < dataU16.rows; i++)
{
pxvecU16 = dataU16.ptr < U16>(i);
pxvecU8 = dataU8.ptr<U8>(i);
for (int j = 0; j < dataU16.cols; j++)
{
pxvecU8[j] = (U8)pxvecU16[j];
}
}
OpenCV—矩阵数据类型转换cv::convertTo
函数
void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) const;
参数
m – 目标矩阵。如果m在运算前没有合适的尺寸或类型,将被重新分配。
rtype – 目标矩阵的类型。因为目标矩阵的通道数与源矩阵一样,所以rtype也可以看做是目标矩阵的位深度。如果rtype为负值,目标矩阵和源矩阵将使用同样的类型。
alpha – 尺度变换因子(可选)。
beta – 附加到尺度变换后的值上的偏移量(可选)。
描述
函数将源矩阵中的像素值转换为目标类型。最后会使用溢出保护函数saturate_cast<> ,以避免转换过程中可能出现的溢出。函数执行如下运算:
![](https://img-blog.csdn.net/20151017150143984)
示例
在应用分水岭算法分割图像时,标记图像为32位有符号整型CV_32S变量(以便定义超过255个标签,每个值标记一类物体,如255标记目标,128标记背景,0标记未知等等)构成的矩阵markers,想要将标记图像显示出来必须转换其数据类型。
markers.convertTo(tmp,CV_8U,255,255);
将矩阵markers转换为CV_8U类型的矩阵tmp:tmp(x,y)= markers(x,y)*255+255.这样,将图像做线性变换,使值为-1的像素变为0(-1*255+255=0)。值大于255的像素将赋值为255,这是因为CV32S转换为无符号CV_8U时,应用了饱和度运算。具体应用参看分水岭算法的相关博文。
如果scale=1,shift=0,则不进行比例缩放。
如果输入数组与输出数组的类型相同,则函数可以被用于缩放和平移矩阵或图像;
例:cv::Mat src, src_f;
image.convertTo(src_f, CV_32F, 1.0/255, 0);
图像大小没有变化,但是类型又UINT8变为了FLOAT32位.
openCV Mat初始化
这次来记一下自己对Mat类的理解,供交流
首先,使用Mat就不需要为其手动分配内存大小,最后也不需要手动释放它。但是我们在使用openCV函数的时候,还是要手动分配其输入数据。
第二点,Mat本质是由两部分数据组成的类,矩阵头(header)和指针Pointer,矩阵头主要是包含矩阵的大小,存储方式,存储地址等信息,指针中存储了指向存储图像像素值矩阵的指针。
一个常用的Mat类的构造函数,但是要知道,Mat类的有很多重载的构造函数
int main()
{
Mat M(2, 2, CV_8UC3, cv::Scalar::all(1));
//前两个参数时指矩阵的行数和列数,
//第3个参数时重点,表示矩阵的数据类型,接下来会详细讲解
//第4个是对每个像素值赋初值,这个代码就是把每个通道的像素值都
//都赋值1.,如果是Scalsr(255,0,0),就是将255,0,0分别赋予每个像素点的3个通道。
cout << "M=" << endl << M << endl;
system("pause");
return 0;
}
阵列的数据类型说明
阵列的数据类型定义了为阵列的每个元素(图片中的像素)通道数和每个通道上表示像素值得比特数(位数)。任何阵列的元素都应该有下面数据类型的一种:
单通道阵列
CV_8U:
CV_8U (8 bit 无符号整数)
CV_8S (8 bit 有符号整数)
CV_16U(16 bit 无符号整数)
CV_16S (16 bit 有符号整数)
CV_32S (32 bit 有符号整数)
CV_32F (32 bit 浮点数)
CV_64F (64 bit 浮点数)
举例来说:下图展示了一个使用8 bit无符号整数的单通道阵列。因为数据类型是8 bit无符号整数,因此这个阵列的每个元素为0-255的值
对应存储图为
这里写图片描述
多通道阵列(最大支持512个通道)
CV_8UC1 (单通道阵列,8 bit 无符号整数)
CV_8UC2 (2通道阵列,8 bit 无符号整数)
CV_8UC3 (3通道阵列,8 bit 无符号整数)
CV_8UC4 (4通道阵列,8 bit 无符号整数)
CV_8UC(n) (n通道阵列,8 bit 无符号整数 (n 可以从 1 到 512) )
下图展示了一个使用8 bit 无符号整数的3通道阵列。因为数据类型是8 bit无符号整数,因此这个阵列的每个元素为0-255的值。由于是3通道阵列,所以阵列由带有3个元素的元组组成,第一个元组是{54, 0, 34},第二个元组是 {58, 78, 185} ,以此类推。
这里写图片描述
对上面的数据类型使用举例
Mat image(3,5,CV_32F);
Mat image(3,5CV_64FC(5));
Mat image(Size(100,200),CV16UC2);
-还可能会遇到这个表达,IPlImage*,这个表达式C语言操作OPENCV的数据结构,地位相当于Mat。
Mat(const IplImage* img, bool copyData=false);
可以很好的将IplImage*转换为Mat
还有一种定义也可以转换
IplImage* img = cvLoadImage("greatwave.png", 1);
Mat mtx(img); // convert IplImage* -> Mat
还要注意Mat类使用了引用技术机理,简单讲,就是在拷贝Mat类的对象的时候,只是将矩阵头和指针复制,而复制后两个对象指针还是指向同一块矩阵数据区。如果要将数据矩阵也复制,必须使用copyTo()或者clone()函数。
可以用下图来帮助理解
这里写图片描述
Mat A,C;
A=imread("1.jpg",type);
Mat B(A);//拷贝构造函数
C=A;//直接是赋值运算
代码中的所有Mat对象最终都指向同一个也是唯一一个数据矩阵。虽然它们的信息头不同,但通过任何一个对象所做的改变也会影响其它对象。实际上,不同的对象只是访问相同数据的不同途径而已。
如果要将数据区一起赋值,就要这样,如下
Mat F=A.clone();
Mat G;
A.copyTo(G);
现在改变 F 或者 G 就不会影响 Mat 信息头所指向的矩阵。 所以说Mat 不但是一个很赞的图像容器类,它同时也是一个通用的矩阵类,所以可以用来创建和操作多维矩阵。