[OpenCV学习笔记2][Mat数据类型和操作]
[Mat数据类型和基本操作]
®.运行环境:Linux(RedHat+OpenCV3.0)
1.Mat的作用:
Mat类用于表示一个多维的单通道或者多通道的稠密数组。能够用来保存实数或复数的向量、矩阵,灰度或彩色图像,立体元素,点云,张量以及直方图(高维的直方图使用SparseMat保存比较好)。简而言之,Mat就是用来保存多维的矩阵的。
2.Mat的常见属性:
- data:
uchar类型的指针,指向Mat数据矩阵的首地址。可以理解为标示一个房屋的门牌号;
- dims:
Mat矩阵的维度,若Mat是一个二维矩阵,则dims=2,三维则dims=3,大多数情况下处理的都是二维矩阵,是一 个平面上的矩阵。
可以理解为房屋是一个一层的平房,三维或更多维的则是多层楼房;
- rows:
Mat矩阵的行数。可理解为房屋内房间行数;
- cols:
Mat矩阵的列数。可理解为房屋内房间列数;
- size():
首先size是一个结构体,定义了Mat矩阵内数据的分布形式,数值上有关系式:
image.size().width==image.cols; image.size().height==image.rows
可以理解为房屋内房间的整体布局,这其中包括了房间分别在行列上分布的数量信息;
- channels():
Mat矩阵元素拥有的通道数。例如常见的RGB彩色图像,channels==3;而灰度图像只有一个灰度分量信息, channels==1。
可以理解为每个房间内放有多少床位,3通道的放了3张床,单通道的放了1张床;
- depth:
用来度量每一个像素中每一个通道的精度,但它本身与图像的通道数无关!depth数值越大,精度越高。在 Opencv中,Mat.depth()得到的是一个0~6的数字,分别代表不同的位数,对应关系如下:
enum{CV_8U=0,CV_8S=1,CV_16U=2,CV_16S=3,CV_32S=4,CV_32F=5,CV_64F=6}
其中U是unsigned的意思,S表示signed,也就是有符号和无符号数。
可以理解为房间内每张床可以睡多少人,这个跟房间内有多少床并无关系;
- elemSize:
elem是element(元素)的缩写,表示矩阵中每一个元素的数据大小,如果Mat中的数据类型是CV_8UC1,那么 elemSize==1;如果是CV_8UC3或CV_8SC3,那么elemSize==3;如果是CV_16UC3或者CV_16SC3,那么 elemSize==6;即elemSize是以8位(一个字节)为一个单位,乘以通道数和8位的整数倍;
可以理解为整个房间可以睡多少人,这个时候就得累计上房间内所有床位数(通道)和每张床的容纳量了;
- elemSize1:
elemSize加上一个“1”构成了elemSize1这个属性,1可以认为是元素内1个通道的意思,这样从命名上拆分后就很 容易解释这个属性了:表示Mat矩阵中每一个元素单个通道的数据大小,以字节为一个单位,所以有:
eleSize1==elemSize/channels;
- step:
可以理解为Mat矩阵中每一行的“步长”,以字节为基本单位,每一行中所有元素的字节总量,是累计了一行中所 有元素、所有通道、所有通道的elemSize1之后的值;
- step1():
以字节为基本单位,Mat矩阵中每一个像素的大小,累计了所有通道、所有通道的elemSize1之后的值,所以有:
step1==step/elemSize1;
- type:
Mat矩阵的类型,包含有矩阵中元素的类型以及通道数信息,type的命名格式为CV_(位数)+(数据类型)+(通道数),所有取值如下:
3.矩阵的构造、初始化、释放
注:1、在程序的最开始加上: using namespace cv; 2、把Mat改为 cv::Mat (由于本人不会C++所有开始有点没明白如何使用函数)
创建Mat类的方式:1.构造函数 2.create()函数创建对象3.从已有的数据源初始化
1.构造函数
Mat::Mat()
无参数构造方法;
Mat::Mat(int rows, int cols, int type)
创建行数为 rows,列数为col,类型为type的图像;
Mat::Mat(Size size, int type)
创建大小为 size,类型为type的图像;
Mat::Mat(int rows, int cols, int type, const Scalar& s)
创建行数为rows,列数为col,类型为type的图像,并将所有元素初始化为值s;
Mat::Mat(Size size, int type, const Scalar& s)
创建大小为 size,类型为type的图像,并将所有元素初始化为值s
Mat::Mat(const Mat& m)
将 m 赋值给新创建的对象,此处不会对图像数据进行复制, m 和新对象共用图像数据;
Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
创建行数为 rows,列数为col,类型为type的图像,此构造函数不创建图像数据所需内存,而是直接使用data所指内存,图像的行步长由step指定。
Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)
创建大小为 size,类型为type的图像,此构造函数不创建图像数据所需内存,而是直接使用data所指内存,图像的行步长由step指定。
Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange)
创建的新图像为 m 的一部分,具体的范围由 rowRange 和 colRange 指定,此构造函数也不进行图像数据的复制操作,新图像与m共用图像数据;
Mat::Mat(const Mat& m, const Rect& roi)
创建的新图像为 m 的一部分,具体的范围 roi 指定,此构造函数也不进行图像数据的复制操作,新图像与m共用图像数据。
type的类型有CV_8UC1,CV_16SC1,…,CV_64FC4等。里面的8U表示8位无符号整数,16S表示16位有符号整数,64F表示64位浮点数(即double类型);C后面的数表示通道数,例如C1表示一个通道的图像,C4表示4个通道的图像,以此类推。
如果你需要更多的通道数,需要用宏CV_8UC(n),例如:Mat M(3,2, CV_8UC(5));//创建行数为3,列数为2,通道数为5的图像
计算机视觉中,图像的读取是图像处理的基础,图像就是一系列像素值,OpenCV使用数据结构cv::Mat来存储图像。cv::Mat是一个矩阵类,矩阵中每一个元素都代表一个像素,对于灰度图像,像素用8位无符号数,0表示黑色,255表示白色。对于彩色像素而言,每个像素需要三位这样的8位无符号数来表示,即三个通道(R,G,B),矩阵则依次存储一个像素的三个通道的值,然后再存储下一个像素点。
cv::Mat中,
cols代表图像的宽度(图像的列数),
rows代表图像的高度(图像的行数),
step代表以字节为单位的图像的有效宽度,
elemSize返回像素的大小,
channels()方法返回图像的通道数,
total函数返回图像的像素数。
像素的大小 = 颜色大小(字节)*通道数,
比如:
三通道short型矩阵(CV_16SC3)的大小为2*3 = 6,
三通道Byte型矩阵(CV_8UC3)的大小为1*3= 3,像素的channels方法返回图像的通道数,total函数返回图像的像素数。
RGB图像的颜色数目是256*256*256,本文对图像进行量化,缩减颜色数目到256的1/8(即32*32*32)为目标,分别利用一下几种方法实现,比较几种方法的安全和效率。
方法一:使用Mat的成员函数ptr<>()
cv::Mat中提供ptr函数访问任意一行像素的首地址,特别方便图像的一行一行的横向访问,如果需要一列一列的纵向访问图像,就稍微麻烦一点。但是ptr访问效率比较高,程序也比较安全,有越界判断。
- int nl = image.rows; //行数
- int nc = image.cols * image.channels();
- for (int j = 0; j<nl; j++)
- {
- uchar* data = image.ptr<uchar>(j);
- for (int i = 0; i<nc; i++)
- {
- data[i] = data[i] / div*div + div / 2;
- }
- }
方法二:使用迭代器遍历图像
cv::Mat同样有标准模板库(STL),可以使用迭代器访问数据。
用迭代器来遍历图像像素,可简化过程降低出错的机会,比较安全,不过效率较低;如果想避免修改输入图像实例cv::Mat,可采用const_iterator。iterator有两种调用方法,cv::MatIterator_<cv::Vec3b>it;cv::Mat_<cv::Vec3b>::iterator it;中间cv::Vec3b是因为图像是彩色图像,3通道,cv::Vec3b可以代表一个像素。
- cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();
- cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();
- for (; it != itend; ++it)
- {
- (*it)[0] = (*it)[0] / div*div + div / 2;
- (*it)[1] = (*it)[1] / div*div + div / 2;
- (*it)[2] = (*it)[2] / div*div + div / 2;
- }
方法三:使用Mat的成员函数at<>()
cv::Mat也是向量,可以使at方法取值,使用调用方法image.at<cv::Vec3b>(j,i),at方法方便,直接给i,j赋值就可以随意访问图像中任何一个像素,其中j表示第j行,i表示该行第i个像素。但是at方法效率是这3中访问方法中最慢的一个,所以如果遍历图像或者访问像素比较多时,建议不要使用这个方法,毕竟程序的效率还是比程序的可读性要重要的。下面是完整的调用方法,其运行时间在下面会介绍。
- for (int j = 0; j< image.rows; j++)
- {
- for (int i = 0; i< image.cols; i++)
- {
- image.at<cv::Vec3b>(j, i)[0] = image.at<cv::Vec3b>(j, i)[0] / div*div + div / 2;
- image.at<cv::Vec3b>(j, i)[1] = image.at<cv::Vec3b>(j, i)[1] / div*div + div / 2;
- image.at<cv::Vec3b>(j, i)[2] = image.at<cv::Vec3b>(j, i)[2] / div*div + div / 2;
- } // end of line
- }
注意:使用at函数时,应该知道矩阵元素的类型和通道数,根据矩阵元素类型和通道数来确定at函数传递的类型,使用的是Vec3b这个元素类型,他是一个包含3个unsigned char类型向量。之所以采用这个类型来接受at的返回值,是因为,我们的矩阵im是3通道,类型为unsigned char类型
完整实例:
- #include <iostream>
- #include < opencv.hpp>
- using namespace cv;
- using namespace std;
- int main()
- {
- //新建一个uchar类型的3通道矩阵
- Mat img(5, 3, CV_8UC3, Scalar(50,50,50));
- cout << img.rows << endl; //5
- cout << img.cols << endl; //3
- cout << img.channels() << endl; //3
- cout << img.depth() << endl; //CV_8U 0
- cout << img.dims << endl; //2
- cout << img.elemSize() << endl; //1 * 3,一个位置,三个通道的CV_8U
- cout << img.elemSize1() << endl; //1
- cout << img.size[0] << endl; //5
- cout << img.size[1] << endl; //3
- cout << img.step[0] << endl; //3 * ( 1 * 3 )
- cout << img.step[1] << endl; //1 * 3
- cout << img.step1(0) << endl; //3 * 3
- cout << img.step1(1) << endl; //3
- cout << img.total() << endl; //3*5
- //-------------------------------------- 地址运算 --------------------------------//
- for (int row = 0; row < img.rows; row++)
- {
- for (int col = 0; col < img.cols; col++)
- {
- //[row, col]像素的第 1 通道地址被 * 解析(blue通道)
- *(img.data + img.step[0] * row + img.step[1] * col) += 15;
- //[row, col]像素的第 2 通道地址被 * 解析(green通道)
- *(img.data + img.step[0] * row + img.step[1] * col + img.elemSize1()) += 15;
- //[row, col]像素的第 3 通道地址被 * 解析(red通道)
- *(img.data + img.step[0] * row + img.step[1] * col + img.elemSize1() * 2) += 15;
- }
- }
- cout << img << endl;
- //-------------------------------------- Mat的成员函数at<>( ) --------------------------------//
- for (int row = 0; row < img.rows; row++)
- {
- for (int col = 0; col < img.cols; col++)
- {
- img.at<Vec3b>(row, col) = Vec3b(0, 0, 0);
- }
- }
- cout << img << endl;
- //-------------------------------------- 使用Mat的成员函数ptr<>() --------------------------------//
- for (int row = 0; row < img.rows; row++)
- {
- // data 是 uchar* 类型的, m.ptr(row) 返回第 row 行数据的首地址
- // 需要注意的是该行数据是按顺序存放的,也就是对于一个 3 通道的 Mat, 一个像素3个通道值, [B,G,R][B,G,R][B,G,R]...
- // 所以一行长度为:sizeof(uchar) * m.cols * m.channels() 个字节
- uchar* data = img.ptr(row);
- for (int col = 0; col < img.cols; col++)
- {
- data[col * 3] = 50; //第row行的第col个像素点的第一个通道值 Blue
- data[col * 3 + 1] = 50; // Green
- data[col * 3 + 2] = 50; // Red
- }
- }
- cout << img << endl;
- Vec3b *pix(NULL);
- for (int r = 0; r < img.rows; r++)
- {
- pix = img.ptr<Vec3b>(r);
- for (int c = 0; c < img.cols; c++)
- {
- pix[c] = pix[c] * 2;
- }
- }
- cout << img << endl;
- //-------------------------------------- 使用Mat的成员函数ptr<>() --------------------------------//
- MatIterator_<Vec3b> it_im, itEnd_im;
- it_im = img.begin<Vec3b>();
- itEnd_im = img.end<Vec3b>();
- for(; it_im != itEnd_im; it_im++)
- {
- *it_im = (*it_im) * 2;
- }
- cout << img << endl;
- cvWaitKey();
- return 0;
- }
时间:2018.1.21.1.41{失眠夜总结OpenCV吧想起了一段话送给自己}
{Can not force others to love themselves only to make themselves worthy of love ,and the rely on fate.}
@晚安 Liu在身边 27
[OpenCV学习笔记2][Mat数据类型和操作]的更多相关文章
- OpenCV学习笔记:MAT解析
在2001年刚刚出现的时候,OpenCV基于 C 语言接口而建.为了在内存(memory)中存放图像,当时采用名为 IplImage 的C语言结构体,时至今日这仍出现在大多数的旧版教程和教学材料.但这 ...
- OpenCV学习笔记:矩阵的掩码操作
矩阵的掩码操作很简单.其思想是:根据掩码矩阵(也称作核)重新计算图像中每个像素的值.掩码矩阵中的值表示近邻像素值(包括该像素自身的值)对新像素值有多大影响.从数学观点看,我们用自己设置的权值,对像素邻 ...
- OpenCV学习笔记(十) 直方图操作
直方图计算 直方图可以统计的不仅仅是颜色灰度, 它可以统计任何图像特征 (如 梯度, 方向等等).直方图的一些具体细节: dims: 需要统计的特征的数目, 在上例中, dims = 1 因为我们仅仅 ...
- opencv学习笔记(五)----图像的形态学操作
图像的形态学操作有基本的腐蚀和膨胀操作和其余扩展形态学变换操作(高级操作)-----开运算,闭运算,礼帽(顶帽)操作,黑帽操作...(主要也是为了去噪声,改善图像) 形态学操作都是用于处理二值图像(其 ...
- OpenCV学习笔记(十一) 轮廓操作
在图像中寻找轮廓 首先利用Canny算子检测图像的边缘,再利用Canny算子的输出作为 寻找轮廓函数 findContours 的输入.最后用函数 drawContours 画出轮廓.边界Counto ...
- opencv学习笔记(三)基本数据类型
opencv学习笔记(三)基本数据类型 类:DataType 将C++数据类型转换为对应的opencv数据类型 OpenCV原始数据类型的特征模版.OpenCV的原始数据类型包括unsigned ch ...
- opencv学习笔记(一)IplImage, CvMat, Mat 的关系
opencv学习笔记(一)IplImage, CvMat, Mat 的关系 opencv中常见的与图像操作有关的数据容器有Mat,cvMat和IplImage,这三种类型都可以代表和显示图像,但是,M ...
- OpenCV 学习笔记(模板匹配)
OpenCV 学习笔记(模板匹配) 模板匹配是在一幅图像中寻找一个特定目标的方法之一.这种方法的原理非常简单,遍历图像中的每一个可能的位置,比较各处与模板是否"相似",当相似度足够 ...
- 【opencv学习笔记六】图像的ROI区域选择与复制
图像的数据量还是比较大的,对整张图片进行处理会影响我们的处理效率,因此常常只对图像中我们需要的部分进行处理,也就是感兴趣区域ROI.今天我们来看一下如何设置图像的感兴趣区域ROI.以及对ROI区域图像 ...
随机推荐
- 程序员的自我救赎---1.4.1:核心框架讲解(DAL)
<前言> (一) Winner2.0 框架基础分析 (二)PLSQL报表系统 (三)SSO单点登录 (四) 短信中心与消息中心 (五)钱包系统 (六)GPU支付中心 (七)权限系统 (八) ...
- lesson - 2 yum /单用户/救援模式/Linux 启动
课程大纲:1. yum使用yum 是一个在线安装软件包的工具,它可以帮我们解决软件包的依赖,这个日后会详细介绍.我们介绍了以下几个用法:yum list 这个命令可以列出所有安装过和未安装的软 ...
- 环形进度条的实现方法总结和动态时钟绘制(CSS3、SVG、Canvas)
缘由: 在某一个游戏公司的笔试中,最后一道大题是,“用CSS3实现根据动态显示时间和环形进度[效果如下图所示],且每个圆环的颜色不一样,不需要考虑IE6~8的兼容性”.当时第一想法是用SVG,因为SV ...
- php 面向对象三大特点:封装、继承、多态
在讲解这三大特性前,我们先讲访问修饰符. php中有3中访问修饰符:public protected private: public:表示公有的:可在本类.子类.对象实例中访问. protected: ...
- angular4.0如何引入外部插件1:import方案
引入外部插件是项目中非常重要的环节.因为部分插件以js语法写的,而ng4用的是ts语法,所以在引入时需要配置. Step1:引入swiper插件的js文件[css在下面会讲到,先别急] 很重要的意见: ...
- timeline时间轴进度“群英荟萃”
timeline时间轴进度“群英荟萃” 是日,无论PC项目还是APP,都涉及到了通常称谓的“时间轴”UI展现布局.产品和设计师都喜欢横向.纵向的时间轴来传达产品的寓意.如此,如斯!总结一套 time ...
- ES6 Generators并发
ES6 Generators系列: ES6 Generators基本概念 深入研究ES6 Generators ES6 Generators的异步应用 ES6 Generators并发 如果你已经读过 ...
- windows 异常处理
为了程序的健壮性,windows 中提供了异常处理机制,称为结构化异常,异常一般分为硬件异常和软件异常,硬件异常一般是指在执行机器指令时发生的异常,比如试图向一个拥有只读保护的页面写入内容,或者是硬件 ...
- Django源码分析之程序执行入口分析
一般我们开启一个django项目,最简单的方法是进入project 目录,这时目录结构是这样的 然后我们执行python manage.py runserver,程序就开始执行了. 那django是如 ...
- 常见的VPS虚拟化架构:OpenVZ、Xen、Hyper-V、KVM、VMWare OpenVZ
OpenVZ OpenVZ特点是,它是直接调用母服务器的内核,所以会导致部分软件无法使用,以及部分内核文件是无法修改. OpenVZ适用人群:新手.低预算客户 OpenVZ注意事项:资源不是自己独有的 ...