OpenCV学习心得——基础篇——图像与大型数组类型——cv::Mat类与数组与矩阵

OpenCV学习心得——基础篇——图像与大型数组类型——cv::Mat类与数组与矩阵
FOR THE SIGMA
FOR THE GTINDER
FOR THE ROBOMASTER

简介:

这一系列的学习心得第一轮将参考《学习OpenCV3》一书

操作系统版本:Ubuntu16.04(在这里博主在Linux下进行运行的)
http://www.ubuntu.org.cn/download/desktop 桌面版ubuntu16.04 下载

电子版书籍下载地址
暂无资源

参考:
https://blog.csdn.net/CV_Jason/article/details/54928920
https://segmentfault.com/a/1190000015653101

内容:

前言:
cv::Mat类结构被视为Opencv所有C++实现的核心,其所有主要函数都或是cv::Mat类的成员,或是将cv::Mat作为参数,或是返回一个cv::Mat类型。
参考 :https://blog.csdn.net/weixin_35695879/article/details/85948011 opencv cv::Mat数据类型总结

稠密数组与稀疏数组
cv::Mat类一般作为任意维度的稠密数组。特别需要指出的是这里的“稠密”表示该数组的所有部分都有一个值储存,即便这个值是0。而大多数图像都是以稠密数组形式储存的。

当然有稠密数组就有稀疏数组即只有非零的数值会被储存。其优点是在数组很多都是0的情况下,会非常节约内存,但是在数组比较稠密时,反而会浪费大量内存。

例如:统计直方图中,由于大部分直方图的大部分数值为0,我们并没有必要储存所有值为0的部分。

在一维数组中,元素是按顺序排列的;
在二维数组中,数据按行组织,每一行也按顺序排列
在三维数组中,所有的通道都被行填充,每一个通道同样按顺序排列。

在所有的矩阵都含有一个代表它所包含数组类型的元素flag;表示其维度的元素dims;分别表示行与列的数目的元素rows和cols(但是在dims大于2时无效);指示数据真正储存位置的data指针;表示该内存区域有多少个引用的元素refcount等。

如何创建一个数组
诚然我们可以通过实例化变量cv::Mat来创建一个数组,但是这数组将没有大小与数据类型。这时便需要使用create( )来申请一个内存区域。即通过指定的行数列数以及数据类型来配置二维数组的规模。
而数据类型则需要一个详细指明的数据类型+通道数,而CV_{8U,16S,16U,32S,32F,64F}C{1,2,3}是一个常见的模板。
例如 :

CVC_32FC3 = 三通道的32位浮点数据。

还有一种与create( )函数有相同的参数(该接口存在第四个可选参数来指定如何初始化数组的所有元素)。
例如:

cv::Mat m;
m.create( 3, 10, CV_32FC3)
m.setTo( cv::Scalar( 0.1f, 0.0f, 1.0f ) );

等效于:

cv::Mat m( 3, 10, cv_32FC3 , cv::Scalar( 0.1f, 0.0f, 1.0f ) )      #将其合并了

重点注意:需要明白数组中数据与数组对象的解耦。对象cv::Mat是数据实体的头,原则上来说,例如我们将矩阵a赋值给矩阵b时(a=b),b内部的数据指针之前所指向的数据实体b就会被释放(若存在的话),同时,b与啊共享的内存区域的引用指针会增加一个引用计数。之后b的与数据实体相关的成员依照新数据进行更新即数组可以被赋值给另一个数组,并保证其运算结果正确。

参考:https://blog.csdn.net/u012058778/article/details/90764430 cv::Mat初识和它的六种创建方法

cv::Mat的构造函数(非复制构造函数)

构造函数 说明
cv::Mat 默认构造函数
cv::Mat( int rows,int cols,int type); 指定类型的二维数组
cv::Mat(int rows,int cols,int type,const Scalar&s); 指定类型的二维数组并指定初始化
cv::Mat(int rowsint cols,int type,void* data,size_t step=AUTO_STEP); 指定类型的二维数组并指定预先储存数据
cv::Mat( cv::Size sz, int type); 指定类型的二维数组(大小由sz指定)
cv::Mat(cv::Size sz,int type,const Scalar&s); 指定类型的二维数组,并指定初始化值(大小sz指定)
cv::Mat(cv::Size sz,int type,void* data,size_t step=AUTO_STEP); 指定类型的二维数组,并指定预先储存数据(大小sz指定)
cv::Mat(int ndims,const int* sizes,int type); 指定类型的多维数组
cv::Mat(int ndims,const int* sizes,int type,const Scalar&s); 指定类型的多维数组并指定初始化值
cv::Mat(int ndims,const int* sizes,int type,void* data,size_t step=AUTO_STEP); 指定类型的多维数组并指定预先储存数据

上面主要范围三类:
一、输入行数和列数来构造一个二维数组的
二、cv::Size对象来构造一个二维数组的
三、通过一个整型的序列来确定每一维数据维度的。

cv::Mat的构造函数从其他cv::Mat复制数据的构造函数

构造函数 说明
cv::Mat(const Mat&mat) 复制构造函数
cv::Mat( const Mat&mat,const cv::Range&rows,const cv::Range&cols); 只从指定行列中复制数据的复制构造函数
cv::Mat( const Mat&mat,const cv::Rect&roi); 只从感兴趣的区域中复制数据的复制构造函数
cv::Mat(const Mat&mat, const cv::Range* ranges); 服务于n维数组的,从泛化的感兴趣区域中复制数据的复制构造函数
cv::Mat(const cv::MatExpr&expr); 从其他矩阵的线性代表表述中生成新矩阵的复制构造函数

子区域(ROI感兴趣区域)构造函数有三个类别:
一种输入为行与列的范围(在二维矩阵的情况下有效)
一种使用cv::Rect来指定一个矩形的子区域(同上)
一种输入一个range数组的。(有效范围的维度必须与mat的维度相同)

cv::Mat模板构造函数

构造函数 说明
cv::Mat(const cv::Vec&vec,bool copy copyData=true) 构造一个如图cv::Vec所指定的数据类型为T、大小为n的一维数组
cv::Mat(const cv::Matx&vec,bool copyData = true); 构造一个如图cv::Matx所指定的数据类型为T、大小为mxn的二维数组。
cv::Mat(const std::vector&vec, bool copyData = true); 构造STL的vector所指定的数据类型为T、大小为vector元素数的一维数组

构造cv::Mat的静态方法

函数 说明
cv::Mat::zeros(rows, cols, type); 构造一个大小为rows×cols,数据类型为type指定类型的,值全为0的矩阵
cv::Mat::ones(rows, cols, type); 构造一个大小为rows×cols,数据类型为type指定类型的,值全为1的矩阵
cv::Mat::eye(rows, cols, type); 构造一个大小为rows×cols,数据类型为type指定类型的单位矩阵

利用模板函数at<>( )来实现直接访问,例子:
单通道数组

cv::Mat m = cv::Mat::eye(10,10,32FC1);
printf( 
	"Element (3,3) is %f\n",
	m.at(3,3)
	);

多通道数组

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

使用at<>( )模板时,最好使用cv::Vec< >对象

at<>()访问器函数的变体

示例 说明
M.at ( i ); 整型数组M中的元素i
M.at ( i , j ); 浮点型数组M中的元素(i,j)
M.at ( pt ); 整型矩阵M中处于(pt.x, pt.y)的元素
M.at ( i , j , k ); 三维浮点型矩阵M中处于(I,J,K)位置的元素
M.at( idx ); 无符号字符数组M中位于idx[ ]所索引的n维位置的元素

这边需要注意的是有两种方法可以获得一个指向矩阵mtx的数据区域的指针。一种通过使用ptr<>( )成员函数,另一种是直接使用数据指针data,然后使用成员数组step来计算地址。但是这种做法并不推荐,唯一的优点在处理二维的数组中,直接计算地址也是最有效率的做法。

另外一种序列存储的方法是使用cv::Mat内嵌的迭代器机制。(基于STL容器所通过的机制)
因为这一代的迭代器具有足够的智能来处理连续的内存区域和非连续的内存区域,所以也十分方便。
例如:

//使用迭代器来计算三通道三维数组中的“最长”元素
int sz[3] = { 4,4,4 };
cv::Mat m(3,sz,cv_32FC3);
cv::randu(m, -1.0f, 1.0f);

float max = 0.0f;
cv::MatConstIterator 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++;
}

若两个数组相加,或者将RGB颜色空间的数组转换到HSV颜色空间的情况下,数组中的每一个元素都会进行这样的操作。
参考:https://www.cnblogs.com/ChrisCoder/p/10083755.html cv::Mat遍历

一种只要求被迭代的数组有相同的几何结构(维度以及每个维度的范围)
cv::MatIterator<> 迭代器不会返回一个迭代的单独元素,而通过返回一堆数组来进行迭代操作,这些返回的数组也存为“面”,这个“面”则表示输入数组有连续内存的部分。

//按面进行多维数组相加
const int n_mat_size = 5;
const int n_mat_sz[ ] = { n_mat_size, n_mat_size, n_mat_size };
cv::Mat n_mat( 3 , n_mat_sz, CV_32FC1 );

cv::RNG rng;
rang.fill( n_mat , cv::RNG::UNIFORM, 0.f , 1.f );

const cv::Mat* arrays[ ] = { &n_mat , 0};
cv::Mat my_planes[1];
cv::NAryMatIterator it( arrays , my_planes );

cv::Mat区块访问

示例 说明
m.row ( i ); m中第i行数组
m.col ( j ); m中第j列数组
m.rowRange( i0 , i1 ); M中第i0行到第i1-1行所构成的数组
m.rowRange( cv::Range( i0, i1) ); M中第i0行到第i1-1行所构成的数组
m.colRange( j0 , j1 ); m中地j0列到第j1-1列所构成的数组
m.rowRange( cv::Range( j0, j1) ); M中第j0行到第j1-1行所构成的数组
m.diag( d ); m中偏移为d的对角线所组成的数组
m( ranges ); M中依据ranges[0]到ranges[ndim-1]所索引区域构成的数组

矩阵表达式可用的运算操作

示例 说明
m1 + m2, m1 - m2; 矩阵的加法和减法
m1 + s; m1 - s; s + m2 ; s - m2; 矩阵和单个元素的加和减
-m1; 矩阵取负
s * m1; m1 * s 矩阵按单元素缩放
m1.mul(m2) ; m1/m2; 按元素将m1与m2相乘,按元素将m1与m2相除
m1 * m2 m1与m2进行矩阵相乘
m1.inv(method); 对m1矩阵求逆
cv::abs( m1 ); 对m1按元素取绝对值

其中矩阵求逆操作inv() 主要有3种操作:
第一种为cv::DECOMP_LU,表示使用LU分解,对任意的非奇异矩阵都有效。
第二种为cv::DECOMP_CHOLESKY,表示使用Cholesky分解。
第三种为cv::DECOMP_SVD,进行奇异值分解进行求逆。
(SVD是唯一一种在矩阵奇异或非方阵的情况下都可工作的方法)

cv::saturate_cast<>( )饱和转换
opencv进行算术或其他操作时自动检查是否上溢出或是下溢出。
例如:
加了保护的

for (int i=0; i(i);
	const uchar* src2_ptr = src2.ptr(i);
	uchar* dst_ptr  = dst.ptr(i);
	for (int j=0; j(src1_ptr[j]*alpha + src2_ptr[j]*beta + gama);//gama = -100, alpha = beta = 0.5
//		dst_ptr[j] = (src1_ptr[j]*alpha + src2_ptr[j]*beta + gama);
	}
}
imshow("output2",dst);

没加保护的

//没加入溢出保护
	for (int i=0; i(i);
		const uchar* src2_ptr = src2.ptr(i);
		uchar* dst_ptr  = dst.ptr(i);
		for (int j=0; j(src1_ptr[j]*alpha + src2_ptr[j]*beta + gama);//gama = -100, alpha = beta = 0.5
			dst_ptr[j] = (src1_ptr[j]*alpha + src2_ptr[j]*beta + gama);
		}
	}
	imshow("output2",dst);

cv::SparesMat类
稀疏数据类一般在数组非零0元素非常少的情况下使用,常用于直方图或者高维数组。
cv::SparseMat::ptr()、cv::SparseMat::ref()、cv::SparseMat::value()、cv::SparseMat::find()

你可能感兴趣的:(OpenCV3教程,OpenCV3学习)