OpenCV3学习(2.2)——图像Mat矩阵操作与示例

三、大型数组类

    OpenCV3对大型数据的存储,具有代表性的是 cv::Mat和cv::SparseMat 类型。 cv::Mat针对的是密集连续性的存储,大多数的图像数据被存储为这种类,即使数据为空,预留的存储空间仍然存在;而cv::SparseMat针对的是稀疏的存储方式,只有数据不为0才保留空间,否则不会预留。显然cv::SparseMat存储更为节省空间,典型使用cv::SparseMat的例如直方图数据的存储。

1 创建与初始化cv::Mat

 cv::Mat用于密集连续型的n维数据存储。成员变量:

flags: 数据内容标识;
dims: 数据维数;
rows和cols: 数据行列数;
data: 指向存储的数据;
refcount: 用于智能指针的引用计数。
cv::Mat分为头部和数据部分,不拷贝数据的操作为“浅拷贝”,只是复制了头部;如果拷贝了数据则叫“深拷贝”,这种操作会创建空间并拷贝对方的数据。

我们可以通过载入图像来创建Mat类型矩阵,当然也可以直接手动创建矩阵,基本方法是指定矩阵尺寸和数据类型:

// 先声明,再创建数据
cv::Mat m;
// Create data area for 3 rows and 10 columns of 3-channel 32-bit floats
m.create( 30, 20, CV_32FC3 );
// Set the values in the 1st channel to 1.0, the 2nd to 0.0, and the 3rd to 1.0
m.setTo( cv::Scalar( 255.0f, 0.0f, 255.0f ) );

// 同时创建
cv::Mat m( 3, 10, CV_32FC3, cv::Scalar( 255.0f, 0.0f, 255.0f ) );

// 
	cv::Mat a(cv::Size(5,5),CV_8UC1); // 单通道
	cv::Mat b = cv::Mat(cv::Size(5,5),CV_8UC3); //3通道每个矩阵元素包含3个uchar值
	
// 初始化方法
	cv::Mat mz = cv::Mat::zeros(cv::Size(5,5),CV_8UC1); // 全零矩阵
	cv::Mat mo = cv::Mat::ones(cv::Size(5,5),CV_8UC1);  // 全1矩阵
	cv::Mat me = cv::Mat::eye(cv::Size(5,5),CV_32FC1);  // 对角线为1的对角矩阵


结果:

  

其中,type的组成方式为:CV_{8U,16S,16U,32S,32F,64F}C{1,2,3} 。例如:CV_32FC3 代表32bit浮点类型的三通道数据。图像处理中常用的几种数据类型如下:

CV_8UC1// 8位无符号单通道
CV_8UC3// 8位无符号3通道
CV_8UC4
CV_32FC1// 32位浮点型单通道
CV_32FC3// 32位浮点型3通道
CV_32FC4

 包括数据位深度8位、32位,数据类型U:uchar、F:float型以及通道数C1:单通道、C3:三通道、C4:四通道。

2 元素访问

    cv::Mat元素的方式:位置访问、迭代器访问和块访问。

2.1 位置访问

最直接的位置访问方式是使用 at<>() 

cv::Mat m = cv::Mat::eye( 10, 10, 32FC1 );//创建单位阵
printf("Element (3,3) is %f\n",m.at<float>(3,3));

cv::Mat m = cv::Mat::eye( 10, 10, 32FC2 );
printf("Element (3,3) is (%f,%f)\n",m.at<cv::Vec2f>(3,3)[0],m.at<cv::Vec2f>(3,3)[1]);

2.2 迭代器访问

使用 cv::MatIterator<> 和cv::MatConstIterator<> 迭代器可以对cv::Mat元素进行访问:

int sz[3] = { 4, 4, 4 };
cv::Mat m( 3, sz, CV_32FC3 ); // A three-dimensional array of size 4-by-4-by-4
cv::randu( m, -1.0f, 1.0f ); // fill with random numbers from -1.0 to 1.0
float max = 0.0f; // minimum possible value of L2 norm

cv::MatConstIterator<cv::Vec3f> it = m.begin();
while( it != m.end() ) 
{
    len2 = (*it)[0]*(*it)[0]+(*it)[1]*(*it)[1]+(*it)[2]*(*it)[2];
    if( len2 > max ) max = len2;
    it++;
}

2.3 块访问

块访问提供对cv::Mat子数组的访问方式。这种访问方式返回的往往是个数组或范围,而不是单个元素。

m.row( i );	//Array corresponding to row i of m
m.col( j );	//Array corresponding to column j of m
m.rowRange( i0, i1 );	//Array corresponding to rows i0 through i1-1 of matrix m
m.rowRange( cv::Range( i0, i1 ) );	//Array corresponding to rows i0 through i1-1 of matrix m
m.colRange( j0, j1 );	//Array corresponding to columns j0 through j1-1 of matrix m
m.colRange( cv::Range( j0, j1 ) );	//Array corresponding to columns j0 through j1-1 of matrix m
m.diag( d );//Array corresponding to the d-offset diagonal of matrix m
m( cv::Rect(i0,i1,w,h) );	//Array corresponding to the subrectangle of matrix m with one cornerat i0, j0 and the opposite corner at (i0+w-1, j0+h-1)

矩阵运算

矩阵加减法

我们可以使用"+"和"-"符号进行矩阵加减运算。

cv::Mat a= Mat::eye(Size(3,2), CV_32F);
cv::Mat b= Mat::ones(Size(3,2), CV_32F);
cv::Mat c= a+b;
cv::Mat d= a-b;

     

矩阵乘法

使用"*"号计算矩阵与标量相乘,矩阵与矩阵相乘(必须满足矩阵相乘的行列数对应规则)

	Mat m1= Mat::eye(2,3, CV_32F); //使用cv命名空间可省略cv::前缀,下同
	Mat m2= Mat::ones(3,2, CV_32F);
	cout<<"m1  = "<<endl<<m1<<endl<<endl;
	cout<<"m2  = "<<endl<<m2<<endl<<endl;
	// Scalar by matrix
	cout << "\nm1.*2 = \n" << m1*2 << endl;
	// matrix per element multiplication
	cout << "\n(m1+2).*(m1+3) = \n" << (m1+1).mul(m1+3) << endl;
	// Matrix multiplication
	cout << "\nm1*m2 = \n" << m1*m2 << endl;

   

矩阵转置

矩阵转置是将矩阵的行与列顺序对调(第i行转变为第i列)形成一个新的矩阵。OpenCV通过Mat类的t()函数实现。

// 转置
	Mat m1= Mat::eye(2,3, CV_32F);	
	Mat m1t = m1.t();
	cout<<"m1  = "<<endl<<m1<<endl<<endl;
	cout<<"m1t  = "<<endl<<m1t<<endl<<endl;
	system("pause");

 

求逆矩阵

逆矩阵在某些算法中经常出现,在OpenCV中通过Mat类的inv()方法实现

// 求逆
	Mat meinv = me.inv();
	cout<<"me  = "<<endl<<me<<endl<<endl;
	cout<<"meinv = "<<endl<<meinv<<endl<<endl;
	system("pause");

计算矩阵非零元素个数

计算物体的像素或面积常需要用到计算矩阵中的非零元素个数,OpenCV中使用countNonZero()函数实现。

// 非零元素个数
	int nonZerosNum = countNonZero(me); // me为输入矩阵或图像
	cout<<"me  = "<<endl<<me<<endl;
	cout<<"me中非零元素个数 = "<<nonZerosNum<<endl<<endl;
	system("pause");

   

均值和标准差

OpenCV提供了矩阵均值和标准差计算功能,可以使用meanStdDev(src,mean,stddev)函数实现。

参数

  • src – 输入矩阵或图像
  • mean – 均值,OutputArray
  • stddev – 标准差,OutputArray
// 均值方差
	Mat mean;
	Mat stddev;
	meanStdDev(me, mean, stddev); //me为前文定义的5×5对角阵
	cout<<"mean = "<<mean<<endl;
	cout<<"stddev = "<<stddev<<endl;
	system("pause");

 

需要说明的是,如果src是多通道图像或多维矩阵,则函数分别计算不同通道的均值与标准差,因此返回值mean和stddev为对应维度的向量

	Mat mean3;
	Mat stddev3;
	Mat m3(cv::Size(5,5),CV_8UC3,Scalar(255,200,100));
	cout<<"m3  = "<<endl<<m3<<endl<<endl;
	meanStdDev(m3, mean3, stddev3);
	cout<<"mean3 = "<<mean3<<endl;
	cout<<"stddev3 = "<<stddev3<<endl;
	system("pause");

 

 求最大最小值

求输入矩阵的全局最大最小值及其位置,可使用函数minMaxLoc();

void minMaxLoc(InputArray src, CV_OUT double* minVal,
                           CV_OUT double* maxVal=0, CV_OUT Point* minLoc=0,
                           CV_OUT Point* maxLoc=0, InputArray mask=noArray());

参数:

src – 输入单通道矩阵(图像).
minVal – 指向最小值的指针, 如果未指定则使用NULL
maxVal – 指向最大值的指针, 如果未指定则使用NULL
minLoc – 指向最小值位置(2维情况)的指针, 如果未指定则使用NULL
maxLoc – 指向最大值位置(2维情况)的指针, 如果未指定则使用NULL
mask – 可选的蒙版,用于选择待处理子区域
from:https://blog.csdn.net/iracer/article/details/51296631

cv::Mat支持的其他操作:

m1 = m0.clone();	//(深拷贝)Make a complete copy of m0, copying all data elements as well; cloned array willbe continuous
m0.copyTo( m1 );	//equivalent to m1=m0.clone()
m0.copyTo( m1, mask );	//Same as m0.copyTo(m1), except only entries indicated in the array mask arecopied
m0.convertTo(m1, type, scale, offset);	//在缩放或不缩放的情况下转换为另一种数据类型。

m0.setTo( s, mask );	//将阵列中所有的或部分的元素设置为指定的值; if mask is present, set only those valuescorresponding to nonzero elements in mask
m0.reshape( chan, rows );	//在无需复制数据的前提下改变2D矩阵的形状和通道数或其中之一。

m0.push_back( s );	//Extend an m × 1 matrix and insert the singleton s at the end
m0.push_back( m1 );	//Extend an m × n by k rows and copy m1 into those rows; m1 must be k × n
m0.pop_back( n );	Remove n rows from the end of an m × n (default value of n is 1)

m0.locateROI( size, offset );	Write whole size of m0 to cv::Size size; if m0 is a “view” of a larger matrix,write location of starting corner to Point& offset
m0.adjustROI( t, b, l, r );	Increase the size of a view by t pixels above, b pixels below, l pixels to the left,and r pixels to the right
m0.total();	Compute the total number of array elements (does not include channels)
m0.isContinuous();	Return true only if the rows in m0 are packed without space between them inmemory

m0.elemSize();	//Return the size of the elements of m0 in bytes (e.g., a three-channel float matrixwould return 12 bytes)
m0.elemSize1();	//Return the size of the subelements of m0 in bytes (e.g., a three-channel floatmatrix would return 4 bytes)

m0.type();	//Return a valid type identifier for the elements of m0 (e.g., CV_32FC3)
m0.depth();	//Return a valid type identifier for the individial channels of m0 (e.g., CV_32F)
m0.channels();	//Return the number of channels in the elements of m0
m0.size();	//Return the size of the m0 as a cv::Size object
m0.empty();	//Return true only if the array has no elements (i.e., m0.total==0 orm0.data==NULL)

推荐一个写的很好的Mat矩阵的博客:https://blog.csdn.net/giantchen547792075/article/details/7169255

from:http://blog.sina.com.cn/s/blog_7908e1290101i97z.htm

猜你喜欢

转载自blog.csdn.net/qq_30815237/article/details/86657359