Opencv中数据结构Mat的相关属性

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Opencv中数据结构Mat的相关属性相关的知识,希望对你有一定的参考价值。

参考技术A

搬运自本人 CSDN 博客: 《Opencv中数据结构Mat的相关属性》

以上摘自OpenCV 2.4.9的官方文档opencv2refman.pdf。

以前虽然能够比较熟练的使用OpenCV,但是最近感觉其实笔者自己对OpenCV的最底层数据结构Mat与IplImage都不怎么熟悉…… 由于笔者比较反感总是需要管理内存的IplImage,所以对Mat数据结构做一下学习工作还是有必要的。

官方说明文档opencv2refman.pdf中,写出了Mat的定义如下:

下面笔者将从几个方面总结Mat数据结构的主要组成。

参考网址:
《OpenCV中对Mat里面depth,dims,channels,step,data,elemSize和数据地址计算的理解 》
《OpenCV Mat的常见属性》
《OpenCV学习笔记(四十)——再谈OpenCV数据结构Mat详解》

参考文档:
《opencv2refman.pdf》

如上面的Mat定义源码,Mat类中有很多重要的数据类型成员。
下面进行简单的列举。

把这四个数据成员放在一起,是因为这四个数据成员相互之间有关系。

数据的存储一直都是个值得关注的问题,所以数据元素存储的位数和范围就十分重要了。depth就体现了每一个像素的位数,即深度。
Mat中包含的图像深度如下所示:

另外还需要注意:大部分OpenCV的函数支持的数据深度只有8位和32位,所以尽量使用CV_64F。

channels表示了矩阵拥有的通道数量,这个比较容易理解:

type表示矩阵中元素的类型(depth)与矩阵的通道个数(channels),可以理解成上面的depth与channels的综合说明。type是一系列预定义的常量,命名规则如下:
<code>CV_+位数+数据类型+通道数</code>
具体有如下值:

表格中,行代表了通道数量channels,列代表了图像深度depth。
例如CV_8UC3,可以拆分为:

注:type一般是在创建Mat对象时设定,若要去的Mat的元素类型,可以不使用type,使用depth。

elemSize表示了矩阵中每一个元素的数据大小,单位是字节。公式如下:
<code>elemSize = channels * depth / 8</code>
例如type == CV_16SC3,则elemSize = 3 * 16 / 8 = 6 Bytes。

elemSize1表示了矩阵元素的一个通道占用的数据大小,单位是字节。公式如下:
<code>elemSize = depth / 8</code>
例如type == CV_16SC3,则elemSize1 = 16 / 8 = 2 Bytes。

使用OpenCV处理图像时,最普遍的处理方式便是遍历图像,即访问所有的图像像素点。但有的算法还需要访问目标像素的邻域,所以这时候就需要了解访问Mat数据元素地址的方式。

假设有矩阵M,则数据元素的地址计算公式如下:
$$ addr(M_i_0, i_1, ... i_m-1) = M.data + M.step[0] * i_0 + M.step[1] * i_1 + ... + M.step[M.dims - 1] * i_M_dims-1 $$
如果是二维数组,则上述公式就简化成:
$$ addr(M_i,j) = M.data + M.step[0] * i + M.step[1] * j $$

注:式中m = M.dims,即矩阵的维度。

假设存在一个二维矩阵如下图所示:

上面是一个3 × 4的矩阵。此时我们按照数据类型为CV_8U, CV_8UC3的情况,分别对其进行讨论。

首先假设其数据类型为CV_8U,也就是单通道的uchar类型,则可以得出上面的数据成员情况分别为:

若假设其数据类型为CV_8UC3,也就是三通道的uchar类型,则可以得出上面的数据成员情况分别为:

假设存在一个三维矩阵如下图所示:

上面是一个3 × 4 × 6的矩阵。假设其数据类型为CV_16SC4,此时对其进行讨论。

关于OpenCV地址访问方法及效率的部分,请见笔者的博文 《OpenCV像素点邻域遍历效率比较,以及访问像素点的几种方法 》 。

OpenCV Mat类常用成员属性和成员方法

Mat类

各个参数详解请查看OpenCV官方文档:https://docs.opencv.org/3.4.8/d3/d63/classcv_1_1Mat.html

Mat常用成员属性

  • data 是指向矩阵数据的uchar类指针,用*解引用后再强转为int可以读到第一个像素数据。
  • dims 矩阵的维度,例如5*6矩阵是二维矩阵,则dims=2,三维矩阵dims=3。
  • rows 矩阵的行数。
  • cols 矩阵的列数。
  • size 矩阵的大小,返回一个向量,二维矩阵即为 行数 x 列数。

Mat常用成员方法

  1. clone() 克隆

    //将m0完全拷贝到m1中,同时拷贝m0中的所有数据,且拷贝的矩阵是连续的。
    m1 = m0.clone();  
    
  2. copyTo()

    // 将m0中的内容拷贝到m1中,如果有必要重新分配m1(等价于m1 = m0.clone())。
    m0.copyTo(m1); 
    //将m0中,mask所指示的的数据拷贝到m1中。
    m0.copyTo(m1, mask); 
    
  3. convertTo()

    //将m0中的元素转换成type类(CV_32F等),作scale尺度的缩放,offset偏移,写入m1中。
    m0.convertTo(m1, type, scale, offset); 
    
  4. setTo()

    //将m0中所有的元素的值设为s;如果使用mask,则只设定mask中的非零元素。
    m0.setTo(s,mask); 
    
  5. reshape()

    //改变二维矩阵的的实际形状,不进行数据拷贝;若chan或rows为0,则表示不作改变。
    m0.reshape(chan, rows)
  6. push_back()

    //对mx1矩阵进行扩展,并在末尾插入单一值s。
    m0.push_back(s); 
    //对mxn矩阵作k行扩展,并将m1拷贝到这些行中;m1的大小为kxn。
    m0.push_back(m1); 
    
  7. pop_back()

    //从m0尾部移除n行,默认情况下n为1。
    m0.pop_back(n); 
    
  8. locateROI()

    //将m0的大小重写为size,如果m0重写后变成更大的矩阵,则起始点为cv::Pointoffset点。
    m0.locateROI(size, offset); 
    
  9. adjustROI()

    //在m0的上下左右分别添加t. `b. ``l. ``r个像素。
    m0.adjustROI(t, b, l, r);
    
  10. total()

    m0.total(); //计算所有数组元素的个数,不考虑通道。
    
  11. isContinuous()

    //如果m0所有的行在内存空间中打包时都没有间隙,则返回true。
    m0.isContinuous(); 
    
  12. elementSize()

    m0.elementSize(); 
    
  13. elementSize1()

    //返回矩阵m0中每个次元素的字节大小(如3通道float型的矩阵则返回4)。
    m0.elementSize1(); 
    
  14. type()

    //返回m0中元素的有效类型标识符(如CV_32FC3)。
    m0.type(); 
    
  15. depth()

    //返回m0中单个通道中元素的有效类型标识符(如CV_32F)。
    m0.depth();
    
  16. channels()

    //返回m0中元素的通道数目。
    m0.channels(); 
    
  17. size()

    //以cv::Size对象的形式返回m0的大小。
    m0.size(); 
    
  18. empty()

    //如果数组中没有元素(如m0.total == 0或m0.data == NULL)则返回true。
    m0.empty(); 
    

Mat类应用简单示例

#include <iostream>
#include <math.h>
#include <opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>

using namespace cv;
using namespace std;

int main(int argc, char** argv)
{
	cv::Mat m1(200, 300, CV_16UC3, cv::Scalar(50, 150, 200));
	cout << "*m1.data = " << int(*m1.data) << endl;
	cout << "m1.dims = " << m1.dims << endl;
	cout << "m1.rows = " << m1.rows << endl;
	cout << "m1.cols = " << m1.cols << endl;
	cout << "m1.size = " << m1.size << endl;
	cout << "m1.channels() = " << m1.channels() << endl;
	cout << "m1.type() = " << m1.type() << endl;
	cout << "m1.depth() = " << m1.depth() << endl;
	cout << "m1.elemSize() = " << m1.elemSize() << endl;
	cout << "m1.elemSize1() = " << m1.elemSize1() << endl;
	cout << "m1.step[0] = " << m1.step[0] << endl;
	cout << "m1.step[1] = " << m1.step[1] << endl;
	cout << "m1.step1(0) = " << m1.step1(0) << endl;
	cout << "m1.step1(1) = " << m1.step1(1) << endl;

	namedWindow("result_image", WINDOW_FULLSCREEN);
	imshow("result_image", m1);

	waitKey(0);

	return 0;
}

以上是关于Opencv中数据结构Mat的相关属性的主要内容,如果未能解决你的问题,请参考以下文章

详解OpenCV的Mat类(构造方法初始化方法常用属性常用成员函数常用操作)

OpenCV Mat类常用成员属性和成员方法

OpenCV Mat类常用成员属性和成员方法

OpenCV-Mat结构详解

OpenCV实战——OpenCV核心数据结构

[OpenCV学习笔记2][Mat数据类型和操作]