五、cv::SparseMat的介绍和使用
文章目录
- 一、cv::SparseMat的概述
- 二、稀疏数组的创建
- 2.1 基本语法
- 2.2 创建稀疏数组的几种方式
- 2.3 设置元素值(赋值)
- 2.4 完整示例:创建 + 赋值 + 遍历
- 三、稀疏数组的访问方法
- 3.1 ref<T>(const int* idx)
- 3.2 find<T>(const int* idx)
- 3.3 ptr<T>(Node* node)
- 3.4 cv::SparseMat::value\<T>()
- 四、示例操作
一、cv::SparseMat的概述
cv::SparseMat 是 OpenCV 中用于 表示稀疏矩阵(Sparse Matrix)的类。和 cv::Mat 用于存储 稠密矩阵(Dense Matrix)不同,cv::SparseMat 更适合用来表示那些 大多数元素为零或空值 的多维数组。
适用场景:
- 多维数组(如高维空间向量)。
- 大部分值为默认值(0)的矩阵。
- 需要节省内存,且不频繁对所有元素进行访问。
基本特性:
- 支持任意维度(不像 cv::Mat 只支持 2D)。
- 仅存储非零值(以哈希表形式实现)。
- 不支持连续内存访问(不像 cv::Mat 支持指针连续区域)。
二、稀疏数组的创建
2.1 基本语法
cv::SparseMat::SparseMat(int dims, const int* sizes, int type);
- dims:维度数量(比如2D就是2,3D就是3)。
- sizes:每一维的大小,是一个数组。
- type:OpenCV 类型(如 CV_32F、CV_8U 等)。
2.2 创建稀疏数组的几种方式
示例1:创建一个 2D 的稀疏矩阵(5x5,类型 float)
int sizes[] = {5, 5}; // 表示 2D:5 行 5 列
cv::SparseMat sparseMat(2, sizes, CV_32F);
示例2:创建一个 3D 稀疏矩阵(10×10×10)
int sizes[] = {10, 10, 10};
cv::SparseMat sparse3D(3, sizes, CV_8U);
示例3:使用 cv::Mat 的类型宏
int sizes[] = {8, 8};
cv::SparseMat sparseMat(2, sizes, CV_64FC1); // 64位浮点数
2.3 设置元素值(赋值)
int idx[] = {2, 3}; // 设置 [2][3] 处的值
sparseMat.ref<float>(idx) = 1.23f;
注意:
- ref() 会在访问位置上自动创建元素。
- 若该位置已存在,则直接修改其值。
2.4 完整示例:创建 + 赋值 + 遍历
#include <opencv2/opencv.hpp>
#include <iostream>int main() {int sizes[] = {5, 5};cv::SparseMat sm(2, sizes, CV_32F);// 设置几个稀疏元素int idx1[] = {0, 1};int idx2[] = {2, 3};sm.ref<float>(idx1) = 10.0f;sm.ref<float>(idx2) = 20.5f;// 遍历所有非零项for (cv::SparseMatConstIterator it = sm.begin(); it != sm.end(); ++it) {const cv::SparseMat::Node* node = it.node();const float* val = it.ptr<float>();std::cout << "Index: [" << node->idx[0] << "][" << node->idx[1] << "]"<< " = " << *val << std::endl;}return 0;
}
三、稀疏数组的访问方法
3.1 ref(const int* idx)
功能:返回对某个位置元素的引用,如果该元素不存在,则创建并返回默认值(0)。
示例:
int idx[] = {1, 2};
sparseMat.ref<float>(idx) = 3.14f; // 如果不存在,会自动创建
3.2 find(const int* idx)
功能:返回该位置的值,如果不存在,返回 默认值(0),不会创建新元素。
示例:
int idx[] = {1, 2};
float value = sparseMat.find<float>(idx);
3.3 ptr(Node* node)
功能:给定一个已存在的 Node* 节点,返回其值的指针。
示例:
for (cv::SparseMatConstIterator it = sparseMat.begin(); it != sparseMat.end(); ++it) {const float* val = it.ptr<float>(); // 获取值
}
3.4 cv::SparseMat::value<T>()
功能:用于访问 稀疏矩阵中当前迭代器位置处的值 的函数。它常用于与 cv::SparseMatConstIterator 或 cv::SparseMatIterator 一起,在遍历稀疏矩阵的所有非零元素时进行读取。
用于遍历 cv::SparseMat 的元素:
for (cv::SparseMatConstIterator it = sm.begin(); it != sm.end(); ++it) {float v = it.value<float>();// 使用 v 进行逻辑处理
}
与此等价的访问方式是:
const float* ptr = it.ptr<float>();
float v = *ptr;
但 value() 更简洁,适合只读场景。
示例:使用 value() 遍历稀疏矩阵
#include <opencv2/opencv.hpp>
#include <iostream>int main() {int sizes[] = {3, 3};cv::SparseMat sm(2, sizes, CV_32F);int idx1[] = {0, 1};int idx2[] = {2, 2};sm.ref<float>(idx1) = 5.0f;sm.ref<float>(idx2) = 10.0f;std::cout << "稀疏矩阵中所有非零元素:" << std::endl;for (cv::SparseMatConstIterator it = sm.begin(); it != sm.end(); ++it) {const cv::SparseMat::Node* node = it.node();float value = it.value<float>(); // 👈 使用 value() 获取值std::cout << "Index: [" << node->idx[0] << "][" << node->idx[1]<< "] = " << value << std::endl;}return 0;
}
四、示例操作
示例:创建一个 500x500 的单通道稀疏图像,并设置部分像素
#include <opencv2/opencv.hpp>
#include <iostream>int main() {// 定义尺寸int sizes[] = {500, 500};// 创建单通道 8 位稀疏矩阵cv::SparseMat sparseImg(2, sizes, CV_8UC1);// 设置部分像素值为非零uchar value = 255;sparseImg.ref<uchar>(100, 100) = value; // 设置点 (100, 100) 为白色sparseImg.ref<uchar>(200, 200) = value;sparseImg.ref<uchar>(300, 300) = value;// 将稀疏图像转换为普通图像用于显示cv::Mat denseImg(500, 500, CV_8UC1, cv::Scalar(0)); // 全部为0cv::SparseMatConstIterator it = sparseImg.begin();cv::SparseMatConstIterator it_end = sparseImg.end();for (; it != it_end; ++it) {const cv::SparseMat::Node* node = it.node();int x = node->idx[0];int y = node->idx[1];denseImg.at<uchar>(x, y) = it.value<uchar>();}// 显示图像cv::imshow("Sparse to Dense Image", denseImg);cv::waitKey(0);return 0;
}
输出结果:
说明:
- cv::SparseMat(2, sizes, CV_8UC1):创建一个 2 维稀疏矩阵,类型为单通道 8 位。
- ref<uchar>(x, y):返回对稀疏矩阵中位置 (x, y) 的引用,若不存在则创建。
为了显示,为什么必须将稀疏图像转成稠密图像?
因为 OpenCV 的图像显示函数(如 cv::imshow)只支持稠密矩阵(cv::Mat),不支持稀疏矩阵(cv::SparseMat)。
原因详解:
1、疏矩阵不是图像格式:cv::SparseMat 本质上是一个稀疏存储结构,它只记录非零位置的索引和值,其内存布局和普通图像的连续二维像素数据完全不同。它并不知道一个“图像”的概念,也无法提供完整的像素数组供 GUI 函数使用。
例如:
sparseMat.ref<uchar>(100, 100) = 255;
这只是记录了 (100, 100) 位置为 255,其余像素都“假设”为 0,实际上并不存在于内存中。
2、imshow 需要一个完整的、连续的像素块:cv::imshow(name, image) 需要图像尺寸、每个像素在内存中的位置、通道、位深度、步长(stride)。这些信息 cv::Mat 都提供了,但 cv::SparseMat 由于数据稀疏、结构不连续,根本无法满足这些要求。