Mat是OpenCV的最基本的类型,他有很多常见的属性和方法,可以获取这张图片的基本信息,帮助我们更好地理解图片,本文做了一个简单的小结,并说明了一些常见的易错点。
一、Mat对象常见的属性以及方法一览
cout << image.cols << endl; //相片的列数,一共有多少列,对应width
cout << image.rows << endl; //相片的行数,一共有多少行,对应height
cout << image.dims << endl; //相片的维度,这里都是二维
cout << image.type() << endl; //16,其实是CV_8U3,即8位无符号三通道
cout << image.total() << endl; //156500 总共有多少个元素,即rows*cols
cout << image.channels() << endl; //相片的通道数,可能是1、3、4
//几个需要特别注意的方法,在下面一个一个说明
cout << image.step << endl; //1500
cout << image.step1() << endl; //1500
cout << image.elemSize() << endl; //3
cout << image.elemSize1() << endl; //1
cout << typeid(image.data).name() << endl;
1、type()方法
该方法返回的是一个int类型的整数,表示的是每一个像素(i,j)的数据类型,常见的比如:
CV_8U:8位单通道无符号,即灰度图像,返回的是0
CV_8UC3:8位三通道无符号,即常见的RGB图像,返回的是16
CV_32FC3:32位浮点数三通道,返回的是21
等等
2、elemSize1()方法
每一个像素位置(i,j)处单个通道所占用的字节数,是以字节byte为单位的。
CV_8U:8位单通道无符号,即灰度图像,只有一个通道,它为1byte
CV_8UC3:8位三通道无符号,即常见的RGB图像,每一个通道是8bit,依然为1byte
CV_32FC3:32位浮点数三通道,每一个通道是32位,所以为4byte
等等
3、elemSize()方法
每一个像素位置(i,j)处所有通道所占用的字节数,是以字节byte为单位的。
CV_8U:8位单通道无符号,即灰度图像,只有一个通道,它为1byte
CV_8UC3:8位三通道无符号,即常见的RGB图像,每一个通道是8bit,有三个通道,所以为3byte
CV_32FC3:32位浮点数三通道,每一个通道是32位,即4byte,一共有三个通道所以是 12byte
等等
注意:elemSize() 和elemSize1()的区别。
4、step属性
这个属性表示的是图片每一行的字节数,一字节byte为单位,
单通道8位灰度图:由于每一个像素只有一个通道,且为8位,即1字节,所以 step=1cols;
三通道8位RGB图:由于每一个像素包含三个通道,每一个通道为1字节,所以一个像素占3字节,所以step=3cols;
对于三通道32位浮点数:即上面的CV_32FC3,每一个像素有三个通道,每一个通道占用32位即4字节,所以每一个像素占用34字节,所以step=34*cols。
5、step1()方法
这个是最容易出错的,step1()=step/elemSize1()
二、Mat类的元素高效遍历方法
Mat类的元素便利有很多的方法,但是油的方法比较慢,这里提供一种高校的元素遍历方法,即通过image.data指针来实现。
2.1 data指针到底是什么意思
需要特别注意的是,
data指针指向的是Mat的首元素的指针,即位置(0,0)处的指针,它是将每一个像素点当成一个一维数组,然后指向这个数组的首元素,如果是单通道,则这个一位数组只有一个元素,如果是三通道,则这个一维数组是三个元素;
而且无论图像是什么类型,他总是返回的是unsigned char * 指针类型,即uchar类型,所以需要注意类型转换。
(1)对于单通道灰度图
//8位单通道,每个像素仅仅占用 1 byte
for (int i = 0; i < rows; i++)
{
uchar * pixel = a.data + i * a.step; //将指针移动到每一行的开始
for (int j = 0; j < 5; j++)
{
cout << pixel[0] << endl; //单通道只有一个元素
pixel+=1; //将指针移动到下一列
}
cout << endl;
}
(2)对于三通道RGB图像
//8位单通道,每个像素仅仅占用 1 byte
for (int i = 0; i < rows; i++)
{
uchar * pixel = a.data + i * a.step; //将指针移动到每一行的开始
for (int j = 0; j < 5; j++)
{
cout << pixel[0] << endl; //三通道第一个元素
cout << pixel[1] << endl; //三通道第二个元素
cout << pixel[2] << endl; //三通道第三个元素
pixel+=3; //将指针移动到下一列的首元素
}
cout << endl;
}
(3)对于三通道32位浮点数,即CV_32FC3
//8位单通道,每个像素仅仅占用 1 byte
for (int i = 0; i < rows; i++)
{
float * pixel = (float *)a.data + i * a.step/4; //将指针移动到每一行的开始需要转换,为什么需要除以4,一定要弄清楚它的本质
for (int j = 0; j < 5; j++)
{
cout << pixel[0] << endl; //三通道第一个元素
cout << pixel[1] << endl; //三通道第二个元素
cout << pixel[2] << endl; //三通道第三个元素
pixel+=3; //将指针移动到下一列的首元素
}
cout << endl;
}
两个需要注意的点:
(1)第一:什么时候需要除以一个数,这取决于每一个像素的每一个通道占用几个字节
float * pixel = (float *)a.data + i * a.step/4;
(2)第二:什么时候加1,什么时候加3,这取决于相片的通道数目
pixel+=3;