目录
动态可变的存储
cv::Mat 类N维稠密数组
创建一个数组
独立获取数组元素
数组迭代器NAryMatlterator
通过块访问数组元素
矩阵表达式:代数和cv::Mat
饱和转换
数组还可以做很多事情
稀疏数据类cv::SparesMat
访问稀疏数组中的元素
稀疏数组中的特有函数
大型数组模板结构
动态可变的存储
大型数组类型最主要的是cv::Mat ,这个结构可以视为OpenCV所有C++实现的核心。
OpenCV所有主要函数:或是cv::Mat类的成员,或是将cv::Mat作为参数,或是返回一个cv::Mat类型。
cv::Mat类用于表示任意维度的稠密数组。“稠密数组”表示该数组的所有部分都有一个值存储,即使这个值是0;“稀疏数组”只有非零的值会被存储,如果数组很多地方都是0,那么稀疏数组会非常节约内存,反之浪费内存。
cv::Mat 类N维稠密数组
cv::Mat类可以作为任意维度的数组使用,其数据可以看作是按照栅格扫描顺序存储的n维数组。
所有的矩阵都含有:
- 一个表示它所包含数组类型的元素flag,
- 一个表示其维度的元素dims,分别表示行和列的数目的元素rows和cols(在dims大于2时无效),
- 一个指示数据真正存储位置的data指针
- 一个表示该内存区域有多少个引用的refcount元素,类似于cv::Ptr<>的引用计数器。
数据数组按如下公式排列数据:
例:对于一个二维数组,这个公式可以如下简化为:
包含在cv::Mat中的数据不要求必须是简单基元。cv::Mat的元素可以是一个数字,也可以是多个数字。(包含多个数字时,被称为“多通道数组”)
很多情况下,将数组看成一个带有熟知的向量的数组是很有用的,因此OpenCV库包含对此类结构的特殊规定:需要一个元素指定通道的数量,cv::Mat 使用一个成员函数cv::channels()返回该值。
创建一个数组
通过实例化变量cv::Mat 来创建一个数组,通过这种方式创建的数组没有大小和数据类型。之后使用成员函数create()来申请一个内存区域。一个create()的变体是通过指定行数和列数以及数据类型来配置二维数组的规模。
void cv::Mat::create(
int rows,
int cols,
int type
);
所有数据类型都在库的头文件中声明,包括CV_{8U,16S,16U,32S,32F,64F} C {1,2,3}的多种组合。
OpenCV允许定义超过3通道的数据类型,但为了创建它,需要调用函数CV_{8U,16S,16U,32S,32F,64F}C();
例:CV_8UC(3)等效于CV_8UC3,CV_8UC(7)
例:CV_32FC3表示一个三通道的32位浮点数据。
int main()
{
cv::Mat m;
m.create(3, 10, CV_32FC3);
m.setTo(cv::Scalar(1.0f, 0.0f, 1.0f));
//等效于
cv::Mat m1(3, 10, CV_32FC3, cv::Scalar(1.0f, 0.0f, 1.0f));
return EXIT_SUCCESS;
}
重点:对象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 rows, int cols, int type, void *data, size_t step = 0Ui64); | 指定类型的二维数组,并指定预先存储的数据 |
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 = 0Ui64); | 指定类型的二维数组,并指定预先存储的数据(大小由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 = 0Ui64); | 指定类型的多维数组,并指定预先存储的数据 |
除了默认构造函数,它们主要分为如下几类:要求输入行数和列数来构造一个二维数组的、使用cv::Size对象来构造一个二维数组,以及构造n维数组并且要求你通过一个整型的序列来确定每一维数据维度的。
int main()
{
cv::Mat mat;
cv::Mat mat1(3, 3, CV_32F);
cv::Scalar s(5);
cv::Mat mat2(3, 3, CV_32FC1, s);
cout << mat2 << endl;
int lst[9] = { 0,1,2,3,4,5,6,7,8 };
cv::Mat mat3(3, 3, CV_32S, lst, 0Ui64);
cout << mat3 << endl;
cv::Mat mat4(cv::Size(3, 3), CV_32FC1);
cv::Mat mat5(cv::Size(3, 3), CV_32FC1, s);
cout << mat5 << endl;
cv::Mat mat6(cv::Size(3, 3), CV_32S, lst, 0Ui64);
cout << mat6 << endl;
int sz[3] = {3,3,3};
cout << sz[1] << endl;
cv::Mat mat7(3, sz, CV_32FC1);
cv::Mat mat8(3, sz, CV_32FC1, cv::Scalar(1.0));
cout << mat8.at<float>(1,1,1) << endl;
return EXIT_SUCCESS;
}
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 cv::Mat &mat, const cv::Rect &roi); | 只从感兴趣的区域中复制数据的复制构造函数 |
cv::Mat(const Mat &mat, const cv::Range* ranges); | 服务于n维数组的,从泛化的感兴趣区域中复制数据的复制构造函数(range:沿每个维度 m 的选定范围的数组。) |
cv::Mat(const cv::MatExpr &expr) | 从其他矩阵的线性代数表述中生成新矩阵的复制构造函数 |
子区域(感兴趣区域,ROI)构造函数也分为三个类别,分别是一种输入为行和列的范围(只在二维),一种使用cv::Rect来指定一个矩形的子区域(二维),以及最后一种输入一个Range数组的,range所指向的有效范围的维度必须和mat的维度相等。
大于四维的Mat,既不能使用at,也不能使用ptr访问元素。
int main{
//复制构造函数
cout << "复制构造函数" << endl;
cv::Mat mat10(mat3);
cout << mat10 << endl;
cv::Mat mat11(6, 6, CV_32FC1, cv::Scalar(1.0));
cout << mat11 << endl;
cv::Mat mat12(mat11, cv::Range(1, 4), cv::Range::all());
cout << mat12 << endl;
cv::Mat mat13(mat11, cv::Rect(1,1,3,3));
cout << mat13 << endl;
int sz3[3] = { 4, 4, 4};
cv::Mat mat14(3, sz3, CV_32FC1, cv::Scalar(1.0));
cv::Mat mat15(mat14, cv::Range(1, 3));//range:沿每个维度 m 的选定范围的数组。
cout << mat15.at<float>(1, 1, 1) << endl;
cout << "stop" << endl;
return EXIT_SUCCESS;
}
最后一组构造函数是带模板的构造函数,它们也被称为“模板构造函数”。这些函数并不会从cv::Mat创建一个模板,而是根据模板创建一个cv::Mat的实例。这些构造函数允许通过模板类cv::Vec<>或cv::Matx<>来创建对应维度和类型的cv::Mat,或者使用一个STL vector<>来创建一个相同类型的数组。
cv::Mat模板构造函数
构造函数 | 描述 |
cv::Mat(const cv::Vec<T, n> &vec, bool copyData = true); | 构造一个如同cv::Vec所指定的数据类型为T,大小为n的一位数组。 |
cv::Mat(const cv::Matx<T, m, n> &matx, bool copyData = true); | 构造一个如同cv::Matx所指定的数据类型为T,大小为m*n的二维数组。 |
cv::Mat(const std::vector<T> &vec, bool copyData = true); | 构造STL的vector所指定的数据类型为T,大小为vector元素的一维数组。 |
int main{
cv::Vec<int, 5> vec(1,2,3,4,5);
cv::Mat mat16(vec, true);
cout << mat16 << endl;
cv::Matx33f matx(1., 2., 3., 4., 5., 6., 7., 8., 9.);
cv::Mat mat17(matx, true);
cout << mat17 << endl;
return EXIT_SUCCESS;
}
构造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<>() 来实现的。
工作方式:先将at<>()特化到矩阵所包含的数据类型,然后使用行和列的位置访问元素。
例子:
int main
{
//单通道
cv::Mat m = cv::Mat::eye(10, 10, 32FC1);
m.at<float>(3,3);
//多通道
cv::Mat m = cv::Mat::eye(10, 10, 32FC2);
m.at<cv::Vec2f>(3,3)[1];
}
当你想要对多维数组指定一个类似于at<>()的模板函数时,最好的方式是使用cv::Vec<>对象(使用预先创建的别名或模板形式)。
创建一个由更加精巧的元素构建的数组,比如复数:
int main{
cv::Mat m = cv::Mat::eye(10, 10, cv::DataType<cv::complexf>::type);
m.at<cv::Complexf>(3,3).re;
m.at<cv::Complexf>(3,3).im;
}
当需要从(编译时)的表示生成另一个(运行时的)表示时,需要cv::DataType<>.
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维位置的元素 |
为了访问二维数组,可以使用指针来指定某一行,这个工作由cv::Mat的成员函数ptr<>()完成,由于at、ptr都是模板函数,所以需要一个类型名进行实例化。函数接收一个整型参数来指示希望指针指向的行,函数将返回一个和矩阵原始数据类型相同的数据指针。
例:mtx.ptr<Vec3f>(3) 将返回 mtx的第三行 指向的第一个元素的 第一个通道的指针。
注:有两种方式可以获得一个指向矩阵mtx的数据区域的指针:一种是使用 ptr<>()成员函数,另一种是直接使用数据指针data,然后使用成员数组step来计算地址,更像C语言中的操作(不推荐)。
成员函数isContinuous()将告诉你数组是否被连续的打包,如果是连续的,就饿可以通过获得第一行第一个元素的指针,然后在整个数组中遍历。
迭代器机制
另一种序列存储的方式是使用cv::Mat内嵌的迭代器机制。
用于只读(const)数组的迭代器:cv::MatConstIterator<>
用于非只读(ono-const)数组的迭代器:cv::MatIterator<>
cv::Mat的成员函数begin() && end() 会返回这种类型的对象。
例:使用迭代器计算三通道三维数组中的“最长”元素
int main
{
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<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++;
}
}
数组迭代器NAryMatlterator
这个迭代器只要求被迭代的数组有相同的几何结构(维度以及每一个维度的范围)。
该迭代器不会返回一个用于迭代的单独元素,而通过返回一堆数组来进行N-ary迭代器操作,这些返回的数组也称为“面”(plane)。一个面表示输入数组有连续内存的部分(一般来说是一维或二维的片段)。
面所包含的内容全部都是从被迭代的多维度数组的内容中分离出来的。
例:按面进行多维数组相加
int main()
{
int sz[] = { 5, 5, 5 };
cv::Mat n_mat(3, sz, CV_32FC1);
cv::RNG rng;
rng.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);
float s = 0.f;
int n = 0;
for (int p = 0; p < it.nplanes; p++, ++it)
{
s += cv::sum(it.planes[0])[0];
n++;
}
return EXIT_SUCCESS;
}
面的数量(每一个数组都一样,有相同的几何结构)始终由it.planes所决定。N-ary迭代器包含C风格数组planes,它为每个输入数组保存当前平面的头。
例4-2:使用N-ary将两个数组相加
int main
{
int sz[] = { 5, 5, 5 };
cv::Mat n_mat0(3, sz, CV_32FC1);
cv::Mat n_mat1(3, sz, CV_32FC1);
cv::RNG rng;
rng.fill(n_mat0, cv::RNG::UNIFORM, 0.f, 1.f);
rng.fill(n_mat1, cv::RNG::UNIFORM, 0.f, 1.f);
const cv::Mat* arrays[] = { &n_mat0, &n_mat1, 0 };
cv::Mat my_planes[2];
cv::NAryMatIterator it(arrays, my_planes);
float s = 0.f;
int n = 0;
for (int p = 0; p < it.nplanes; p++, ++it)
{
s += cv::sum(it.planes[0])[0];
s += cv::sum(it.planes[1])[0];
n++;
}
return EXIT_SUCCESS;
}
通过块访问数组元素
需要将一个数组的子集作为另一个数组访问,这个子集可能是一行或一列,也可能是原始数组的一个子集。
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.colrange(cv::Range(j0, j1)); | m中第j0列到第j1-1列所构成的数组 |
m.diag(d); | m中偏移为d的对角线所组成的数组 |
m(cv::Range(i0, i1), cv::Range(j0, j1)); | m中从点(i0, j0)到点(i1-1, j1-1)所包含数据构成的数组 |
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的成员函数,并且都返回我们所请求的数组的子集。
使用以上函数并没有将数据复制到新的数组中,只是创建一个新的数组头。
矩阵表达式:代数和cv::Mat
"数组"表示一般的cv::Mat的对象,"矩阵"表示作为一个数学对象。
矩阵表达式可用的运算操作
示例 | 描述 |
m0 + m1; m0 - m1; | 矩阵的加法和减法 |
m0 + s; m0 - s; s + m0; s - m0; | 矩阵和单个元素的加和减 |
-m0; | 矩阵取负 |
s * m0; m0 * s; | 矩阵缩放 |
m0.mul(m1); m0/m1; | 按元素将m0和m1相乘,按元素相除 |
m0 * m1; | 矩阵乘法 |
m0.inv(method); | 矩阵求逆(默认使用DECONP_LU) |
m0.t(); | 矩阵求转置 |
m0( > < >= <= ==)m1; | 按元素进行比较,返回元素只有0和255的uchar类型矩阵 |
m0&m1;m0|m1;m0^m1;~m0; m0&s;s&m0;m0|s;s|m0;m0^s; s^m0; | 按位进行操作 |
min((m0,s),(m1, s, m0)); max(m0, m1);max(m0, s); max(s, m0); | 按元素取最大值最小值 |
cv::abs(m0); | 元素取绝对值 |
mo.cross(m1); m0.dot(m1); | 向量叉乘和点乘(叉乘只适用于3*1矩阵) |
cv::Mat::zeros(Nr, Nc, type);cv::Mat::ones(Nr, Nc, type);cv::Mat::eye(Nr, Nc, type); | 用于返回规定类型的N*N矩阵的静态方法 |
矩阵求逆操作inv()是一系列矩阵求逆操作的接口。
饱和转换
在Opencv中,有一些操作由溢出的风险,为了处理这种情况,实现了一个称为“饱和转换”的构造。
在进行算术或其他操作时会自动检查是否上溢出和下溢出,这个库函数会将结果值转换为相对最小或者最大的可行值。
模板函数是:cv::saturate_cast<>(),它允许你指定喜欢的类型来转换参数。
例:
uchar &vxy = m0.at<uchar>(y, x);
vxy = cv::saturate_cast<uchar>((vxy - 128) * 2 + 128);
数组还可以做很多事情
cv::Mat类的更多函数成员
示例 | 描述 |
m1 = m0.clone(); | 将m0复制给m1(所有元素,重新分配空间) |
m0.copyTo(m1); | 将m0复制给m1(所有元素,重新分配空间) |
m0.copyTo(m1, mask); | 将m0复制给m1,只复制mask指示区域 |
m0.convertTo(m1, type, scale, offset); | 转换m0中元素的类型,并在尺度变换(默认1)和增加偏置(默认0)之后赋值给m1 |
m0.assignTo(m1, type); | 只在内部使用(集成在convertTo中) |
m0.setTo(s, mask); | 设置所有元素为s,如果mask存在,则只操作指示区域 |
m0.reshape(chan, rows); | 改变二维数组的有效形状,如果chan和rows为0,则不更改 |
m0.push_back(s); | m*n的数组末尾增加m*1的数组 |
m0.push_back(m1); | m1大小为k*n,将m1增加到m0末尾 |
m0.pop_back(n); | m*n矩阵移除n行(默认为1) |
m0.locateROI(cv::Size size, cv::Point offset); | 将m0的全尺寸写入size,如果m0只是一小块区域,则会写入offset |
m0.adjustROI(t, b, l, r); | 通过四个值(上下左右)调整ROI范围 |
m0.total(); | 计算数组序列的元素的数目 |
m0.isContinuous(); | 如果m0的行之间没有间隔,返回true |
m0.elemSize(); | 返回m0的位长度(例:三通道浮点矩阵返回12) |
m0.elemSize1(); | 返回m0的最基本元素的位长度(例:三通道浮点矩阵返回4) |
m0.type(); | 返回m0元素的类型 |
m0.depth(); | 返回m0通道中的元素类型 |
m0.channels(); | 返回m0通道数目 |
m0.size(); | 以cv::Size 返回m0的大小 |
m0.empty(); | 如果数组没有元素,将返回true(比如m0.total == 0 & m0.data == NULL) |
稀疏数据类cv::SparesMat
cv::SparesMat类在数组非0元素非常少的情况下使用。
OpenCV的稀疏矩阵类cv::SparesMat的函数在很多方面都类似于稠密矩阵类cv::Mat, 它们的定义十分相似,支持大多数相同的操作,并且可以包含相同的数据类型。
稀疏矩阵内部内存为一个hash表,其值为0的元素其实并没有占用内存空间,只存储其值为非零的元素,值为0的元素不占用内存空间,同时为了保证查找速度快 内部使用一个hash表进程存储。
访问稀疏数组中的元素
稀疏数组和稠密数组最重要的区别:元素的访问。
稀疏数组提供四种访问机制:cv::SparesMat::ptr(), cv::SparesMat::ref(), cv::SparesMat::value() 和cv::SparesMat::find().
uchar *cv::SparesMat::ptr(int i0, bool createMissing, size_t* hashval = 0);
这个特定版本用于访问一维数组。
参数:
i0:请求元素的索引
createMissing:元素是否应该被创建(如果不存在,函数调用时则会创建该元素,并且一个合理的非0指针将指向这个新的元素,如果存在,它将直接返回指向这个元素的指针)
hashval:cv::SparesMat的诗句存储格式为哈希表,在cv::SparesMat::ptr()的例子中,如果参数hashval是默认的NULL,哈希key将被计算,如果提供一个key,他将会被使用。
cv::SparesMat::ptr()的变体允许使用两个或三个索引,也有第一个参数是一个整型数组指针的版本(const int* idx),它要求这个索引的元素数量和被访问的数组的维度一样多。
访问器模板函数cv::SparesMat::ref<>()用于返回一个指向数组中特定元素的引用,允许使用一个、两个或三个索引,或者一个索引数组,并且支持可选的用于查找的哈希值。
a_sparse_mat.ref<float>(i0, i1) += 1.0f;
模板方法cv::SparesMat::value<>()和 cv::SparesMat::ref<>()彼此独立,它将返回一个值而不是返回值的引用,因此这个方法也叫“只读方法”。
访问器函数cv::SparesMat::find<>()返回一个请求对象的指针。cv::SparesMat::find<>()的指针类型是由模板指定的,所以不需要再次转换。
迭代器访问稀疏数组的元素
模板化的迭代器是cv::SparseMatIterator_<>() and cv::SparseMatConstIterator_<>()(const形式的begin()和end()将返回const的迭代器)。
非模板化的迭代器:cv::SparseMatIterator() and cv::SparseMatConstIterator() ,它们将返回一个非模板的SparseMat::begin() 和 SparseMat::end()
//示例4-3:打印一个系数矩阵中的所有非零元素
int main()
{
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;
}
/* for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
sm1.ref<float>(i,j, hashval) = number*3.14;
number++;
}
}
*/
cv::SparseMatConstIterator_<float> it = sm.begin<float>();
cv::SparseMatConstIterator_<float> it_end = sm.end<float>();
for (; it != it_end; ++it)
{
const cv::SparseMat::Node* node = it.node();
cout << "(" << node->idx[0] << ", " << node->idx[1] << ")" << endl;
}
return EXIT_SUCCESS;
}
定义在迭代器中的方法node().node()返回一个指向被迭代其索引指向的系数矩阵的实际数据区域。
稀疏矩阵只保留矩阵中的非零数据,个人理解,不算一个完整矩阵,仅是完整矩阵的非零数据的索引矩阵。
node()将返回一个类型为cv::SparseMat::Node的对象:
struct Node
{
size_t hashval;
size_t next;
int idx[cv::MAX_DIM];
};
这个结构既包含指向对应元素的哈希值的索引(索引是一个int数组);还包含元素对应的哈希值。
稀疏数组中的特有函数
cv::SparseMat中额外的类成员函数
示例 | 描述 |
cv::SparseMat sm; | 创建一个稀疏矩阵 |
cv::SparseMat sm(int dims, const int *sz, int _type); | 创建一个由sz指定维度大小的n维稀疏浮点矩阵 |
cv::SparseMat sm(sm1); | 复制 |
cv::SparseMat(m0, try1d); | 从已有的稠密矩阵创建一个稀疏矩阵 |
cv::SparseMat(&old_sparse_mat); | 老版本:C风格稀疏矩阵创建一个稀疏矩阵 |
CVSparseMat* old_sm = (cv::SparseMat*) sm; | 创建一个老版本(2.1之前)稀疏矩阵 |
size_t n = sm.nzcount(); | 返回sm中的非0元素数量 |
size_t h = sm.hash(i0); | 返回稀疏矩阵中索引 所指向的数据哈希值 |
size_t h = sm.hash(i0, i1); | |
size_t h = sm.hash(i0,i1, i2); | |
size_t h = sm.hash(idx); | |
sm.ref<float>(i0) = f0; | 设置索引所指向元素的值为f0 |
sm.ref<float>(i0, i1) = f0; | |
sm.ref<float>(i0, i1, i2) = f0; | |
sm.ref<float>(idx) = f0; | |
f0 = sm.value<float>(i0); | 返回索引所指向元素的值 |
f0 = sm.value<float>(i0, i1); | |
f0 = sm.value<float>(i0, i1, i2); | |
f0 = sm.value<float>(idx); | |
p0 = sm.find<float>(i0); | 返回索引所指向元素的值 |
p0 = sm.find<float>(i0, i1); | |
p0 = sm.find<float>(i0, i1, i2); | |
p0 = sm.find<float>(idx); | |
sm.erase(i0, i1, &hashval); | 移除索引所指向的元素 |
sm.erase(i0, i1, i2, &hashval); | |
sm.erase(idx, &hashval); | |
cv::SparseMatIterator<float> it = sm.begin<float>(); | 创建一个浮点型稀疏矩阵迭代器it并指向其第一个元素 |
cv::SparseMatIterator<uchar> it_end = sm.end<uchar>(); | 创建一个无符号字符型稀疏矩阵迭代器 it_end 并将其初始化指向数组sm的最后一个元素的后一个元素 |
大型数组模板结构
使用模板cv::Mat_<> and cv::SparseMat_<> 的目的是:不必再使用其成员函数的时候调用其模板形式。
int main()
{
int i0, i1, x, y;
cv::Mat m(10, 10, CV_32FC2);
//访问元素时
cv::Vec2f(x, y) = m.at<cv::Vec2f>(i0, i1);
//使用模板
cv::Mat_<cv::Vec2f> m1(10, 10);
cv::Vec2f(x, y) = m1.at<cv::Vec2f>(i0,i1);
cv::Vec2f(x, y) = m1(i0, i1);
return EXIT_SUCCESS;
}
//4-4 打印矩阵
template <class T> void print_matrix( const cv::SparseMat_<T>* sm ) {
cv::SparseMatConstIterator_<T> it = sm->begin();
cv::SparseMatConstIterator_<T> it_end = sm->end();
for(; it != it_end; ++it) {
const typename cv::SparseMat_<T>::Node* node = it.node();
cout <<"( " <<node->idx[0] <<", " <<node->idx[1]
<<" ) = " <<*it <<endl;
}
}
void calling_function1( void ) {
int ndim = 2;
int size[] = {4,4};
cv::SparseMat_<float> sm( ndim, size );
// Create a sparse matrix with a few nonzero elements
//
for( int i=0; i<4; i++ ) { // Fill the array
int idx[2];
idx[0] = size[0] * rand();
idx[1] = size[1] * rand();
sm.ref( idx ) += 1.0f;
}
print_matrix<float>( &sm );
}
void calling_function2( void ) {
int ndim = 2;
int size[] = {4,4};
cv::SparseMat sm( ndim, size, CV_32F );
// Create a sparse matrix with a few nonzero elements
//
for( int i=0; i<4; i++ ) { // Fill the array
int idx[2];
idx[0] = size[0] * rand();
idx[1] = size[1] * rand();
sm.ref<float>( idx ) += 1.0f;
}
print_matrix<float>( (cv::SparseMat_<float>*) &sm );
}