三. 图像和大型数组类型
1. cv::Mat类N维稠密数组
可作为任意维度的数组使用, 数据按照栅格扫描顺序存储的n维数组.
所有的矩阵中都包含表示数组类型的元素flag, 表示维度的元素dims 分别表示行和列的数目元素row和cols 一个指示真正存储位置的data值镇 以及表示该内存区域有多少个引用的refcount元素.
初始化函数
构造函数
构造函数 | 说明 |
---|---|
cv::Mat | 默认构造函数 |
cv::Mat( int rows, int cols, int type ); | 指定类型的二维数组 |
cv::Mat( int rows, int cols, int type, const Scalar&s ); | 指定类型的二维数组, 并指定初始化值 |
cv::Mat( int rows, int 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 ); | 指定类型的二维数组, 并指定初始化值 |
cv::Mat( cv::Size sz, int type, void *data, size_t step=AUTO_STEP ); | 指定类型的二维数组,并指定预先存储的数据 |
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构造一个二维数组 构造n维数组并通过一个整型的序列来确定每一维数据
复制构造函数
复制构造函数 | 描述 |
---|---|
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::Rec& roi ); | 从感兴趣的区域中复制数据 |
cv::Mat( const Mat& mat, cosnt cv::Range* ranges ); | 服务于n维数组的, 从泛化的感兴趣区域中复制数据 |
cv::Mat( const cv::MatExpr& expr ); | 从其他矩阵的线性代数表述中生成新矩阵 |
其复制构造函数也分为三个类别: 输入行和列的范围 使用cv::Rect来指定一个矩形的子区域(只对二维矩阵有效) 输入一个range数组(range所指向的有效范围必须和mat的维度相等)
模板构造函数
模板构造函数 | 描述 |
---|---|
cv::Mat( const cv::Vec<T, n>& vec, bool copyData = true ); | 构造一个如同cv::Vec所指定的 数据类型为T, 大小为n的一位 |
cv::Mat( const cv::Matx<T, m, n>& vec, bool copyData = true ); | 构造一个如同cv:Matx所指定的 数据类型为T, 大小为m x nd的二维数组 |
cv::Mat( cosnt std::vector< T >& vec, bool copyData = true ); | 构造STL的vector所指定的 数据类型为T, 大小为vector的元素的一维数组 |
静态方法构造函数
函数 | 描述 |
---|---|
cv::Mat::zeros ( rows, cols, type ); | 构造一个rows x cols, 数据类型type, 值全为0的矩阵 |
cv::Mat::ones ( rows, cols, type ); | 构造一个rows x cols, 数据类型type, 值全为1的矩阵 |
cv::Mat::eye ( rows, cols, type ); | 构造一个rows x cols, 数据类型type, 的单位矩阵 |
数组数据获取
独立获取数组元素
通过模板函数**at<>()**来直接访问数组中的某一个元素, 其对不同维度的数组有不同的参数要求.
模板类要求先将**at<>()**转化为矩阵所包含的数据类型, 然后使用你所想要的数据的行和列的位置访问该元素.
例子:
//单通道访问单一元素
cv::Mat m = cv::Mat::eye(10, 10, CV_32FC1); //单通道浮点型单位矩阵
cout << m.at<float>(3, 3) << endl; //模板类使用float
//双通道访问单一元素
cv::Mat m2 = cv::Mat::eye(10, 10, CV_32FC2); //双通道浮点型单位矩阵
cout << m.at<cv::Vec2f>(3, 3)[0] << endl; //模板类使用Vec2f
注意: 当你想要对多维数组指定一个类似于at<>() 的模板函数时, 最好的方式是使用cv:Vec<>对象
at<>()访问函数的变体:
示例 | 描述 |
---|---|
M.at< int >( i ); | 整型数组M中元素i |
M.at< float >( i, j ); | 浮点型数组M中的元素( i, j ) |
M.at< int >( pt ); | 整型矩阵M中处于( pt.x, pt.y )的元素 |
M.at< float >( i, j, k ); | 三维浮点型矩阵M中处于(i, j, k )位置的元素 |
M.at< uchar >( idx ); | 无符号字符数组M中位于idx[]索引的n维位置的元素 |
也可以使用**ptr<>()**来访问数组, 得到指向数组的指针.
例如: 给定一个类型为float三通道的矩阵mtx, 结构体**mtx.ptr< Vec3f >(3)**将返回mtx的第三行指向第一个元素第一个(浮点)通道的指针.
使用数组迭代器
cv::MatIterator<>
使用cv::Mat内嵌的迭代器机制, 这种机制在某种程度上是基于STL容器所提供的机制. 基础想法是OpenCV提供一对迭代器模板, 一个用于只读(Const)数组和一个用于非只读的数组. 上述迭代器分别被命名为cv::MatConstIterator<>和cv::MatIterator<>. cv::Mat的成员函数begin()和end()会返回这种类型的对象.
例子: 使用迭代器计算三通道三维数组中"最长"元素(一个三维向量域)
int sz[3] = {4, 4, 4};
cv::Mat m(3, sz, CV_32FC3);
//在m中填充-1.0 - 1.0的小数
cv::randu(m, -1.0f, 1.0f);
float max, len = 0.0f;
//使用迭代器模板类获取m的起始位置
cv::MatConstIterator_<cv::Vec3f> it = m.begin<cv::Vec3f>();
while(it != m.end<cv::Vec3f>())
{
len = (*it)[0]*(*it)[0] + (*it)[1]*(*it)[1] + (*it)[2]*(*it)[2];
if (len > max)
max = len;
it++;
}
NAryMatIlterator
该迭代器只要求被迭代的数组有相同的几何结构(维度以及每一个维度的范围).
与MatIterator不同, 该迭代器不会返回一个用于迭代的单独元素, 而通过返回一堆数组来进行N-ary迭代器操作, 这些返回的数组也称作"面"(Plane). 一个面表示输入数组有连续内存的部分.
通过块访问数组元素
该方法适用于: 需要将一个数组的子集作为另一个数组访问, 这个子集可能是一行或者一列, 也可能是原始数组的一个子集.
cv::Mat区块访问
示例 | 描述 |
---|---|
m.row( i ); | m中第 i 行数组 |
m.col( j ); | m中第 j 列数组 |
m.rowRange( i0, i1 ); | m中第 i0 行到第 i1行所构成的数组 |
m.rowRange( cv::Range( i0, i1 )); | m中第 i0 行到第 i1 行所构成的数组 |
m.colRange( j0, j1 ); | m中第 j0 列到第 j1 列所构成的数据 |
m.colRange( cv::Range( i0, i1 )); | m中第 j0 列到第 j1 列所构成的数据 |
m.diag( d ); | m中偏移为 d 的对角线所构成的数组 (若参数为0, 将会是主队角, 若为正数, 则相对于主队角向数组上半部分 偏移, 反之负数向下半部分偏移) |
m( cv::Range( i0, i1), cv::Range( j0, j1 )); | m中从点( i0, j0 )到点( i1, j1 )所包含数据构成的数组 |
m( cv;:Rect( i0, i1, w, h )); | m中从点( i0, j0 )到点( i0+w-1, j0+h-1 )所包含的数据 构成的数组 |
m( ranges ); | m中依据ranges[0]到ranges[ndim-1]所索引区域 构成的数组 |
矩阵表达式
使用运算重载符运算矩阵cv::Mat
示例 | 描述 |
---|---|
m0 + m1, m0 - m1 | 矩阵的加法和减法 |
m0 + s; m0 - s; s - m1; | 矩阵和单个元素的加和减 |
-m0; | 矩阵取负 |
s * m0; m0 * s; | 缩放矩阵; |
m0.mul( m1 ); m0/m1; | 将元素m0和m1相乘, 按元素将m0和m1 相除; |
m0 * m1; | m0和m1进行矩阵乘法 |
m0.inv( method ); | 对m0矩阵进行求逆(默认使用DECONP_LU); |
m0.t(); | 对m0矩阵求转置; |
m0>m1; m0>=m1; m0==m1; … | 按元素进行比较, 返回元素只有0和255的 uchar类型矩阵; |
min(m0, m1); max(m0, m1); min(m0, s); min(s, m0); max(m0, s); max(s, m0); | 矩阵和矩阵之间或者矩阵和单个元素之间按元素 取最大或者最小值. |
cv::abs( m0 ); | 对m0按元素取绝对值; |
其他一些操作:
示例 | 描述 |
---|---|
m0.cross( m1 ); m0.dot( m1 ); | 向量叉乘和点乘操作; (叉乘操作只适用于3x1矩阵); |
cv::Mat::eye( Nr, Nc, type ); cv::Mat::zeros( Nr, Nc, type ); cv::Mat::ones( Nr, Nc, type ); | 用于返回规定类型NxN矩阵的静态方法; |
矩阵求逆操作inv()实际上是一系列矩阵求逆操作的接口:
- 使用cv::DECOMP_LU进行LU分解, 此方法对任意的非奇异矩阵都有效.
- 使用cv::DECOMP_CHOLESKY, 表示使用Cholesky分解, 其在矩阵半正定的时候有效, 且在处理大型矩阵的时候比LU分解快很多.
- 使用cv::DECOMP_SVD, 为奇异值分解(SVD)进行求逆, 在矩阵奇异或者非方阵的情况下都可以工作(可以求出矩阵的伪逆).
其他的一些成员函数
示例 | 描述 |
---|---|
m1 = m0.clone(); | 从m0进行完全复制所有的数据元素 |
m0.copyTo( m1 ); | 将m0复制给m1, 如果有必要, 将给m1重分配内存空间 (等同于m1 = m0.clone() ); |
m0.copyTo( m1, mask ); | 只复制mask所指示的区域 |
m0.convertTo( m1, type, scale, offset ); | 转换m0中元素的类型, 并且在尺度变换和增加偏置后赋值给m1; |
m0.assignTo( m1, type ); | 只在内部使用(集成在convertTo中) |
m0.set( s, mask ); | 将m0所有元素设为s, 如果存在mask, 则只对mask指示区域进行操作 |
m0.reshape( chan, rows ); | 改变二维数组的有效形状, chan和rows若为0, 则便是不做更改 |
m0.push_back( s ); | 在末尾增加一个mx1大小的数组 |
m0.push_back( m1 ); | 向m x n大小的矩阵m0增加k行并复制到m1中, m1大小必须是k x n (扩充矩阵) |
m0.pop_back( n ); | 从m x n大小的矩阵移除n行 |
m0.locateROI( size, offset ); | 将m0的全尺寸写入cv::Size size, 如果m0是一个大矩阵的一块区域, 还会 写入Point类型的offset |
m0.adjustROI( t, b, l, r ); | 通过t(最上), b(最下), l(最左), r(最右), 调整ROI范围 |
m0.total(); | 计算数组序列的元素的数目(不包括通道) |
m0.isContinous(); | 如果m0的行之间没有空隙, 将返回true |
m0.elemSize(); | 返回m0的位长度(比如三通道浮点矩阵将返回12) |
m0.elemSize1(); | 返回m0的最基本元素的位长度(比如三通道浮点矩阵将返回4) |
m0.type(); | 返回m0的元素类型(比如: CV_32FC3) |
m0.depth(); | 返回m0通道中的元素类型(比如: CV_32F) |
m0.channels(); | 返回m0的通道数目 |
m0.size(); | 以从cv::Size返回m0的大小 |
m0.empty(); | 如果数组没有元素, 返回true |
2. cv::SparseMat稀疏数据类
cv::SparseMat稀疏类矩阵在非0元素非常少的情况下使用(0很多), 在一些特殊的应用中, 大部分元素为空的时候使用稀疏类矩阵可以节约大量的内存. 但稀疏类矩阵计算时需要对每个元素进行计算, 其运算速度较慢.
Opencv的稀疏矩阵类cv::SparseMat函数在很多方面与稠密矩阵类cv::Mat的定义十分相似.
cv::Mat使用接近C风格(数据按照序列被打包, 且地址可以通过元素的索引计算出来), 而cv::SparseMat使用哈希表来存储非0元素, 这个哈希表会自动维护, 当数组中的非0数据变得太多以致于无法高效进行查找的时候, 表也会增长.
访问稀疏数组中的元素
函数访问
稀疏数组提供四种访问机制:
cv::SparseMat::ptr() | cv::Sparse::ref() | cv::SparseMat::value() | cv::SparseMat::find() |
---|
1. ptr方法
cv::SparseMat::ptr()方法有几种变体, 最简单的如下:
uchar* cv::SparseMat::ptr(int i0, bool createMissing, size_t* hashval=0)
这个版本用于访问一维数组, 第一个参数i0是请求元素的索引, 第二个参数creatMissing表示这个元素是否应该被创建
2. ref方法
cv::SparseMat::ref<>()用于返回一个指向数组中特定元素的引用, 这个函数类似于SparseMat::ptr(), 可以接受一个、两个或者三个索引, 或者一个索引数组, 并且同样支持一个可选的用于查找的哈希值.
3. value方法
cv::SparseMat::value<>()和cv::SparseMat::ref<>()彼此独立, 它将返回一个值而不是返回值的引用, 因此, 这个方法也叫"只读方法"
4. find方法
返回一个请求对象的指针, 其指针类型由cv::SparseMat::find<>()的模板指定. 是一个"只读方法".
迭代器访问
迭代器访问可使用cv::SparseMatIterator_ <>() 和 cv::SparseMatConstIterator_ <>(), 其包含模板类和非模板类(可以不使用模板, 返回一个非模板的迭代器).
使用迭代器例子:
int size[] = {10, 10};
cv::SparseMat sm(2, size, CV_32F);
for (int i=0; i<10; i++)
{
int idx[2];
idx[0] = size[0] * rand();
idx[1] = size[1] * rand();
sm.ref<float>(idx) += 1.0f; //对sm中的随即索引赋值
}
cv::SparseMatIterator_<float> it = sm.begin<float>();
cv::SparseMatIterator_<float> it_end = sm.end<float>();
for (; it != it_end; ++it)
{
const cv::SparseMat::Node* node = it.node();
cout << node->idx[0] << endl;
cout << node->idx[1] << endl;
cout << *it << endl << endl;
}
以上的例子中, 路过了定义在迭代器中的方法node(), node()返回一个指向被迭代器索引指向的稀疏矩阵的实际数据区域, 它返回一个类型为cv::SparseMat::Node的对象:
struct Node
{
size_t hashval;
size_t next;
int idx[cv::MAX_DIM];
};
稀疏矩阵的特有函数
示例 | 描述 |
---|---|