[] 文件存储]CvFileStorage文件存储结构 typedef struct CvFileStorage { ... // hidden fields } CvFileStorage; 构造函数 CvFileStorage 是将磁盘上存储的文件关联起来的“黑匣子” 。在下列函数描述中利用CvFileStorage 作为输入,允许存储或载入各种格式数据组成的层次集合,这些数据由标量值(scalar ),或者CXCore 对象(例如 矩阵,序列,图表 ) 和用户自定义对象。 CXCore 能将数据读入或写入 XML (http://www./XML) or YAML (http://www.) 格式. 下面这个例子是利用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)来存取 下表描述不同类型的节点:
一个用户对象是一个标准的类型实例,例如CvMat, CvSeq等,或者任何一个已注册的类型使用cvRegisterTypeInfo。这样的对象最初在文件中表现为一种层级关系,(像表现XML 和 YAM示例文件一样) 。在文件存储器打开并分析之后。当用户调用cvRead或cvReadByName函数时 那么对象将请求被解析 (按照原来的存储方式)。 [编辑] CvAttrList属性列表 typedef struct CvAttrList { const char** attr; /* NULL-指向数组对(attribute_name,attribute_value) 的空指针 */ struct CvAttrList* next; /* 指向下一个属性块的指针 */ } CvAttrList; /* 初始化构造函数CvAttrList */ inline CvAttrList cvAttrList( const char** attr=NULL, CvAttrList* next=NULL ); /* 返回值为属性值,找不到适合的属性则返回值为0(NULL)*/ const char* cvAttrValue( const CvAttrList* attr, const char* attr_name ); 在当前版本的属性列表用来传递额外的参数,在使用cvWrite写入自定义数据对象时。除了对象类型说明(type_id 属性)以外,它不支持 XML 在标签内的属性(注:例如<A name="test"></A>不支持)。 [编辑] OpenFileStorage打开文件存储器读/写数据。 CvFileStorage* cvOpenFileStorage( const char* filename, CvMemStorage* memstorage, int flags );
函数cvOpenFileStorage打开文件存储器读写数据,之后建立文件或继续使用现有的文件 。文件扩展名决定读文件的类型 : .xml 是 XML的扩展名, .yml 或 .yaml 是 YAML的扩展名。该函数的返回指针指向CvFileStorage结构。 [编辑] ReleaseFileStorage释放文件存储单元 void cvReleaseFileStorage( CvFileStorage** fs );
函数cvReleaseFileStorage 关闭一个相关的文件存储器并释放所有的临时内存。只有在内存的I/O操作完成后才能关闭文件存储器。 [编辑] 写数据[编辑] StartWriteStruct向文件存储器中写数据 void cvStartWriteStruct( CvFileStorage* fs, const char* name, int struct_flags, const char* type_name=NULL, CvAttrList attributes=cvAttrList());
函数 cvStartWriteStruct 开始写复合的数据结构(数据集合)包括序列或图表, 在结构体中所有的字段(可以是标量和新的结构)被写入后, 需要调用 cvEndWriteStruct . 该函数能够合并一些对象或写入一些用户对象(参考 CvTypeInfo )。 [编辑] EndWriteStruct结束数据结构的写操作 void cvEndWriteStruct( CvFileStorage* fs );
函数cvEndWriteStruct 结束普通的写数据操作。 [编辑] WriteInt写入一个整型值 void cvWriteInt( CvFileStorage* fs, const char* name, int value );
函数 cvWriteInt 将一个单独的整型值(有符号的或无符号的)写入文件存储器。 [编辑] WriteReal写入一个浮点数 void cvWriteReal( CvFileStorage* fs, const char* name, double value );
函数 cvWriteReal 将一个单精度浮点数(有符号的或无符号的)写入文件存储器。 一些特殊的值以特殊的编码表示: NaN 表示不是数字 +.Inf 表示正无穷 -.Inf 表示负无穷 下面的实例展示 怎样使用底层写函数存储自定义数据结构。 void write_termcriteria( CvFileStorage* fs, const char* struct_name, CvTermCriteria* termcrit ) { cvStartWriteStruct( fs, struct_name, CV_NODE_MAP, NULL, cvAttrList(0,0)); cvWriteComment( fs, "termination criteria", 1 ); if( termcrit->type & CV_TERMCRIT_ITER ) cvWriteInteger( fs, "max_iterations", termcrit->max_iter ); if( termcrit->type & CV_TERMCRIT_EPS ) cvWriteReal( fs, "accuracy", termcrit->epsilon ); cvEndWriteStruct( fs ); } [编辑] WriteString写入文本字符串 void cvWriteString( CvFileStorage* fs, const char* name, const char* str, int quote=0 );
函数 cvWriteString将文本字符串写入文件存储器。 [编辑] WriteComment写入注释 void cvWriteComment( CvFileStorage* fs, const char* comment, int eol_comment );
函数 cvWriteComment将注释写入文件存储器。读内存时注释将被跳过,它只能被用于调试和查看描述。 [编辑] StartNextStream打开下一个数据流 void cvStartNextStream( CvFileStorage* fs );
函数 cvStartNextStream 从文件存储器中打开下一个数据流。 YAML 和 XML 都支持多数据流。这对连接多个文件和恢复写入的程序很有用。 [编辑] Write写入用户对象 void cvWrite( CvFileStorage* fs, const char* name, const void* ptr, CvAttrList attributes=cvAttrList() );
函数 cvWrite将对象写入文件存储器 。首先,使用cvTypeOf 查找恰当的类型信息。其次写入指定的方法类型信息。 属性被用于定制写入程序。下面的属性支持标准类型 (所有的*dt 属性在cvWriteRawData中都有相同的格式):
下面的代码的含义是建立YAML文件用来描述CvFileStorage : #include "cxcore.h" int main( int argc, char** argv ) { CvMat* mat = cvCreateMat( 3, 3, CV_32F ); CvFileStorage* fs = cvOpenFileStorage( "example.yml", 0, CV_STORAGE_WRITE ); cvSetIdentity( mat ); cvWrite( fs, "A", mat, cvAttrList(0,0) ); cvReleaseFileStorage( &fs ); cvReleaseMat( &mat ); return 0; } [编辑] WriteRawData写入基本数据数组 void cvWriteRawData( CvFileStorage* fs, const void* src, int len, const char* dt );
函数 cvWriteRawData 将数组写入文件存储器,数组由单独的数值构成。这个函数也可以用循环调用 cvWriteInt 和 cvWriteReal 替换,但是一个单独的函数更加有效。需要说明的是,那是因为元素没有名字, 把它们写入序列(无名字)比写入图表(有名字关联)速度会快。 [编辑] WriteFileNode将文件节点写入另一个文件存储器 void cvWriteFileNode( CvFileStorage* fs, const char* new_node_name, const CvFileNode* node, int embed );
函数 cvWriteFileNode将一个文件节点的拷贝写入文件存储器 可能应用范围是: 将几个文件存储器合而为一。在XML 和YAML 之间变换格式等。 [编辑] 读取数据从文件存储器中得到数据有两种步骤:首先查找文件节点包括哪些被请求的数据;然后利用手动或者使用自定义read 方法取得数据。 [编辑] GetRootFileNode从文件存储器中得到一个高层节点 CvFileNode* cvGetRootFileNode( const CvFileStorage* fs, int stream_index=0 );
函数 cvGetRootFileNode 返回一个高层文件节点。 高层节点没有名称,它们和流相对应,接连存入文件存储器。如果超出索引范围, 函数返回NULL指针, 所以要得到所有高层节点需要反复调用函数stream_index=0,1,...,直到返回NULL指针。这个函数是在文件存储器中递归寻找的基础方法。 [编辑] GetFileNodeByName在图表或者文件存储器中查找节点 CvFileNode* cvGetFileNodeByName( const CvFileStorage* fs, const CvFileNode* map, const char* name );
函数 cvGetFileNodeByName 文件节点通过name 查找文件节点 该节点在图表中被查找,或者如果指针为NULL,那么在内存中的高层文件节点中查找。 在图表中或者在序列调用cvGetSeqElem中使用到这个函数,这样可能遍历整个文件存储器。 为了加速确定某个关键字的多重查询(例如 结构数组 ),可以在cvGetHashedKey 和cvGetFileNode之中用到一个。 [编辑] GetHashedKey返回一个指向已有名称的唯一指针 CvStringHashNode* cvGetHashedKey( CvFileStorage* fs, const char* name, int len=-1, int create_missing=0 );
函数 cvGetHashedKey返回指向每一个特殊文件节点名的唯一指针。这个指针可以传递给cvGetFileNode函数。它比cvGetFileNodeByName快, 因为比较指针相对比较字符串快些。 观察下面例子: 用二维图来表示一个点集,例: %YAML:1.0 points: - { x: 10, y: 10 } - { x: 20, y: 20 } - { x: 30, y: 30 } # ... 因而,它使用哈希指针“x”和“y"加速对点的解析。 例:从一个文件存储器中读取一组的结构 #include "cxcore.h" int main( int argc, char** argv ) { CvFileStorage* fs = cvOpenFileStorage( "points.yaml", 0, CV_STORAGE_READ ); CvStringHashNode* x_key = cvGetHashedKey( fs, "x", -1, 1 ); CvStringHashNode* y_key = cvGetHashedKey( fs, "y", -1, 1 ); CvFileNode* points = cvGetFileNodeByName( fs, 0, "points" ); if( CV_NODE_IS_SEQ(points->tag) ) { CvSeq* seq = points->data.seq; int i, total = seq->total; CvSeqReader reader; cvStartReadSeq( seq, &reader, 0 ); for( i = 0; i < total; i++ ) { CvFileNode* pt = (CvFileNode*)reader.ptr; #if 1 /* 快变量 */ CvFileNode* xnode = cvGetFileNode( fs, pt, x_key, 0 ); CvFileNode* ynode = cvGetFileNode( fs, pt, y_key, 0 ); assert( xnode && CV_NODE_IS_INT(xnode->tag) && ynode && CV_NODE_IS_INT(ynode->tag)); int x = xnode->data.i; // or x = cvReadInt( xnode, 0 ); int y = ynode->data.i; // or y = cvReadInt( ynode, 0 ); #elif 1 /* 慢变量:不使用x值与y值 */ CvFileNode* xnode = cvGetFileNodeByName( fs, pt, "x" ); CvFileNode* ynode = cvGetFileNodeByName( fs, pt, "y" ); assert( xnode && CV_NODE_IS_INT(xnode->tag) && ynode && CV_NODE_IS_INT(ynode->tag)); int x = xnode->data.i; // or x = cvReadInt( xnode, 0 ); int y = ynode->data.i; // or y = cvReadInt( ynode, 0 ); #else /* 最慢的可以轻松使用的变量 */ int x = cvReadIntByName( fs, pt, "x", 0 /* default value */ ); int y = cvReadIntByName( fs, pt, "y", 0 /* default value */ ); #endif CV_NEXT_SEQ_ELEM( seq->elem_size, reader ); printf("%d: (%d, %d)\n", i, x, y ); } } cvReleaseFileStorage( &fs ); return 0; } 请注意,无论使用那一种方法访问图表 ,都比使用序列慢,例如上面的例子,如果把数据作为整数对放在在单一数字序列中,效率会更高 。 [编辑] GetFileNode在图表或者文件存储器中查找节点 CvFileNode* cvGetFileNode( CvFileStorage* fs, CvFileNode* map, const CvStringHashNode* key, int create_missing=0 );
函数 cvGetFileNode 查找一个文件节点。函数能够插入一个新的节点,当它不在图表中时 [编辑] GetFileNodeName返回文件节点名 const char* cvGetFileNodeName( const CvFileNode* node );
函数 cvGetFileNodeName 返回文件节点名或返回NULL(如果文件节点没有名称或者node为NULL 。 [编辑] ReadInt从文件节点中得到整形值 int cvReadInt( const CvFileNode* node, int default_value=0 );
函数 cvReadInt 从文件节点中返回整数。如果文件节点为NULL, default_value 被返回 。另外如果文件节点有类型 CV_NODE_INT, 则 node->data.i 被返回 。如果文件节点有类型 CV_NODE_REAL, 则 node->data.f 被修改成整数后返回。 其他的情况是则结果不确定。 [编辑] ReadIntByName读取一个有名称的整数型(原:查找文件节点返回它的值) int cvReadIntByName( const CvFileStorage* fs, const CvFileNode* map, const char* name, int default_value=0 );
函数 cvReadIntByName是 cvGetFileNodeByName 和 cvReadInt的简单重叠. [编辑] ReadReal从文件节点中得到浮点形值 double cvReadReal( const CvFileNode* node, double default_value=0. );
函数cvReadReal 从文件节点中返回浮点形值。如果文件节点为NULL, default_value 被返回(这样就不用检查cvGetFileNode 返回的指针是否为空了) 。另外如果文件节点有类型 CV_NODE_REAL , 则node->data.f 被返回 。如果文件节点有类型 CV_NODE_INT , 则 node->data.i 被修改成浮点数后返回。 另外一种情况是,结果不确定。 . [编辑] ReadRealByName查找文件节点返回它的浮点形值 double cvReadRealByName( const CvFileStorage* fs, const CvFileNode* map, const char* name, double default_value=0. );
函数 cvReadRealByName 是 cvGetFileNodeByName 和cvReadReal 的简单重叠。 [编辑] ReadString从文件节点中得到字符串文本 const char* cvReadString( const CvFileNode* node, const char* default_value=NULL );
函数cvReadString 从文件节点中返回字符串文本。如果文件节点为NULL, default_value 被返回 。另外如果文件节点有类型CV_NODE_STR, 则data.str.ptr 被返回 。 另外一种情况是,结果不确定。 [编辑] ReadStringByName查找文件节点返回它的字符串文本 const char* cvReadStringByName( const CvFileStorage* fs, const CvFileNode* map, const char* name, const char* default_value=NULL );
函数 cvReadStringByName是 cvGetFileNodeByName 和cvReadString 的简单重叠。 [编辑] Read解释对象并返回指向它的指针 void* cvRead( CvFileStorage* fs, CvFileNode* node, CvAttrList* attributes=NULL );
不被使用的参数. 函数 cvRead 解释用户对象 (在文件存储器子树中建立新的对象)并返回。对象被解释 ,必须按原有的支持读方法的类型 (参考 CvTypeInfo).用类型名决定对象,并在文件中被解释 。如果对象是动态结构,它将在内存中创建传递给cvOpenFileStorage或者使NULL指针被建立在临时性内存中。当cvReleaseFileStorage 被调用时释放内存。 如果对象不是动态结构 ,将在堆中被建立,释放它的内存需要用专用函数或通用函数cvRelease。 [编辑] ReadByName查找对象并解释 void* cvReadByName( CvFileStorage* fs, const CvFileNode* map, const char* name, CvAttrList* attributes=NULL );
函数 cvReadByName 是由cvGetFileNodeByName 和 cvRead叠合的。 . [编辑] ReadRawData读重数 void cvReadRawData( const CvFileStorage* fs, const CvFileNode* src, void* dst, const char* dt );
函数 cvReadRawData从有序的文件节点中读取标量元素。 [编辑] StartReadRawData初始化文件节点读取器 void cvStartReadRawData( const CvFileStorage* fs, const CvFileNode* src, CvSeqReader* reader );
函数 cvStartReadRawData 初始化序列读取器从文件节点中读取数据。初始化的首部通过传给cvReadRawDataSlice使用 。 [编辑] ReadRawDataSlice初始化文件节点序列 void cvReadRawDataSlice( const CvFileStorage* fs, CvSeqReader* reader, int count, void* dst, const char* dt );
函数 cvReadRawDataSlice 从文件节点读一个或多个元素,组成一个序列用于指定数组。读入元素的总数由其他数组的元素总和乘以每个数组元素数目。例如 如果 dt='2if', 函数将读是total*3数量的序列元素。对于任何数组,可以使用cvSetSeqReaderPos自由定位,跳过某些位置或者重复读取。 [编辑] 运行时类型信息和通用函数[编辑] CvTypeInfo类型信息 typedef int (CV_CDECL *CvIsInstanceFunc)( const void* struct_ptr ); typedef void (CV_CDECL *CvReleaseFunc)( void** struct_dblptr ); typedef void* (CV_CDECL *CvReadFunc)( CvFileStorage* storage, CvFileNode* node ); typedef void (CV_CDECL *CvWriteFunc)( CvFileStorage* storage, const char* name, const void* struct_ptr, CvAttrList attributes ); typedef void* (CV_CDECL *CvCloneFunc)( const void* struct_ptr ); typedef struct CvTypeInfo { int flags; /* 不常用 */ int header_size; /* (CvTypeInfo)的大小 sizeof(CvTypeInfo) */ struct CvTypeInfo* prev; /* 在列表中已定义过的类型 */ struct CvTypeInfo* next; /* 在列表中下一个已定义过的类型 */ const char* type_name; /*定义类型名,并写入文件存储器 */ /* methods */ CvIsInstanceFunc is_instance; /* 选择被传递的对象是否属于的类型 */ CvReleaseFunc release; /* 释放对象的内存空间 */ CvReadFunc read; /* 从文件存储器中读对象 */ CvWriteFunc write; /* 将对象写入文件存储器 */ CvCloneFunc clone; /* 复制一个对象 */ } CvTypeInfo; 结构 CvTypeInfo包含的信息包括标准的或用户自定义的类型。类型的实例可能有也可能没有包含指向相应的CvTypeInfo结构的指针。在已有的对象中查找类型的方法是使用cvTypeOf函数。在从文件存储器中读对象的时候已有的类型信息可以通过类型名使用cvFindType来查找 。 用户可以通过cvRegisterType定义一个新的类型 ,并将类型信息结构加到文件列表的开始端, 它可以从标准类型中建立专门的类型,重载基本的方法。 [编辑] RegisterType定义新类型 void cvRegisterType( const CvTypeInfo* info );
函数 cvRegisterType 定义一个新类型,可以通过信息来描述它。这个函数在内存创建了一个copy,所以在用完以后,应该删除它。 [编辑] UnregisterType删除定义的类型 void cvUnregisterType( const char* type_name );
函数 cvUnregisterType通过指定的名称删除已定义的类型。 如果不知道类型名,可以用cvTypeOf或者连续扫描类型列表,从cvFirstType开始,然后调用 cvUnregisterType(info->type_name)。 [编辑] FirstType返回类型列表的首位。 CvTypeInfo* cvFirstType( void ); 函数 cvFirstType 返回类型列表中的第一个类型。可以利用CvTypeInfo 的prev next来实现遍历。 [编辑] FindType通过类型名查找类型 CvTypeInfo* cvFindType( const char* type_name );
函数 cvFindType通过类型名查找指定的类型。如果找不到返回值为NULL。 [编辑] TypeOf返回对象的类型 CvTypeInfo* cvTypeOf( const void* struct_ptr );
函数 cvTypeOf 查找指定对象的类型。它反复扫描类型列表,调用每一个类型信息结构中的函数和方法与对象做比较,直到它们中的一个的返回值不为0或者所有的类型都被访问。 [编辑] Release删除对象 void cvRelease( void** struct_ptr );
函数 cvRelease 查找指定对象的类型,然后调用release。 [编辑] Clone克隆一个对象 void* cvClone( const void* struct_ptr );
函数 cvClone 查找指定对象的类型,然后调用 clone。 [] Save存储对象到文件中 void cvSave( const char* filename, const void* struct_ptr, const char* name=NULL, const char* comment=NULL, CvAttrList attributes=cvAttrList());
函数 cvSave存储对象到文件。它给cvWrite提供一个简单的接口。 [] Load从文件中打开对象。 void* cvLoad( const char* filename, CvMemStorage* memstorage=NULL, const char* name=NULL, const char** real_name=NULL );
函数 cvLoad 从文件中打开对象。它给cvRead提供一个简单的接口.对象被打开之后,文件存储器被关闭,所有的临时缓冲区被删除。因而,为了能打开一个动态结构,如序列,轮廓或图像,你应该为该函数传递一个有效的目标存储器。 |
|