让代码更简单

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

重要:本文最后更新于2019-02-18 08:53:16,某些文章具有时效性,若有错误或已失效,请在下方留言或联系代码狗

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

CvFileStorage

文件存储结构

复制
typedef struct CvFileStorage
{
    ...       // hidden fields
} CvFileStorage;

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

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

XML:

复制
<?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:

复制
%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

文件存储器节点

复制
/* 文件节点类型 */
#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函数时 那么对象将请求被解析 (按照原来的存储方式)。

感觉很棒!可以赞赏支持我哟~

0 打赏

评论 (0)

登录后评论
QQ咨询 邮件咨询 狗哥推荐