"概念梳理"
// 矩阵的完整类型 = CV_ + 位深 + 数据类型 + C + 通道数
// e.g.1 CV_32FC3: 32位float型矩阵,通道数为3
// e.g.2 CV_8UC1:   8位uchar型矩阵,通道数为1 
// e.g.3 CV_64F: 等价于CV_64FC1 (前者是简写),即64位double型矩阵,通道数为1 

// 假设存在一个float型单通道矩阵
cv::Mat mat = cv::Mat::zeros(2, 2, CV_32FC3);

mat.depth()    // 返回矩阵的数据类型, 如此处返回 CV_32F (int值)
mat.channels() // 返回矩阵的通道数, 如此处返回 3 (int值)
mat.type()     // 返回矩阵的完整类型, = 数据类型+通道数, 如此处返回 CV_32FC3 (int值)

mat.convertTo(mat, CV_64F);  // 处理后mat的完整类型是CV_64FC3
                             // 也就是说,.convertTo函数第二个参数传入的是数据类型(位深)而不是完整类型
                             // 也就是说,.convertTo函数只能转换数据类型而不能改变通道数


// 由type()值换算出depth()值和channels()值
int type = mat.type();
int depth = type & 7; 
int channels = (type >> 3) + 1;  // 表示右移三位,即低三位不要,高位补0, e.g. (9 >> 1) == 4

// 由depth()值和channels()值计算type()值
type = depth + (channels-1)*8

附1:depth 可能存在的值即意义

数字宏定义数据类型
0CV_8Uuchar / 无符号 8 位
1CV_8Schar / 有符号 8 位
2CV_16Uunsigned short
3CV_16Sshort
4CV_32Sint
5CV_32Ffloat
6CV_64Fdouble

附2:常见type值即对应depth和channels

数据类型C1 (单通道)C3(三通道)C4(四通道)
CV_8U (uchar)0        1624
CV_32S (int)42028
CV_32F (float)52129
CV_64F (double)62230

附3:depth值和channels值,计算type值

type() = depth + (channels - 1) * 8

附4:type值分解成depth值和channels值

见上述代码

总结:

(1)和矩阵数据类型有关的概念

  1. 假设存在矩阵 cv::Mat mat
  2. 矩阵的完整数据类型由 mat.type() 可以访问得到, e.g. mat.type()=20, 对应 CV_32SC3, 表明该矩阵是由32位int值组成的三通道矩阵;
  3. 矩阵的位深:由 mat.depth() 可以访问得到, e.g. mat.depth()=4, 对应 CV_32S, 表明该矩阵是由32位int值组成的;
  4. 矩阵的通道数:由 mat.channels() 可以访问得到, e.g. mat.channels()=3, 表明该矩阵是三通道的;
  5. 因此可以基于位深,即 mat.depth() 所得值通过查表来唯一地确定构成该矩阵的数据的数据类型。

只通过mat.type()得到的矩阵完整数据类型是一个数字,只有知道其对应的位深通道数后才能获悉对编程者有意义的数据类型概念,通常有三个方法:

方法A:通过查表(附2中表格),四个通道及以下的矩阵很常用,因此由mat.type()值能够快速获悉,例如mat.type()=20,可以快速获悉,其矩阵完整数据类型为CV_32SC1,同时位深和通道数也一并获悉;

方法B:通过mat.depth()和mat.channels()可以获悉位深和通道数;注意,mat.depth()获悉的位深也是一个数字,但位深只存在7种可能,位深数字对应的意义是固定的,如附1种表格所示。例如mat.depth()=4,其对应的意义就是CV_32S;

方法C:如果不使用mat.depth()和mat.channels()这两个函数,则可以通过以下公式计算出位深和通道数

int type = mat.type();
int depth = type & 7; 
int channels = (type >> 3) + 1;  // 表示右移三位,即低三位不要,高位补0, e.g. (9 >> 1) == 4

或者

int type = mat.type();
int depth = CV_MAT_DEPTH(type);
int channels = CV_MAT_CN(type);

(2)和矩阵所占大小有关的概念

  1. 通常说一个矩阵或者一段数据占内存的大小用的单位是字节,比如windows系统种,对一个文件(如png图像)如果右键属性,可以在“大小”一栏中看到其所占的字节数;顺便说一句,右键属性,也可以看到“占用空间”这栏,后者大于等于前者,前者表征数据实际大小,后者表征为了保存该数据,计算机不得不分配多少空间来保存它;
  2. 每个字节代表八个二进制数,即 1 字节 = 8 位,对于一个文件或者一个变量,我们也会用“它占了多少位”来表征其大小。
    比如,我们说一个uchar类型的数字/变量,其大小为1个字节,对应了8位,这也就是为什么uchar表示的数字区间是2的0次方到2的8次方(左闭右开区间,即最小为0最大为255);这也就是为什么uchar类型的矩阵其深度称为 CV_8U(即8位uchar类型数字构成)
  3. 一个变量的字节数可以通过 sizeof(...) 来获取,如:
    sizeof(uchar) = 1;   // 即一个uchar型变量占1个字节,即8位
    sizeof(int) = 4;        // 即一个int型变量占4个字节,即32位
    sizeof(float) = 4;     // 即一个float型变量占4个字节,即32位
    sizeof(double) = 4; // 即一个double型变量占8个字节,即64位
  4. 假设存在矩阵 cv::Mat mat
  5. 矩阵的每像素字节数:由mat.elemSize()可以访问得到,即矩阵的每个位置所占用的字节数大小,mat.elemSize() = 通道数 ✖ 每通道上每个像素位置对应数据所占的字节大小
    e.g. mat.type() = CV_32SC3,因此 mat.elemSize() = 12,即 3 ✖ 4,其中3是通道数,4是int值对应的字节数
  6. 矩阵的每通道每像素字节数:由mat.elemSize1()可以访问得到,即矩阵的每个通道的每个位置所占用的字节数大小,mat.elemSize() =  mat.channels()*mat.elemSize(),e.g. mat.type() = CV_32SC3,因此 mat.elemSize1() = 4,4即位int值对应的字节数;
  7. 因此一个矩阵的大小,如果用字节数表述,有如下方式可计算得出:
    矩阵占用字节数 = mat.rows * mat.cols * mat.elemSize()
    矩阵占用字节数 = mat.rows * mat.cols * mat.channels() * mat.elemSize1()

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐