OpenCV从入门到放弃:摸鱼笔记(二)Mat类常用变量和像素访问
2017-11-22 17:21
731 查看
一、常用变量
Mat类中包含了很多的变量,比如用的比较多的rows,cols等。其中有几个变量和函数比较抽象,但是可以知道图像储存为Mat类的内部形式和本身的一些性质,有助于对Mat类的理解,step1(),step[],size,elemSize和elemSize1。type 表示Mat的类型,用CV_XXXX表示,如 CV_8UC1,CV_16SC3,CV_32FC3,分别表示8位无符号整型一通道,16位有符号整型三通道,32位浮点型三通道。在初始化Mat类的时候会需要使用到。当使用Mat.type()获得参数输出时,其输出为预设的一个整数表示上面的CV_XXXX的各种情况,具体往下看。
depth 表示Mat的深度,CV_XX表示,即为type类型表示去掉通道信息,上面三个即为CV_8U,CV_16S,CV_32F。使用Mat.depth输出时,同样输出为预设的整数,具体如下:
#define CV_8U 0
#define CV_8S 1
#define CV_16U 2
#define CV_16S 3
#define CV_32S 4
#define CV_32F 5
#define CV_64F 6
#define CV_USRTYPE1 7
深度共有八个,且以上预设数值对应Mat.type()输出的单通道,即对CV_8UC1类型Mat.type()输出即为0,若为多通道,则加上8*(通道数-1),如CV_8UC3则为16,CV_16SC3则为19。
step[0] 在二维图像的Mat中表示的是一行元素的大小,以字节为单位;
step[1] 在二维图像的Mat中表示的是一个元素的大小,以字节为单位;
step1(0) 在二维图像的Mat中表示的是每一行元素的通道数;
step1(1) 在二维图像的Mat中表示的是每一个元素的通道数;
(即step1位step的桂正华,step1 = step / elemSize,即将字节单位换成通道)
size[0]和size[1] 分别等同于rows和cols;
elemSize() 表示的是每个元素的大小 ,以字节为单位;
elemSize1() 表示的一个元素中每个通道的大小,以字节为单位。
二、像素访问
这里中只实践了两个方法,故只举出两个1、向量访问
Mat.at<cv::Vec3b>(row,col)[i] Mat.at<cv::Vec3b>(cv::Point(x,y))[i] //Point(x,y)亦为Point(col,row)两种均为通过向量访问,上面一种是通过row和col,即行数和列数定位,下面一种则是通过Point类的点的坐标定位。
此处强调两个点,无论是行列还是点坐标,在Mat里面均从(0,0)开始,也就是说第一行第一列为(0,0),坐标为Point(0,0)。
第二个点,行跟列分别对应y轴和x轴,即row对应y,col对用x,因此有上面注释部分。
2、data指针访问
uchar *data = img.data; b = data[row*img.step[0]+img.elemSize()*col]; g = data[row*img.step[0]+img.elemSize()*col+1]; r = data[row*img.step[0]+img.elemSize()*col+2];
data表示取出Mat的第一个数据的存储地址,row/col表示行/列,step[0]和elemeSize()上面讲过,分别表示每一行的字节数和每个元素的字节数
初始化了指针变量data为Mat的第一个数据地址之后,可以通过data[ i ] 的格式对每个元素进行访问,i 表示与第一个数据的相对地址字节数。
如上面代码表示,此处图片为CV_8UC3,即八位无符号三通道图像。则每个元素占三个字节,即elemSize()输出值为3,且元素数值按BGR的顺序储存。
当在第row行第col列的G通道时,i 应为 (第row行之前所有行占的字节数)+(第col列前面该行的元素所占的字节数)+(对应是第几个通道分量),即
row*img.step[0]+img.elemSize()*col+1
三、程序示例
#include<opencv2/core/core.hpp> #include<opencv2/highgui/highgui.hpp> #include<opencv2/imgproc/imgproc.hpp> #include<iostream> cv::Mat img = cv::imread("C:/Users/Administrator/Pictures/Roberto.jpg"); void on_Mouse(int event,int x,int y,int flags,void*) { int r,g,b; if(event == cv::EVENT_FLAG_LBUTTON) { int row = y,col = x; //鼠标函数返回的x,y从0开始,且行列计数均从0开始,x对应列,y对应行 double t1,t2; t1 = static_cast<double>(cv::getTickCount()); b = img.at<cv::Vec3b>(cv::Point(col,row))[0]; //用坐标定位 g = img.at<cv::Vec3b>(row,col)[1]; //用行列数定位 r = img.at<cv::Vec3b>(row,col)[2]; t1 = ((double)cv::getTickCount()-t1)/cv::getTickFrequency(); std::cout<<"方法一: "; std::cout<<"("<<r<<","<<g<<","<<b<<")"<<"\t"; std::cout<<"耗时: "<<t1*1000000<<" us"<<std::endl; t2 = static_cast<double>(cv::getTickCount()); uchar *data = img.data; b = data[row*img.step[0]+img.elemSize()*col]; g = data[row*img.step[0]+img.elemSize()*col+1]; r = data[row*img.step[0]+img.elemSize()*col+2];
t2 = ((double)cv::getTickCount()-t2)/cv::getTickFrequency(); std::cout<<"方法二: "; std::cout<<"("<<r<<","<<g<<","<<b<<")"<<"\t"; std::cout<<"耗时: "<<t2*1000000<<" us"<<std::endl; std::cout<<std::endl; } } int main() { std::cout<<"img.rows/行:\t\t\t"<<img.rows<<std::endl; std::cout<<"img.cols/列:\t\t\t"<<img.cols<<std::endl; std::cout<<"img.channels/通道数:\t\t"<<img.channels()<<std::endl; std::cout<<"img.type/Mat类型(显示预设常数):\t"<<img.type()<<std::endl; std::cout<<"img.depth/Mat深度(显示预设常数):"<<img.depth()<<std::endl; std::cout<<"img.step1(0)/一行元素的通道数:\t"<<img.step1(0)<<std::endl; std::cout<<"img.step1(1)/一个元素的通道数:\t"<<img.step1(1)<<std::endl; std::cout<<"img.step[0]/一行元素的字节数:\t"<<img.step[0]<<std::endl; std::cout<<"img.step[1]/一个元素的字节数:\t"<<img.step[1]<<std::endl; std::cout<<"即stpe1为规整化的step,step1 = step / elemSize1"<<std::endl; std::cout<<"img.size[0]:\t"<<img.size[0]<<std::endl; std::cout<<"img.size[1]:\t"<<img.size[1]<<std::endl; std::cout<<"img.elemSize/每个元素的字节数:\t"<<img.elemSize()<<std::endl; std::cout<<"img.elemSize1/每个通道的字节数:\t"<<img.elemSize1()<<std::endl; printf("\n"); imshow("Picture",img); cv::setMouseCallback("Picture",on_Mouse); std::cout<<"点击图片得到对应点像素的RGB值"<<std::endl; while(cv::waitKey(0)!=27); return 0; }
四、运行结果
五、总结
经过计算可以验证各个变量所表达的意思:示例所用图片type为CV_8UC3,则depth为CV_8U,则对应预设数值为0,通道数为3,则 0+8*(3-1) = 16。
step1(0)表示每一行元素的通道数,每行元素通道数 = 每一行的元素个数 * 每个元素的通道数 = 列数 * 通道数 = 808 * 3 = 2424。
因为为八位无符号整型,也就是说每个通道数值占一个字节,每个元素有三个通道数值,则每个元素占3个字节。因此elemSize1和elemSize均为1,step和step1的值也一样。
通过输出的耗时可以看出,用指针的方法方位像素所需用的时间明显比第一种用向量访问的方法要快很多。
相关文章推荐
- OpenCV从入门到放弃:摸鱼笔记(一)
- OpenCV学习笔记九——访问图像中的像素
- OpenCV从入门到放弃(五):像素!
- OPENCV学习笔记2-1_像素访问案例(椒盐噪点)
- 【opencv学习笔记七】访问图像中的像素与图像亮度对比度调整
- OpenCV从入门到放弃(五):像素!
- OpenCV 2 学习笔记(5): 像素的访问:给图像加入椒盐噪声
- opencv入门笔记之二 操作图像像素点
- 像素临近区域、颜色通道、ROI区域的访问---opencv.2学习笔记2
- OPENCV学习笔记2-5_扫描图像并访问相邻像素
- opencv学习笔记(十四)——图像像素的访问
- openCV学习笔记-三种访问像素的方法
- opencv学习笔记(八)IplImage* 访问图像像素的值
- opencv学习笔记1::访问图像中像素的三类方法(用指针,迭代器,动态地址)代码及用时检测
- opencv笔记7----Mat访问每个像素
- OpenCV入门教程(2)-Mat类之像素的读写
- Opencv学习笔记2--像素的访问
- opencv学习笔记-入门(7)单通道的图像数据访问
- OpenCV学习笔记(1):openCV的数据结构与像素的访问
- opencv学习笔记-入门(7)单通道的图像数据访问