OpenCV稀疏矩阵SparseMat

OpenCV中一般一张图片在内存中用Mat来表述及管理,Mat内部申请一块类似与数组的内存用于存储图片中的每个像素的值即为稠密矩阵,但是有时在矩阵中其值为零的元素远远多于非为零的元素个数即稀疏矩阵,如何此时还使用Mat进行存储 显然非常浪费空间,为了应对此中场景,OpenCV使用SparseMat类来应对稀疏矩阵场景,稀疏矩阵内部内存为一个hash表,其值为0的元素其实并没有占用内存空间,只存储其值为非零的元素,值为0的元素不占用内存空间,同时为了保证查找速度快 内部使用一个hash表进程存储。下面看来自于“Learning OpenCV3”中一段对cv::SparseMat优劣势说明

The cv::SparseMat class is used when an array is likely to be very large compared to the number of nonzero entries.This situation often arises in linear algebra with sparse matrices, but it also comes up when one wishes to represent data, particularly histograms. in higher-dimensional arrays, since most of the space will be empty in many practical applications.A sparse representation stores only data that is actually present and so can save a great deal pf memory.In practice, many sparse objects would be too huge to represent at all in a dense format.The disadvantage of sparse representations is that computation with them is slower(on a per-element basis). This last point is important, in that computation with sparse matrices is not categorically slower. as there can be a great economy in knowing in advance that many operations need not be done at all.

上文中指出,一个稠密性的矩阵Mat 也可以使用SparseMat来表示,只不会所使用hash 表会非常大,在相同稠密矩阵用Mat与SparseMat向比较而言,在矩阵计算中SparseMat与Mat相比会比较慢。同样一个稀疏矩阵也可以使用Mat来表示,在相同稀疏矩阵场景中,SparseMat会减少占用的内存,虽然在稠密性矩阵中SparseMat计算会比较慢,但是由于在稀疏矩阵中由于很多元素为0,其实并不会参与计算中,这部分减少的计算量在一定程度上能够抵消掉SparseMat计算性能慢的问题。

SparseMat类支持的操作与Mat的操作在很多地方都很相似,但是又有一些不一样的操作,分块进行分析。

SparseMat构造和拷贝函数

SparseMat类构造函数如下:

Method Description
 SparseMat() 默认构造函数
SparseMat(int dims, const int* _sizes, int _type)

使用数组参数构造函数,与mat类似

dims,为维度,_sizes为数组,表示每个维度大小,数组大小与维度一致。

_type代表的是数据类型,如:CV_8U 、CV_16U 、CV_32F等

parseMat(const SparseMat& m) 拷贝函数
explicit SparseMat(const Mat& m) 参数为Mat的拷贝函数,可以利用此根据mat生成一个SparseMat

在 SparseMat稀疏矩阵中,值为0的元素并不占用空间,所以在构造函数中初始值都为0,故实际上并未占用内存。

构造函数和拷贝函数用例:

const int dims = 2;
int size[dims] = { 10, 10 };

cv::SparseMat sm;
cv::SparseMat sm1(dims, size, CV_8U);
cv::SparseMat sm2(sm1);
cv:Mat  m1(460, 480, CV_8UC1);
cv::SparseMat sm3(m1);

获取相关函数

 SparseMat矩阵中获取相关系列函数接口,用于获取稀疏矩阵中的参数:

Method Description
int type()

获取矩阵元素类型,返回值为:

#define CV_8U   0
#define CV_8S   1
#define CV_16U  2
#define CV_16S  3
#define CV_32S  4
#define CV_32F  5
#define CV_64F  6
#define CV_USRTYPE1 7

int depth()

获取矩阵元素深度,其返回的其类型和Mat一致

-   CV_8U = 0 - 8-bit unsigned integers ( 0..255 )
  -   CV_8S = 1 - 8-bit signed integers ( -128..127 )
    -   CV_16U = 2 - 16-bit unsigned integers ( 0..65535 )
    -   CV_16S  = 3 - 16-bit signed integers ( -32768..32767 )
    -   CV_32S  = 4 - 32-bit signed integers ( -2147483648..2147483647 )
    -   CV_32F  = 5 - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )
    -   CV_64F = 6- 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )

   -   CV_USRTYPE1=7

int channels() 返回矩阵通道数目
const int* size() 返回矩阵大小,其返回值为int *,相当于数组,数组大小与维度相同,如果矩阵还未定义大小或还未申请内存则返回NULL
int dims() 返回矩阵维度
size_t nzcount() 返回矩阵中非零元素个数,相当于hash table的节点数
ize_t elemSize() 返回每个元素占用的字节数大小(带通道),elemSize1*channels
size_t elemSize1() 返回单个元素值占用的字节数大小,其大小为elemSize()/channels()

获取矩阵相关参数的API 大部分和Mat一样。

用例:

#include 
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;



void main()
{
	const int dims = 2;
	int size[dims] = { 10, 10 };
    
	int number = 0;

	cv::SparseMat sm;
	cv::SparseMat sm1(dims, size, CV_8U);
	cv::SparseMat sm2(sm1);
    cv:Mat  m1(460, 480, CV_32FC3);
	cv::SparseMat sm3(m1);

	cout << "sm type: " << sm.type() << endl;
	cout << "sm depth: " << sm.depth() << endl;
	cout << "sm channels: " << sm.channels() << endl;
	const int * pSize = sm.size();
	if (NULL == pSize)
	{
		cout << "sm size is empty" << endl;
	}

	cout << "sm1 type: " << sm1.type() << endl;
	cout << "sm1 depth: " << sm1.depth() << endl;
	cout << "sm1 channels: " << sm1.channels() << endl;
	const int * pSize1 = sm1.size();
	if (NULL == pSize1)
	{
		cout << "sm1 size is empty" << endl;
	}
	cout << "sm1 dims: " << sm1.dims() << endl;
	cout << "sm1 nzcount: " << sm1.nzcount() << endl;

	cout << "sm3 type: " << sm3.type() << endl;
	cout << "sm3 depth: " << sm3.depth() << endl;
	cout << "sm3 channels: " << sm3.channels() << endl;
	const int * pSize3 = sm1.size();
	if (NULL == pSize3)
	{
		cout << "sm3 size is empty" << endl;
	}

	cout << "sm3 dims: " << sm3.dims() << endl;
	cout << "sm3 nzcount: " << sm3.nzcount() << endl;
	cout << "sm3 elemSize: " << sm3.elemSize() << endl;
	cout << "sm3 elemSize1: " << sm3.elemSize1() << endl;
}

运行结果:

OpenCV稀疏矩阵SparseMat_第1张图片

SparseMat访问矩阵数据

由于SparseMat类种的矩阵数据存储方式是采用的hash表方式,与Mat矩阵采用数组方式进行存储不同,所以两者在矩阵的读写方式上也有很大不同,SparseMat类有自己特有的读取矩阵方式,分别为ptr、ref, find, value。

ptr读写数据方式

SparseMat支持指针访问方式,其输入参数分别为相应维度下面的index,直接返回元素的地址,这点与Mat ptr类似,但是返回的指针为单个元素的地址,而不能采用Mat中的ptr方式根据ptr指针可以获取后面所有的元素,仅仅返回的是相应元素的地址,不可以通过指针偏移的方式访问到下一个元素地址,这是与Mat中的ptr最大的不同。因为SparseMat矩阵中存储的并不是连续的,而是一张hash表。

SparseMat相关ptr的API,分别根据维度提供不同的API:

Method Description
 uchar* ptr(int i0, bool createMissing, size_t* hashval=0)

一维矩阵

i0:一维矩阵元素index

createMissing: 如果id0元素没有申请内存,是否申请内存。1:申请内存,0:不申请内存

返回值为元素地址,如果不申请内存,且元素为0,则返回值为NULL

hashval: hash 表key值。如果hashval为0,则在查找的过程中首先要根据i0,计算hash key值,再查找hash表。

             因为hash表查找过程,时间复杂度为O(1),性能都浪费在计算hash key值,如果想提高效率,可以提前计算获取到                hash值,能够提高算法效率

uchar* ptr(int i0, int i1, bool createMissing, size_t* hashval=0)

二维矩阵

i0和i1 两个维度下的index

uchar* ptr(int i0, int i1, int i2, bool createMissing, size_t* hashval=0)

三维矩阵

i0,i1和i2为各自维度下的index

uchar* ptr(const int* idx, bool createMissing, size_t* hashval=0) idx:将各维度采用数组方式,其数组大小必须与 矩阵维度一致。

针对返回值,OpenCV源代码中给出了详细解释:

specialized variants for 1D, 2D, 3D cases and the generic_type one for n-D case.
     return pointer to the matrix element.
      - if the element is there (it's non-zero), the pointer to it is returned
      - if it's not there and createMissing=false, NULL pointer is returned
      - if it's not there and createMissing=true, then the new element
        is created and initialized with 0. Pointer to it is returned
      - if the optional hashval pointer is not NULL, the element hash value is
        not computed, but *hashval is taken instead.

  •  当元素值不为0,且在hash表中存在,则指针直接返回改元素的地址
  • 当createMissing 设为false,且该元素不存在hash表中,则直接返回空指针NULL
  • 当createMissing 设为true,且该元素不存在hash表中,则将会为该元素申请一个新的空间,并初始化为0,并将该元素的地址返回
  • 如果hashval 参数不为空,则不用计算该元素的hash值,直接使用hashval 作为hash key,直接查表

 ptr相关用例

用例1:创建一个二维数组 10*10,数据类型为CV_8U, 使用ptr指针进行遍历,prt中参数createMissing=false 查看是否全部为空

#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	const int dims = 2;
	int size[dims] = { 10, 10 };
    
	int number = 0;

	cv::SparseMat sm;
	cv::SparseMat sm1(dims, size, CV_8U);

	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{		
			if (NULL == sm1.ptr(i, j, 0))
			{
				printf("sm1 is ptr\n");
			}			
	}
}

结果:

OpenCV稀疏矩阵SparseMat_第2张图片

用例2:创建一个二维数组 10*10,数据类型为CV_8U, 使用ptr指针进行遍历,prt中参数createMissing=true ,并对每个元素赋值:

#include 
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;



void main()
{
    const int dims = 2;
    int size[dims] = { 10, 10 };
    
    int number = 0;

    cv::SparseMat sm;
    cv::SparseMat sm1(dims, size, CV_8U);

    for (int i = 0; i < 10; i++)
    {
        for (int j = 0; j < 10; j++)
	{
	    *sm1.ptr(i, j, 1) = number;
	    number++;
	}
    }

    for (int i = 0; i < 10; i++)
    {
	for (int j = 0; j < 10; j++)
	{
	    printf("id0: %d, id1:%d, value:%d\n",i,j, *sm1.ptr(i, j, 1));
	}
    }

    cout << "sm1 element number:" << sm1.nzcount() << endl;
}

运行结果:

 

OpenCV稀疏矩阵SparseMat_第3张图片

 用例3:创建一个二维数组 10*10,数据类型为CV_32F, 使用ptr指针进行遍历,prt中参数createMissing=true ,并对每个元素赋值:

#include 
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	const int dims = 2;
	int size[dims] = { 10, 10 };
    
	int number = 0;

	cv::SparseMat sm;
	cv::SparseMat sm1(dims, size, CV_32F);

	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			*(float *)sm1.ptr(i, j, 1) = number*3.14;
			number++;
		}
	}

	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			printf("id0: %d, id1:%d, value:%f\n",i,j, *(float *)sm1.ptr(i, j, 0));
		}
	}

	cout << "sm1 element number:" << sm1.nzcount() << endl;

运行结果:

OpenCV稀疏矩阵SparseMat_第4张图片

  用例4:创建一个二维数组 10*10,数据类型为CV_32F, 使用ptr指针进行遍历,prt中参数createMissing=true ,并对每个元素赋值,使用hash值对每个元素进行遍历:

#include 
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;



void main()
{
	const int dims = 2;
	int size[dims] = { 10, 10 };
    
	int number = 0;

	cv::SparseMat sm;
	cv::SparseMat sm1(dims, size, CV_32F);

	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			*(float *)sm1.ptr(i, j, 1) = number*3.14;
			number++;
		}
	}

	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			size_t hash = sm1.hash(i, j);
			printf("id0: %d, id1:%d, value:%f\n",i,j, *(float *)sm1.ptr(i, j, false, &hash));
		}
	}

	cout << "sm1 element number:" << sm1.nzcount() << endl;
}

运行结果:

OpenCV稀疏矩阵SparseMat_第5张图片

 ref读写引用方式

ref为SparseMat矩阵中的元素另外一种访问方式,相当于*(_Tp*)ptr(i0,...,true[,hashval]) 方式,如果矩阵中的元素在hash表中不存在,则新申请一个并返回0,其API如下:

Method Description
template _Tp& ref(int i0, size_t* hashval=0) 一维数组访问
template _Tp& ref(int i0, int i1, size_t* hashval=0) 二维数组访问
template _Tp& ref(int i0, int i1, int i2, size_t* hashval=0) 三维数组访问
template _Tp& ref(const int* idx, size_t* hashval=0) 数组参数形式,其数组大小必须和dim一致

各个入参的意思和ptr参数一致,与ptr相比ref相当于一个模板能够适应各种数据类型,而ptr中需要根据相应的数据类型进行强转很容易出现错误,而ref可以避免这种错误。

ref用例

ref的大部分用法和ptr类似,列举一个10*10的矩阵,并对其赋值以及读取:

#include 
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;



void main()
{
	const int dims = 2;
	int size[dims] = { 10, 10 };
    
	int number = 0;

	cv::SparseMat sm;
	cv::SparseMat sm1(dims, size, CV_32F);

	size_t* hashval = NULL;

	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			sm1.ref(i,j, hashval) = number*3.14;
			number++;
		}
	}

	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			printf("id0: %d, id1:%d, value:%f\n",i,j, sm1.ref(i, j, hashval));
		}
	}

	cout << "sm1 element number:" << sm1.nzcount() << endl;

结果ptr中的用例一致。

find读取元素方式

find方式为只读取元素的方式,不可以对其元素赋值,相当于(_const Tp*)ptr(i0,...false[,hashval]), 返回的指针指向为一个常量,所以只可读不可写,相关API如下:

Method Description
 template const _Tp* find(int i0, size_t* hashval=0) const 一维矩阵
template const _Tp* find(int i0, int i1, size_t* hashval=0) const 二维矩阵
template const _Tp* find(int i0, int i1, int i2, size_t* hashval=0) const 三维矩阵
template const _Tp* find(const int* idx, size_t* hashval=0) const 参数为数组的读取函数,数组大小必须与dim一致

find方式只可读,不可写,返回为一个指针常量,find也是一个模板函数,支持常用的数据类型,如果该元素不存在,返回NULL.

find用例

列举一个10*10的矩阵,使用ref对矩阵进行赋值,用find读取元素。

#include 
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;



void main()
{
	const int dims = 2;
	int size[dims] = { 10, 10 };
    
	int number = 0;

	cv::SparseMat sm;
	cv::SparseMat sm1(dims, size, CV_32F);

	size_t* hashval = NULL;

	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			sm1.ref(i,j, hashval) = number*3.14;
			number++;
		}
	}

	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			printf("id0: %d, id1:%d, value:%f\n",i,j, *sm1.find(i, j, hashval));
		}
	}

	cout << "sm1 element number:" << sm1.nzcount() << endl;
}

运行结果和ptr用例一致。

value读取元素方式

value方式为另外一种读取矩阵元素的方式,也是只可读不可写,相当于

 const _Tp* p = find<_Tp>(i0,...[,hashval]); return p ? *p : _Tp();

与find不同的是,函数直接返回的是元素的值而不是指针, API如下:

Method Description
template _Tp value(int i0, size_t* hashval=0) const 一维元素读取
template _Tp value(int i0, int i1, size_t* hashval=0) const 二维元素读取
template _Tp value(int i0, int i1, int i2, size_t* hashval=0) const 三维元素读取
template _Tp value(const int* idx, size_t* hashval=0) const 入参为数组的元素读取,数组大小必须与dim一直

 value函数用法与find一样

value用例

列举一个10*10的矩阵,使用ref对矩阵进行赋值,用value读取元素。

#include 
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;



void main()
{
	const int dims = 2;
	int size[dims] = { 10, 10 };
    
	int number = 0;

	cv::SparseMat sm;
	cv::SparseMat sm1(dims, size, CV_32F);

	size_t* hashval = NULL;

	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			sm1.ref(i,j, hashval) = number*3.14;
			number++;
		}
	}

	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			printf("id0: %d, id1:%d, value:%f\n",i,j, sm1.value(i, j, hashval));
		}
	}

	cout << "sm1 element number:" << sm1.nzcount() << endl;
}

value函数用法与find一样

迭代访问

SparseMat还支持利用迭代器对数组进行迭代访问,SparseMat中的迭代只能变量访问hash表,并不能创建新的hash表。

共有四个迭代器SparseMatIterator、SparseMatIterator_、SparseMatConstIterator、SparseMatConstIterator_

Method Description
SparseMatIterator begin() SparseMatIterator迭代器开始
template SparseMatIterator_<_Tp> begin() SparseMatIterator_迭代器模板开始
SparseMatConstIterator begin() const SparseMatConstIterator迭代器开始
template SparseMatConstIterator_<_Tp> begin() const SparseMatConstIterator_迭代器开始
 SparseMatIterator end() SparseMatIterator 迭代器结束位置
SparseMatConstIterator end() SparseMatConstIterator 迭代器结束位置
template SparseMatIterator_<_Tp> end() SparseMatIterator_迭代器结束位置
template SparseMatConstIterator_<_Tp> end() SparseMatConstIterator_迭代器结束
 template _Tp& value(Node* n) 根据迭代节点获取相应元素值 模板,可读可改
template const _Tp& value(const Node* n) 根据迭代器节点获取相应元素值,只可读不可改

SparseMat中每个迭代器都有一个迭代节点,每个节点Node记录相对应元素的hashval值,以及对应的元素在矩阵中的位置,Node数据结构如下:

    struct CV_EXPORTS Node
    {
        //! hash value
        size_t hashval;
        //! index of the next node in the same hash table entry
        size_t next;
        //! index of the matrix element
        int idx[MAX_DIM];
    };

可以根据Node中的hashval值直接查找到对应元素的值,或者根据封装好的value(Node* n)节点获取到相对应的值。

迭代用例

迭代用例如下:

#include 
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;



void main()
{
	const int dims = 2;
	int size[dims] = { 10, 10 };
    
	int number = 0;

	cv::SparseMat sm;
	cv::SparseMat sm1(dims, size, CV_32F);

	size_t* hashval = NULL;
	
	
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			sm1.ref(i, j, hashval) = number*3.14;
			number++;
		}
	}

	cv::SparseMatIterator_ it = sm1.begin();
	cv::SparseMatIterator_ it_end = sm1.end();
	for (; it != it_end; ++it)
	{	
		const cv::SparseMat::Node * node = it.node();
		printf("id0: %d, id1:%d, value:%f\n", node->idx[0], node->idx[1], sm1.value(it.node()));
		++number;
	}
	

	cout << "sm1 element number:" << sm1.nzcount() << endl;
}

运行结果:

OpenCV稀疏矩阵SparseMat_第6张图片

可以看到节点的存储并不是按照矩阵的索引进行排序。 

hash获取hash值

SparseMat提供接口根据索引index获取到hash表中对应的hash key值,API如下:

Method Description
 size_t hash(int i0)

一维矩阵获取hash key

i0:矩阵index

size_t hash(int i0, int i1)

二维矩阵获取hash key

i0和i1为二维矩阵的各维度的索引

 size_t hash(int i0, int i1, int i2)

三维矩阵获取hash key

i0、i1和i2为三维矩阵各维度的索引

size_t hash(const int* idx)

参数为数组形式的获取hash key,

其中数组大小必须与维度一致。

hash()函数用法相对比较简单,提前获取到hash key值,在获取或设置hash表中的值时,配合ptr, ref等函数时可以节约根据索引计算hash值得时间,从而提高效率。

erase删除hash表某个元素

SparseMat与Mat中还有一个最大的不同,就是可以根据需要删除hash表中的某个元素,API:

Method Description
void erase(int i0, int i1, size_t* hashval=0) 二维数组删除hash表中的某个元素
void erase(int i0, int i1, int i2, size_t* hashval=0) 三维数组删除hash表中的元素
void erase(const int* idx, size_t* hashval=0) 数组形式删除hash表中的元素

erase用例

#include 
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;



void main()
{
	const int dims = 2;
	int size[dims] = { 10, 10 };
    
	int number = 0;

	cv::SparseMat sm;
	cv::SparseMat sm1(dims, size, CV_32F);

	size_t* hashval = NULL;
	
	
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			sm1.ref(i, j, hashval) = number*3.14;
			number++;
		}
	}

	cout << "sm1 element number:" << sm1.nzcount() << endl;
	cout << "Delete all element..." << endl;

	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			sm1.erase(i, j);
			number++;
		}
	}
	

	cout << "sm1 element number:" << sm1.nzcount() << endl;
}

 运行结果:

 clear清除hash表

SparseMat矩阵支持clear一次性清除所有hash表,将所有元素值清为0,并释放所有hash表内存

void clear();

SparseMat与Mat相互转换

SparseMat支持与稠密矩阵Mat转换,API如下:

Method Description
void copyTo( Mat& m ) 将SparseMa矩阵转成Mat矩阵
void convertTo( Mat& m, int rtype, double alpha=1, double beta=0 )

将SparseMat 矩阵转成Mat矩阵m, rtype为转换的矩阵type,

alpha和beta是否需要将值进行转换计算,计算公式如下:

alpha* element + beta

alpha默认为1, beta默认为0,相当于默认情况下时等值转换,此时相当于copyTo

 SparseMat(const Mat& m) 根据Mat 创建 SparseMat矩阵

Mat矩阵转成SparseMat用例 

创建一个640*480,通道为1的uchar的Mat,其值为0,将其转换到SparseMat

#include 
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;



void main()
{
    cv:Mat  m1(460, 480, CV_8UC1);
	cv::SparseMat sm1(m1);
	
	cout << "sm1 element number:" << sm1.nzcount() << endl;
}

 根据Mat创建SparseMat矩阵时,即使Mat的数据全为0,SparseMat矩阵也会创建内存空间,运行结果:

sm1 element number:220800

SparseMat矩阵转换成Mat 用例:

copyTo和convertTo都支持转换成Mat,copyTo至少相当于copy,而convertTo支持修改输出的Mat类型及值,如果Mat没有内存空间,则首先申请内存空间,然后再进行转换

#include 
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;



void main()
{
	const int dims = 2;
	int size[dims] = { 10, 10 };
    
	int number = 0;

	cv::SparseMat sm1(dims, size, CV_32F);

	size_t* hashval = NULL;
	
	
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			sm1.ref(i, j, hashval) = number*3.14;
			number++;
		}
	}

	Mat m1;
	Mat m2;
	sm1.copyTo(m1);
	sm1.convertTo(m2, CV_32F);

	cout << "m1 element number:" << m1.total() << endl;
	cout << "m2 element number:" << m2.total() << endl;
}

运行结果:

 m1 element number:100
         m2 element number:100

copyTo和convertTo

SparseMat还支持SparseMat函数copy,以及根据源SparseMat 生成目的SparseMat,且修改目的SparseMat的类型

Method Description
copyTo( SparseMat& m ) copy到矩阵m,如果m矩阵中之前存在内容,则将旧的全部清空
 void convertTo( SparseMat& m, int rtype, double alpha=1 ) 

将所有的矩阵转成成按照需要的rtype,且所有矩阵的值乘以一个系数alpha,可以用来在转换过程中数值越界

alpha默认为1

用例

上述两个函数相关用例:

#include 
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;



void main()
{
	const int dims = 2;
	int size[dims] = { 10, 10 };
    
	int number = 0;

	cv::SparseMat sm1(dims, size, CV_32F);

	size_t* hashval = NULL;
	
	
	for (int i = 0; i < 10; i++)
	{
		for (int j = 0; j < 10; j++)
		{
			sm1.ref(i, j, hashval) = number*3.14;
			number++;
		}
	}

	cv::SparseMat sm2;
	cv::SparseMat sm3;
	sm1.copyTo(sm2);
	sm1.convertTo(sm3, CV_32F);
	cout << "sm1 element number:" << sm1.nzcount() << endl;
	cout << "sm2 element number:" << sm2.nzcount() << endl;
	cout << "sm3 element number:" << sm3.nzcount() << endl;
}

运行结果:

sm1 element number:100
       sm2 element number:100
       sm3 element number:100

 SparseMat矩阵头

SparseMat内部使用hdr用于存储矩阵头相关信息,矩阵头主要内容如下:

   struct CV_EXPORTS Hdr
    {
        Hdr(int _dims, const int* _sizes, int _type); //Hdr构造函数
        void clear(); //一次性清除所有hash
        int refcount;//hashtabe内存引用计数,当为0使,直接是否内存
        int dims; //矩阵维度
        int valueOffset;// node不包含hash 值大小
        size_t nodeSize; //node 占用空间大小,包含hash 值
        size_t nodeCount;//node节点数目
        size_t freeList;
        std::vector pool;//node 节点地址
        std::vector hashtab; //hashtabe
        int size[MAX_DIM];
    };

  operator = 重构

SparseMat矩阵支持=等号重构,如下:

    //! assignment operator. This is O(1) operation, i.e. no data is copied
    SparseMat& operator = (const SparseMat& m);
    //! equivalent to the corresponding constructor
    SparseMat& operator = (const Mat& m);

支持 SparseMat两个矩阵直接赋值,也支持等号右值为Mat矩阵

 

你可能感兴趣的:(#,OpenCV笔记)