当前位置:文档之家› OPENCV_Mat类存取方法(元素访问)

OPENCV_Mat类存取方法(元素访问)

Opencv----Mat类∙cv::Mat∙depth/dims/channels/step/data/elemSizeMat矩阵中数据元素的地址计算公式:addr(M i0,i1,…i m-1) = M.data + M.step[0] * i0 + M.step[1] * i1+ … + M.step[m-1] * i m-1。

其中m = M.dims 是指M的维度i.data:Mat对象中的一个指针,指向内存中存放矩阵数据的一块内存(uchar*data).ii.row: 行;col:列;rows:行数;cols:列数。

iii.dims :Mat所代表的矩阵的维度,如3 * 4 的矩阵为2 维,3 * 4 * 5 的为3维. iv.channels:通道,矩阵中的每一个矩阵元素拥有的值的个数,比如说3 * 4 矩阵中一共12 个元素,如果每个元素有三个值,那么就说这个矩阵是3 通道的,即channels = 3。

常见的是一张彩色图片有红、绿、蓝三个通道。

但是opencv用imread(opencv读图的函数)读进来的图像,三通道存放顺序为B、G、R。

v.depth:深度,即每一个像素的位数(bits),在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 };可见0和1都代表8位,2和3都代表16位,4和5代表32位,6代表64位;vi.step:是一个数组,定义了矩阵的布局,具体见下面图片分析,另外注意step1(step / elemSize1),M.step[m-1] 总是等于elemSize,M.step1(i)返回的是第i 维的步长,因此M.step1(m-1)总是等于channels,m是M的维度;这里是解释步长step[k]的,步长也可以看作是与第k维的存储单位,在2维的矩阵中,因为存储是按照行的顺序存储的,整个矩阵存储为一个平面,所以第k=0维的步长也就是单位肯定就是一行所占的字节数;如果是3维的话,第0维是按照面为单位来存储的,第1维是按照行为单位来存储的,第2维是按照元素类型为单位存储的,每个元素类型是基本类型(即uchar,float,short等等)与通道数的乘积...;也就是基本数据类型与通道数组成元素,多个元素组成了行,多行组成了面,多个面组成了3维体,多个3维体组成4维超体。

以此类推,如此看来某一维的步长应该等于高一维的步长step*低一维的大小size 。

vii.elemSize : 矩阵中每一个元素的数据大小,如果是n通道,就是(n*数据类型)。

如果Mat中的数据的数据类型是CV_8U 那么elemSize = 1,CV_8UC3 那么elemSize = 3,CV_16UC2 那么elemSize = 4;记住另外有个elemSize1 表示的是矩阵中数据类型的大小,即elemSize / channels 的大小。

图片分析1:考虑二维情况(stored row by row)按行存储上面是一个3 X 4 的矩阵,假设其数据类型为CV_8U,也就是单通道的uchar 类型∙这是一个二维矩阵,那么维度为2 (M.dims == 2);∙M.rows == 3; M.cols == 4;∙sizeof(uchar) = 1,那么每一个数据元素大小为1 (M.elemSize() == 1, M.elemSize1() == 1);∙CV_8U 得到M.depth() == 0, M.channels() == 1;∙因为是二维矩阵,那么step 数组只有两个值,step[0] 和step[1] 分别代表一行的数据大小和一个元素的数据大小,则M.step[0] == 4, M.step[1] == 1;∙M.step1(0) == M.cols = 4; M.step1(1) == 1;假设上面的矩阵数据类型是CV_8UC3,也就是三通道∙M.dims == 2;M.channels() == 3;M.depth() == 0;∙M.elemSize() == 3 (每一个元素包含3个uchar值)M.elemSize1() == 1 (elemSize / channels)∙M.step[0] == M.cols * M.elemSize() == 12, M.step[1] == M.channels() *M.elemSize1() == M.elemSize() == 3;∙M.step(0) == M.cols * M.channels() == 12 ; M.step(1) == M.channels() == 3;图片分析2:考虑三维情况(stored plane by plane)按面存储上面是一个3 X 4 X 6 的矩阵,假设其数据类型为CV_16SC4,也就是short 类型∙M.dims == 3 ; M.channels() == 4 ; M.elemSize1() == sizeof(short) == 2 ;∙M.rows == M.cols == –1;∙M.elemSize() == M.elemSize1() * M.channels() == M.step[M.dims-1] == M.step[2] == 2 * 4 == 8;∙M.step[0] == 4 * 6 * M.elemSize() == 192;∙M.step[1] == 6 * M.elemSize() == 48;∙M.step[2] == M.elemSize() == 8;∙M.step1(0) == M.step[0] / M.elemSize() == 48 / 2 == 96 (第一维度(即面的元素个数) * 通道数);∙M.step1(1) == M.step[1] / M.elemSize() == 12 / 2 == 24(第二维度(即行的元素个数/列宽) * 通道数);∙M.step1(2) == M.step[2] / M.elemSize() == M.channels() == 4(第三维度(即元素) * 通道数);End以上为Mat的存放形式以下为Mat的一些操作方法具体使用方法Fn 1 :利用step。

Code 1:int main(){//新建一个uchar类型的单通道矩阵(grayscale image 灰度图)Mat m(400, 400, CV_8U, Scalar(0));for (int col = 0; col < 400; col++){for (int row = 195; row < 205; row++){//获取第[row,col]个像素点的地址并用 * 符号解析*(m.data + m.step[0] * row + m.step[1] * col) = 255;}}imshow("canvas", m);cvWaitKey();return 0;}Output 1 :Code1只是演示了单通道的情况,对于多通道的例子,请看Code2 然后再看Code3。

Fn 2 :使用Mat::at 函数∙原型template<typename _Tp> inline _Tp& Mat::at(…) //其中参数有多个,也就是说at 函数有多个重载∙返回值为Mat 类型, Mat 有个索引的重载,也就是[] 符号的重载,用这个重载可以定位多通道数据,具体示例可以看下面代码。

下面的代码把红色通道值大于128的颜色的置为白色,左边为原图,右边为处理过后的图。

Code 2 :int main(){Mat img = imread("lena.jpg");imshow("Lena Original", img);for (int row = 0; row < img.rows; row++){for (int col = 0; col < img.cols; col++){/* 注意 Mat::at 函数是个模板函数, 需要指明参数类型, 因为这张图是具有红蓝绿三通道的图,所以它的参数类型可以传递一个 Vec3b, 这是一个存放 3 个 uchar 数据的Vec(向量). 这里提供了索引重载, [2]表示的是返回第三个通道, 在这里是 Red 通道, 第一个通道(Blue)用[0]返回 */if(img.at<Vec3b>(row, col)[2] > 128)img.at<Vec3b>(row, col) = Vec3b(255, 255, 255);}}imshow("Lena Modified", img);cvWaitKey();return 0;}Output 2 :Code 3 :这段代码用的是Fn1 的方式,效果和Code 2 等价,不过是处理三通道数据而已:int main(){Mat img = imread("lena.jpg");imshow("Lena Original", img);for (int row = 0; row < img.rows; row++){for (int col = 0; col < img.cols; col++){//主要是这里的代码if(*(img.data + img.step[0] * row + img.step[1] * col + img.elemSize1() * 2) > 128){//[row, col]像素的第 1 通道地址被 * 解析(blue通道)*(img.data + img.step[0] * row + img.step[1] * col) = 255;//[row, col]像素的第 2 通道地址被 * 解析(green通道), 关于elemSize1函数的更多描述请见 Fn1 里所列的博文链接*(img.data + img.step[0] * row + img.step[1] * col +img.elemSize1()) = 255;//[row, col]像素的第 3 通道地址被 * 解析(red通道)*(img.data + img.step[0] * row + img.step[1] * col +img.elemSize1() * 2) = 255;}}}imshow("Lena Modified", img);cvWaitKey();return 0;}Output 3 = Output 2Fn 3 :使用Mat 的一个模板子类Mat_<typename _Tp> 的( ) 符号重载定位一个像素Code 4 :int main(){Mat m(400, 400, CV_8UC3, Scalar(255, 255, 255));// m2 是 Mat_<Vec3b> 类型的, 因为 m 中元素的类型是 CV_8UC3, 可以用 Vec3b 存储 3 个通道的值// 注意 Mat_<CV_8UC3> 这种写法是错误的, 因为 CV_8UC3 只是一个宏定义// #define CV_8UC3 CV_MAKETYPE(CV_8U, 3)Mat_<Vec3b> m2 = m;// for 循环画一个红色的实心圆for (int y = 0; y < m.rows; y++){for (int x = 0; x < m.rows; x++){if (pow(double(x-200), 2) + pow(double(y-200), 2) - 10000.0 < 0.00000000001){// Mat_ 模板类实现了对()的重载, 可以定位到一个像素m2(x, y) = Vec3b(0, 0, 255);}}}imshow("Image", m);cvWaitKey();return 0;}Output 4 :[ 看上去怎么有点不爽]Fn 4 :使用Mat::ptr 模板函数Code 5 :int main(){Mat m(400, 400, CV_8UC3, Scalar(226, 46, 166)); imshow("Before", m);for (int row = 0; row < m.rows; row++){if (row % 5 == 0){// data 是 uchar* 类型的, m.ptr<uchar>(row) 返回第 row 行数据的首地址// 需要注意的是该行数据是按顺序存放的,也就是对于一个 3 通道的 Mat, 一个像素有// 有 3 个通道值, [B,G,R][B,G,R][B,G,R]... 所以一行长度为:// sizeof(uchar) * m.cols * m.channels() 个字节uchar* data = m.ptr<uchar>(row);for (int col = 0; col < m.cols; col++){data[col * 3] = 102; //第row行的第col个像素点的第一个通道值 Blue data[col * 3 + 1] = 217; // Greendata[col * 3 + 2] = 239; // Red}}}imshow("After", m);cout << (int)m.at<Vec3b>(0, 0)[0] << ','; //利用 Fn 1 介绍的方法输出一下像素值到控制台cout << (int)m.at<Vec3b>(0, 0)[1] << ',';cout << (int)m.at<Vec3b>(0, 0)[2] << endl;cvWaitKey();return 0;}Output 5 :End :Author : Ggicci谢谢阅读,有误希望指正!--OpenCV初学者。

相关主题