Opencv学习----基本结构对象-cv::Mat-定义

4.2.1 定义

Mat表示一个n维密集的数值单通道或多通道阵列。它可以用来存储实数或复数的向量和矩阵、灰度或彩色图像、体素体积、向量场、点云、张量、直方图(但是,非常高维的直方图可以更好地存储在SparseMat)。数组的数据布局由数组M定义M.step[],因此元素的地址(i0,...,iM.dims−1)0≤ik<M.size[k]

对于二维数组,将上述公式简化为:

请注意M.step[i]>=M.step[i+1](事实上M.step[i]>= M.step[i+1]*M.size[i+1])。这意味着逐行存储二维矩阵,逐行存储三维矩阵,等等。M.step[M.dims-1]是最小的并且总是等于元素大小M.elemSize()。

因此,Mat中的数据布局与OpenCV 1.x中的CvMat,IplImage和CvMatND类型完全兼容。它还与标准工具包和SDK中的大多数密集数组类型兼容,例如:Numpy (ndarray)、Win32(独立设备位图)和其他类型,即任何使用steps (or strides)计算像素位置的数组。由于这种兼容性,可以为用户分配的数据制作一个Mat头,并使用OpenCV函数对其进行处理。

创建Mat对象有许多不同的方法。最佳选项如下:

(1)使用create(nrows, ncols, type)方法或类似的Mat(nrows, ncols, type[, fillValue])构造函数。分配指定大小和类型的新数组。类型具有与cvCreateMat方法中相同的含义。例如,CV_8UC1表示8位单通道数组,CV_32FC2表示2通道(复杂)浮点数组,等等。

// 创建一个填充1 + 3j的7x7复杂矩阵
Mat M(7,7,CV_32FC2,Scalar(1,3));
// 现在将M转换为100x60 15通道8位矩阵。
// 旧内容将被取消分配
M.create(100,60,CV_8UC(15));

create()在当前数组的形状或类型与指定数组不同时分配一个新数组。

(2)创建一个多维数组

// 创建一个100x100x100 8位阵列
int sz[] = {100, 100, 100};
Mat bigCube(3, sz, CV_8U, Scalar::all(0));

它将1维数传递给Mat构造函数,并创建二维的数组将,列数设置为1。因此,Mat::dims总是>= 2(当数组为空时是0)。

(3)使用拷贝构造函数或赋值运算符,其中可以在右侧有数组或表达式(参见下面)。数组赋值是一个O(1)操作,因为它只复制头并增加引用计数器。clone()方法可用于在需要时获取数组的完整(深度)副本。

(4)为另一个数组的一部分构造一个标头。它可以是单行、单列、多行、多列、数组中的矩形区域(在代数中称为次要区域)或对角线。这些操作也是O(1),因为新头引用了相同的数据。你可以使用这个功能修改数组的一部分,例如:

// 第三行修改为加上第五行乘以3
M.row(3) = M.row(3) + M.row(5)*3;
//将第7列复制到第1列
// M.col(1) = M.col(7); //无效果
Mat M1 = M.col(1);
M.col(7).copyTo(M1);
// 创建一个新的320x240的img
Mat img(Size(320,240),CV_8UC3);
// 从图中选择指定的区域
Mat roi(img, Rect(10,10,100,100));
// 用(0,255,0)(RGB空间为绿色)填充ROI; 
// 将修改原始的320x240图像
roi = Scalar(0,255,0);

由于添加了datastart和dataend成员,可以使用locateROI()计算主容器数组中的相对子数组位置:

Mat A = Mat::eye(10, 10, CV_32S);
// 提取A列, 包括第1列,不包括第3列
Mat B = A(Range::all(), Range(1, 3));
// 提取B行, 包括第5行,不包括第9行
Mat C = B(Range(5, 9), Range::all());
Size size; 
Point ofs;
C.locateROI(size, ofs);
// size(width = 10,height = 10),ofs将是(x = 1,y = 5)

对于整个矩阵,如果需要深度复制,可以使用提取的子矩阵的clone()方法。

(5)根据用户分配的数据创建头。

a、使用OpenCV处理“外部”数据(例如,当您实现DirectShow*过滤器或gstreamer的处理模块时,等等)。例如:

void process_video_frame(const unsigned char* pixels, int width, int height, int step)
{
    Mat img(height, width, CV_8UC3, pixels, step);
    GaussianBlur(img, img, Size(7,7), 1.5, 1.5);
}

b、快速初始化小矩阵和/或获得超快元素访问。

double m[3][3] = {{a, b, c}, {d, e, f}, {g, h, i}};
Mat M = Mat(3, 3, CV_64F, m).inv();

常见的情况是将用户分配的数据从CvMat和IplImage转换到Mat,函数cv::cvarrToMat将指针指向CvMat或IplImage,并使用可选标志指示是否复制数据。

(6)使用matlab样式的数组初始化器,zeros(), ones(), eye(), 例如:

//创建一个双精度单位矩阵,并与M相加.
M += Mat::eye(M.rows, M.cols, CV_64F);

(7)使用逗号分隔的初始化器:

// 创建一个3x3双精度单位矩阵
Mat M = (Mat_<double>(3,3) << 1, 0, 0, 0, 1, 0, 0, 0, 1);

使用这种方法,首先使用适当的参数调用Mat类的构造函数,然后只需输入<<操作符,后跟逗号分隔的值,这些值可以是常量、变量、表达式等等。另外,请注意避免编译错误所需的额外括号。

 

创建阵列后,它将通过引用计数机制自动管理。如果数组头是在用户分配的数据之上构建的,那么您应该自己处理数据。当没有人指向它时,数组数据被释放。如果要在调用数组析构函数之前释放数组头指向的数据,请使用Mat::release()。

关于数组类要学习的下一个重要内容是元素访问。Opencv手册已经描述了如何计算每个数组元素的地址。通常,我们不需要在代码中直接使用该公式。如果知道数组元素类型(可以使用Mat::type()方法检索),则可以访问二维数组的元素Mij:

M.at<double>(i,j) += 1.f;

假设这M是一个双精度浮点数组。对于不同数量的维度,该方法有几种变体。

 

   如果需要处理整个2D数组,最有效的方法是首先获取指向行的指针,然后只使用普通的C运算符[]:

//计算正矩阵元素的总和
// (假设M是双精度矩阵)
double sum=0;
for(int i = 0; i < M.rows; i++)
{
    const double* Mi = M.ptr<double>(i);
    for(int j = 0; j < M.cols; j++)
        sum += std::max(Mi[j], 0.);
}

有些操作,如上面的,实际上并不依赖于数组的形状。它们只是逐个处理一个数组的元素(或者来自多个具有相同坐标的数组的元素,例如数组加法)。这样的操作称为元素智能操作。检查所有输入/输出数组是否连续是有意义的,即每行末尾没有空格。如果是,将它们处理为一长行:

//计算正矩阵元素之和
double sum=0;
int cols = M.cols, rows = M.rows;
if(M.isContinuous())
{
    cols *= rows;
    rows = 1;
}
for(int i = 0; i < rows; i++)
{
    const double* Mi = M.ptr<double>(i);
    for(int j = 0; j < cols; j++)
        sum += std::max(Mi[j], 0.);
}

对于连续矩阵,外环体只执行一次。因此,开销更小,这在小矩阵的情况下尤其明显。

 

最后,还有一些stl风格的迭代器,它们足够聪明,可以跳过连续行之间的间隙:

// 计算正矩阵元素的和,基于迭代器的变量
double sum=0;
MatConstIterator_ it = M.begin(), it_end = M.end();
for(; it != it_end; ++it)
    sum += std::max(*it, 0.);

矩阵迭代器是随机访问迭代器,因此可以将它们传递给任何STL算法,包括std::sort()。

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

dylan55_you

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值