OpenCV基础三:图像和大型数组类型(陆续更新)

三. 图像和大型数组类型

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];
};

稀疏矩阵的特有函数

示例描述

3. 大型数组模板结构

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值