三、大型数组类
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分为头部和数据部分,不拷贝数据的操作为“浅拷贝”,只是复制了头部;如果拷贝了数据则叫“深拷贝”,这种操作会创建空间并拷贝对方的数据。
m.row(j)返回一个Mat类型,m.row(j).copyTo(m.row(i)),可以用于讲一个Mat 拷贝到另一个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:四通道。
type函数
opencv中Mat存在各种类型,其中mat有一个type()的函数可以返回该Mat的类型。类型表示了矩阵中元素的类型以及矩阵的通道个数,它是一系列的预定义的常量,其命名规则为CV_(位数)+(数据类型)+(通道数)。具体的有以下值:
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 mm.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
函数原型:Mat cv::Mat::colRange(int startcol, int endcol) const
功能:该方法为矩阵的指定列跨度创建一个矩阵头。 类似于Mat :: row和Mat :: col,这是一个O(1)操作
参数:(分别表示我们想取的Mat的第几列到第几列)
- startcol:一个包含0的起始索引的列跨度。
- endcol:一个除0以外的结束索引的列跨度。
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)
3、矩阵运算
矩阵加减法
我们可以使用"+"和"-"符号进行矩阵加减运算。
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 – 可选的蒙版,用于选择待处理子区域
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矩阵的博客: