OpenCV 文件存储结构函数 CvFileStorage 与文件存储器节点函数 CvFileNode 详解

我们在做好图像采集工作后,有时需要将图像特征或者视觉检测结果保存下来。由于保存的数据具有一定规律性,因此我们可以采用 opencv 提供的文件存储类函数实现序列化数据,并保存为文件。当然你要是不觉得麻烦,完全可以自己重写数据序列化方法,不必采用 opencv 提供的函数。下面是 OpenCV 中文件存储结构函数 CvFileStorage 与文件存储器节点函数 CvFileNode 的函数构成,及简单使用方法。

我们在做好图像采集工作后,有时需要将图像特征或者视觉检测结果保存下来。由于保存的数据具有一定规律性,因此我们可以采用 opencv 提供的文件存储类函数实现序列化数据,并保存为文件。当然你要是不觉得麻烦,完全可以自己重写数据序列化方法,不必采用 opencv 提供的函数。下面是 OpenCV 中文件存储结构函数 CvFileStorage 与文件存储器节点函数 CvFileNode 的函数构成,及简单使用方法。

 

CvFileStorage

 

文件存储结构

 

PHP

 

1

typedef struct CvFileStorage { … // hidden fields } CvFileStorage;

构造函数 CvFileStorage 是将磁盘上存储的文件关联起来的“黑匣子” 。在下列函数描述中利用 CvFileStorage 作为输入,允许存储或载入各种格式数据组成的层次集合,这些数据由标量值(scalar ),或者 CXCore 对象(例如 矩阵,序列,图表 ) 和用户自定义对象。

 

CXCore 能将数据读入或写入 XML 或者 YAML 格式. 下面这个例子是利用 CXCore 函数将 3×3 单位浮点矩阵存入 XML 和 YAML 文档。

 

XML:

 

PHP

 

1

<?xml version=”1.0″> <opencv_storage> <A type_id=”opencv-matrix”> <rows>3</rows> <cols>3</cols> <dt>f</dt> <data>1. 0. 0. 0. 1. 0. 0. 0. 1.</data> </A> </opencv_storage>

YAML:

 

PHP

 

1

%YAML:1.0 A: !!opencv-matrix rows: 3 cols: 3 dt: f data: [ 1., 0., 0., 0., 1., 0., 0., 0., 1.]

从例子中可以看到, XML 是用嵌套标签来表现层次,而 YAML 用缩排来表现(类似于 Python 语言) 。

 

相同的 CXCore 函数也能够在这两种格式下读写数据,特殊的格式决定了文件的扩展名, .xml 是 XML 的扩展名, .yml 或 .yaml 是 YAML 的扩展名。

 

CvFileNode

 

文件存储器节点

 

PHP

 

1

/* 文件节点类型 */ #define CV_NODE_NONE 0 #define CV_NODE_INT 1 #define CV_NODE_INTEGER CV_NODE_INT #define CV_NODE_REAL 2 #define CV_NODE_FLOAT CV_NODE_REAL #define CV_NODE_STR 3 #define CV_NODE_STRING CV_NODE_STR #define CV_NODE_REF 4 /* not used */ #define CV_NODE_SEQ 5 #define CV_NODE_MAP 6 #define CV_NODE_TYPE_MASK 7 /* 可选标记 */ #define CV_NODE_USER 16 #define CV_NODE_EMPTY 32 #define CV_NODE_NAMED 64 #define CV_NODE_TYPE(tag) ((tag) & CV_NODE_TYPE_MASK) #define CV_NODE_IS_INT(tag) (CV_NODE_TYPE(tag) == CV_NODE_INT) #define CV_NODE_IS_REAL(tag) (CV_NODE_TYPE(tag) == CV_NODE_REAL) #define CV_NODE_IS_STRING(tag) (CV_NODE_TYPE(tag) == CV_NODE_STRING) #define CV_NODE_IS_SEQ(tag) (CV_NODE_TYPE(tag) == CV_NODE_SEQ) #define CV_NODE_IS_MAP(tag) (CV_NODE_TYPE(tag) == CV_NODE_MAP) #define CV_NODE_IS_COLLECTION(tag) (CV_NODE_TYPE(tag) >= CV_NODE_SEQ) #define CV_NODE_IS_FLOW(tag) (((tag) & CV_NODE_FLOW) != 0) #define CV_NODE_IS_EMPTY(tag) (((tag) & CV_NODE_EMPTY) != 0) #define CV_NODE_IS_USER(tag) (((tag) & CV_NODE_USER) != 0) #define CV_NODE_HAS_NAME(tag) (((tag) & CV_NODE_NAMED) != 0) #define CV_NODE_SEQ_SIMPLE 256 #define CV_NODE_SEQ_IS_SIMPLE(seq) (((seq)->flags & CV_NODE_SEQ_SIMPLE) != 0) typedef struct CvString { int len; char* ptr; } CvString; /*所有已读存储在文件元素的关键字被存储在hash表中,这样可以加速查找操作 */ typedef struct CvStringHashNode { unsigned hashval; CvString str; struct CvStringHashNode* next; } CvStringHashNode; /* 文件存储器的基本元素是-标量或集合*/ typedef struct CvFileNode { int tag; struct CvTypeInfo* info; /* 类型信息(只能用于用户自定义对象,对于其它对象它为0) */ union { double f; /* 浮点数*/ int i; /* 整形数 */ CvString str; /* 字符文本 */ CvSeq* seq; /* 序列 (文件节点的有序集合) */ struct CvMap* map; /*图表 (指定的文件节点的集合 ) */ } data; } CvFileNode;

这个构造函数只是用于重新找到文件存储器上的数据(例如 ,从文件中下载数据)。 当数据已经写入文件时,按顺序写入,只用最小的缓冲完成,此时没有数据存放在文件存储器。

 

相反,当从文件中读数据时,所有文件在内存中像树一样被解析和描绘。树的每一个节点被 CvFileNode 表现出来。文件节点 N 的类型能够通过 CV_NODE_TYPE(N->tag) 被重新找到。一些节点(叶结点)作为变量:字符串文本,整数,浮点数。其它的文件节点是集合文件节点,有两个类型集合:序列和图表 (我们这里使用 YAML 符号,无论用哪种方法,对于 XML 符号流也是同样有效)。序列(不要与 CvSeq 混淆) 是由有序的非指定文件节点(注:没有关键字)构成的,图表是由无序的指定文件节点(注:有关键字)构成的。因而 ,序列的数据是通过索引(cvGetSepElem)来存取,图形的数据是通过名字(cvGetFileNodeByName)来存取 下表描述不同类型的节点:

 

Type CV_NODE_TYPE(node->tag) Value

Integer CV_NODE_INT node->data.i

Floating-point CV_NODE_REAL node->data.f

Text string CV_NODE_STR node->data.str.ptr

Sequence CV_NODE_SEQ node->data.seq

Map CV_NODE_MAP node->data.map*

这里不需要直接存取图表内容(顺便说一下 CvMap 是一个隐藏的构造函数)。图形中的数据可以用 cvGetFileNodeByName 函数得到,函数返回指向图表文件节点的指针。

一个用户对象是一个标准的类型实例,例如 CvMat, CvSeq 等,或者任何一个已注册的类型使用 cvRegisterTypeInfo。这样的对象最初在文件中表现为一种层级关系,(像表现 XML 和 YAM 示例文件一样) 。在文件存储器打开并分析之后。当用户调用 cvRead 或 cvReadByName 函数时 那么对象将请求被解析 (按照原来的存储方式)。