Mat数据结构

一开始OpenCV是基于C语言的,在比较早的教材例如《学习OpenCV》中,讲解的存储图像的数据结构还是IplImage,这样需要手动管理内存。现在存储图像的基本数据结构是Mat

Mat是opencv中保存图像数据的基本容器。其定义如下:

class CV_EXPORTS Mat
{
public:
// ... a lot of methods ...
... /*! includes several bit-fields:
- the magic signature
- continuity flag
- depth
- number of channels
*/
int flags;
//! the array dimensionality, >= 2
int dims;
//! the number of rows and columns or (-1, -1) when the array has more than 2 dimensions
int rows, cols;
//! pointer to the data
uchar* data; //! pointer to the reference counter;
// when array points to user-allocated data, the pointer is NULL
int* refcount; // other members
...
};

Mat类可以表示n维的单通道或多通道数组,它可以存储实数/复数的向量和矩阵,单色或彩色图像等。向量\(M\)的布局是由数组\(M.step[]\)决定的,元素\((i_0, ..., i_{M.dims-1})\)的地址为(其中\(0 \leq i_k < M.size[k]\)):

\[addr(M_{i_0, ..., i_{M.dims-1}})=M.data + M.step[0]*i_0 + M.step[1]*i_1+...+M.step[M.dims-1]*i_{M.dims-1}
\]

Mat对象的数据布局和CvMat、Numpy等兼容,实际上它和以step(strides)方式计算像素地址方式的数据结构兼容。

在上面的数据结构可以看出,Mat数据结构中指针信息可以共享,即矩阵头信息独立,矩阵数据可以共享,使用引用计数器,类似智能指针。这样用户使用时,用户可以分配Mat的头信息,共享数据信息,并在原地处理信息,这样可以极大的节省内存。

创建Mat对象

1、使用构造函数Mat(nrows, ncols, type[, fillValue])或者create(nrows, ncols, type)

这样可以创建一个nrows行,ncol列的矩阵,类型为type。例如CV_8UC1表示8位单通道, CV_32FC2表示双通道32位floating-point双通道。

// make a 7x7 complex matrix filled with 1+3j.
Mat M(7,7,CV_32FC2,Scalar(1,3));
// and now turn M to a 100x60 15-channel 8-bit matrix.
// The old content will be deallocated
M.create(100,60,CV_8UC(15));

对于type,格式为CV_位数+数值类型+C通道数,例如:

CV_8UC1表示:单通道阵列,8bit无符号整数

CV_8US2表示:2通道阵列,8bit有符号整数)

2、创建多维矩阵

// create a 100x100x100 8-bit array
int sz[] = {100, 100, 100};
Mat bigCube(3, sz, CV_8U, Scalar::all(0));

3、使用拷贝构造函数或赋值操作符时,只是创建了矩阵头,共享了矩阵信息,时间复杂度为O(1)。Mat::clone()函数是深拷贝,拷贝了Mat的所有信息。

4、只创建信息头部分,时间复杂度为O(1),可以使用这个特征Mat局部信息:

// add the 5-th row, multiplied by 3 to the 3rd row
M.row(3) = M.row(3) + M.row(5)*3; // now copy the 7-th column to the 1-st column
// M.col(1) = M.col(7); // this will not work
Mat M1 = M.col(1);
M.col(7).copyTo(M1); // create a new 320x240 image
Mat img(Size(320,240),CV_8UC3);
// select a ROI
Mat roi(img, Rect(10,10,100,100));
// fill the ROI with (0,255,0) (which is green in RGB space);
// the original 320x240 image will be modified
roi = Scalar(0,255,0);

可以创建ROI(Region of interest)区域

Mat A = Mat::eye(10, 10, CV_32S);
// extracts A columns, 1 (inclusive) to 3 (exclusive).
Mat B = A(Range::all(), Range(1, 3));
// extracts B rows, 5 (inclusive) to 9 (exclusive).
// that is, C ~ A(Range(5, 9), Range(1, 3))
Mat C = B(Range(5, 9), Range::all());
Size size; Point ofs;
C.locateROI(size, ofs);
// size will be (width=10,height=10) and the ofs will be (x=1, y=5)

5、使用用户开辟的数据创建Mat的header部分

6、使用Mat::eye(), Mat::zeros(), Mat::ones()创建矩阵;或使用Mat_<destination_type>()

Mat E = Mat::ones(2, 2, CV_32F);
Mat O = (Mat_<float>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);

矩阵运算

Mat支持矩阵运算。A、B表示Mat类型对象,s表示标量,alpha表示实数,支持以下运算:

** 加:A+B, A-B, A+s, s+B,-A

** 缩放:Aalpha

** 对应像素相乘/除:A.mul(B),A/B, alpha/A。这里要求A、B大小相等,数据类型通道数一致。

** 转置:A.t(),相当于A^T

** 求逆和伪逆矩阵。A.inv([method])宝石求逆,A.inv([method])
B表示求X,其中X满足AX=B

** 逻辑位操作 A & B, ~A

**内积:A.cross(B), A.dot(B),表示对应像素相乘,求和。

常用接口

1、C++: size_t Mat::total() const

返回像素总数

2、C++: int Mat::depth() const

返回矩阵type类型对应的数值。

3、C++: int Mat::channels() const

返回通道数

4、C++: bool Mat::empty() const

Mat::total() = 0 或 Mat::data = NULL,则方法返回 true


访问Mat中的像素

Mat中存储的图像像素,具体如何存储取决于使用的颜色模型和通道数,例如RGB图像对应的存储矩阵如下

RGB存储的子列通道是反过来的:BGR。如果内存足够大,可以连续存储,通过方法Mat::isContinuous()可以判断矩阵是否连续。

访问图像的像素,即访问某位置像素在内存中对应的地址。以提取彩色RGB图像某一通道图像为例:可以有如下方法:

1、使用指针

Mat存储的图像,每一行都是连续的,可以取得每一行开头指针来访问图像像素。例如提取一副图像中R通道的图像,G、B通道像素全部置零,可以获取每一行开头的指针,使用指针遍历每一行的所有像素。如果图像在内存中的存储是连续的,还可以一次遍历所有像素。

/*
original:原图像
new_image:新图像
channel:提取的通道 0 1 2分别表示RGB
*/
void ExtractRGB(Mat& original, Mat& new_image, int channel){
CV_Assert(channel < 3);
// accept only char type matrices
CV_Assert(original.depth() != sizeof(uchar)); int channels = original.channels();
//只接受3通道图像
CV_Assert(channels == 3); new_image = original.clone(); int nRows = new_image.rows;
int nCols = new_image.cols * channels; if (new_image.isContinuous())
{
nCols *= nRows;
nRows = 1;
} int i, j;
uchar* p;
for (i = 0; i < nRows; ++i)
{
p = new_image.ptr<uchar>(i);
for (j = 0; j < nCols; ++j)
{
if (0 == (j + 1 + channel) % 3){
//保留
}
else
p[j] = 0; }
}
return ;
}

2、使用迭代器

在上面的使用裸指针的方法,不安全,不注意的话会造成内存越界访问。迭代器是封装了的指针,相对指针更加安全。

/*
original:原图像
new_image:新图像
channel:提取的通道 0 1 2分别表示BGR
*/
void ExtractRGBIterator(Mat& original, Mat& new_image, int channel){
CV_Assert(channel < 3);
// accept only char type matrices
CV_Assert(original.depth() != sizeof(uchar)); int channels = original.channels();
//只接受3通道图像
CV_Assert(channels == 3); new_image = original.clone(); int i = (channel + 1) % 3;
int j = (channel + 2) % 3; MatIterator_<Vec3b> it, end;
//3通道的图像,迭代器对应三个像素(*it)[0]、(*it)[1]、(*it)[2]
for (it = new_image.begin<Vec3b>(), end = new_image.end<Vec3b>(); it != end; ++it)
{
(*it)[i] = 0;
(*it)[j] = 0;
}
}

3、实时计算

如果想随机获取某一位置像素,例如(i,j)出的像素,要动态实时计算其偏移,OpenCV提供相关接口

/*
original:原图像
new_image:新图像
channel:提取的通道 0 1 2分别表示BGR
*/
void ExtractRGBRandomAcccess(Mat& original, Mat& new_image, int channel){
CV_Assert(channel < 3);
// accept only char type matrices
CV_Assert(original.depth() != sizeof(uchar)); int channels = original.channels();
//只接受3通道图像
CV_Assert(channels == 3); new_image = original.clone(); Mat_<Vec3b> _I = new_image; int m = (channel + 1) % 3;
int n = (channel + 2) % 3; for (int i = 0; i < new_image.rows; ++i)
for (int j = 0; j < new_image.cols; ++j)
{
_I(i, j)[m] = 0;
_I(i, j)[n] = 0; }
}

以上3个方法中,第一种最快,第三种最慢;因为第三种是随机访问像素使用的,每次都会计算(i,j)像素对应的地址。

OpenCV(2)-Mat数据结构及访问Mat中像素的更多相关文章

  1. 【opencv学习笔记七】访问图像中的像素与图像亮度对比度调整

    今天我们来看一下如何访问图像的像素,以及如何改变图像的亮度与对比度. 在之前我们先来看一下图像矩阵数据的排列方式.我们以一个简单的矩阵来说明: 对单通道图像排列如下: 对于双通道图像排列如下: 那么对 ...

  2. 《OpenCV3编程入门》访问图像中像素的三类方法

    ·方法一 指针访问:C操作符[ ]; ·方法二 迭代器iterator; ·方法三 动态地址计算; #include <opencv2/core/core.hpp> #include &l ...

  3. Opencv笔记(七)——访问与操作像素

    一.获取矩阵的元素 1.获取三维矩阵img[i,j]处的元素 (b,g,r) = image[i,j],image大小为:MxNxK. 2.获取三维矩阵的子矩阵的全部元素 newimage = ima ...

  4. 访问图像中的像素[OpenCV 笔记16]

    再更一发好久没更过的OpenCV,不过其实写到这个部分对计算机视觉算法有所了解的应该可以做到用什么查什么了,所以后面可能会更的慢一点吧,既然开了新坑,还是机器学习更有研究价值吧... 图像在内存中的存 ...

  5. (转)OpenCV 访问Mat中每个像素的值

    转自:http://blog.csdn.net/xiaowei_cqu/article/details/19839019 在<OpenCV 2 Computer Vision Applicati ...

  6. OpenCV中Mat数据的访问报错

    最近再写一段程序的时候,要访问Mat中的元素.在定义Mat型数据的时候,用 Mat ObjectPoints(48,3,CV_32FC1,0) 对其进行初始化后,用at进行访问时报内存错误. Mat ...

  7. 【转】OpenCV Mat数据结构

    转载自xiahouzuoxin原文 OpenCV基础篇之Mat数据结构 程序及分析 /* * FileName : MatObj.cpp * Author : xiahouzuoxin @163.co ...

  8. opencv学习笔记(九)Mat 访问图像像素的值

    对图像的像素进行访问,可以实现空间增强,反色,大部分图像特效系列都是基于像素操作的.图像容器Mat是一个矩阵的形式,一般情况下是二维的.单通道灰度图一般存放的是<uchar>类型,其数据存 ...

  9. opencv 3 core组件进阶(1 访问图像中的像素)

    访问图像像素的三类方法 ·方法一 指针访问:C操作符[ ]; ·方法二 迭代器iterator; ·方法三 动态地址计算. #include <opencv2/core/core.hpp> ...

随机推荐

  1. 北京Uber优步司机奖励政策(2月6日)

    滴快车单单2.5倍,注册地址:http://www.udache.com/ 如何注册Uber司机(全国版最新最详细注册流程)/月入2万/不用抢单:http://www.cnblogs.com/mfry ...

  2. codeforces 721C (拓扑+dp)

    题意就是某个人去游览,起点是1点,终点是n点,他总的游览时间不能超过t,第一行给你3个数字,点的个数n,边的个数m,时间t,然后底下m行数据,每行代表一条边,边的起点,终点和权值(走过去花的时间),然 ...

  3. PAT 1033. To Fill or Not to Fill (25)

    题目地址:http://pat.zju.edu.cn/contests/pat-a-practise/1033 此题是一道贪心算法题,难度较大,关键在于贪心策略的选择: #include <cs ...

  4. IOS GCD 使用 (二)

     上一节,主要介绍了GCD的基本的概念,这节将用代码深入详细介绍GCD的使用. 一  使用介绍    GCD的使用主要分为三步:创建代码块;选择或创建合适的分发队列;(同步.异步方式)向分发队列提交任 ...

  5. C#性能优化的一些技巧

    博客搬到了fresky.github.io - Dawei XU,请各位看官挪步.最新的一篇是:C#性能优化的一些技巧.

  6. JavaScript高级程序设计—阅读笔记(第一部分)

    第一章 JavaScript简介1.JavaScript组成(核心(ECMAScript),文档对象模型(DOM),浏览器对象模型(BOM))2.文档对象模型(DOM),提供访问和操作网页内容的方法和 ...

  7. httpClient实现获取网络信息

    自己实现的抓取网络信息 package util; import java.io.IOException; import java.lang.reflect.Field; import java.ma ...

  8. RSA加密解密操作

    国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:http://jdb.jiudingcapital.com/phone.html内部邀请码:C8E245J (不写邀请码,没有现金送)国内私 ...

  9. hdu1002大数相加

    A + B Problem II Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Tot ...

  10. Environment variable:"PATH" 状态 失败

    问题截图: 问题内容: 未能满足某些最低安装要求.请复查并修复下表中列出的问题,然后重新检查系统. Checks    Environment Variable: "PATH"  ...