OpenCV提供了一种机制来序列化(serialize)和去序列化(de-serialize)其各种数据类型,可以从磁盘中按YAML或XML格式读/写。在第4章中,我们将专门介绍存储和调用常见的对象IplImages的函数(cvSaveImage()和cvLoadImage())。此外,第4章将讨论读/写视频的特有函数:可以从文件或者摄影机中读取数据的函数cvGrabFrame()以及写操作函数cvCreateVideoWriter()和cvWriteFrame()。本小节将侧重于一般对象的永久存储:读/写矩阵、OpenCV结构、配置与日志文件。
首先,我们从有效且简便的OpenCV矩阵的保存和读取功能函数开始。函数是cvSave()和cvLoad()。例3-15展示了如何保存和读取一个5×5的单位矩阵(对角线上是1,其余地方都是0)。
例3-15:存储和读取CvMat
- CvMat A = cvMat( 5, 5, CV_32F, the_matrix_data );
- cvSave( "my_matrix.xml", &A );
- . . .
- // to load it then in some other program use …
- CvMat* A1 = (CvMat*) cvLoad( "my_matrix.xml" );
CxCore参考手册中有整节内容都在讨论数据存储。首先要知道,在OpenCV中,一般的数据存储要先创建一个CvFileStorage结构(如例3-16)所示,该结构将内存对象存储在一个树形结构中。然后通过使用CV_STORAGE_READ参数的cvOpenFileStorage()从磁盘读取数据,创建填充该结构,也可以通过使用CV_STORAGE_WRITE的cvOpenFileStorage()创建并打开CvFileStorage写数据,而后使用适当的数据存储函数来填充它。在磁盘上,数据的存储格式为XML或者YAML。
例3-16:CvFileStorage结构,数据通过CxCore数据存储函数访问
- typedef struct CvFileStorage
- {
- ... // hidden fields
- } CvFileStorage;
CvFileStorage树内部的数据是一个层次化的数据集合,包括标量、CxCore对象(矩阵、序列和图)以及用户定义的对象。
假如有一个配置文件或日志文件。配置文件告诉我们视频有多少帧(10),画面大小(320×240)并且将应用一个3×3的色彩转换矩阵。例3-17展示了如何从磁盘中调出cfg.xml文件。
例3-17:往磁盘上写一个配置文件cfg.xml
- CvFileStorage* fs = cvOpenFileStorage(
- "cfg.xml",
- 0,
- CV_STORAGE_WRITE
- );
- cvWriteInt( fs, "frame_count", 10 );
- cvStartWriteStruct( fs, "frame_size", CV_NODE_SEQ );
- cvWriteInt( fs, 0, 320 );
- cvWriteInt( fs, 0, 200 );
- cvEndWriteStruct(fs);
- cvWrite( fs, "color_cvt_matrix", cmatrix );
- cvReleaseFileStorage( &fs );
请留意这个例子中的一些关键函数。我们可以定义一个整型变量通过cvWritelnt()向结构中写数据。我们也可以使用cvStartWriteStruct()来创建任意一个可以任选一个名称(如果无名称请输入0或NULL)的结构。这个结构有两个未命名的整型变量,使用cvEndWriteStruct()结束编写结构。如果有更多的结构体,我们用相似的方法来解决;这种结构可以进行任意深度的嵌套。最后,我们使用cvWrite()编写处色彩转换矩阵。将这个相对复杂的矩阵程序与例3-15中简单的cvSave()程序进行对比。便会发现cvSave()是cvWrite()在只保存一个矩阵时的快捷方式。当写完数据后,使用cvReleaseFileStorage()释放CvFileStorage句柄。例3-18显示了XML格式的输出内容。
例3-18:磁盘中的cfg.xml文件
- <?xml version="1.0"?>
- <opencv_storage>
- <frame_count>10</frame_count>
- <frame_size>320 200</frame_size>
- <color_cvt_matrix type_id="opencv-matrix">
- <rows>3</rows> <cols>3</cols>
- <dt>f</dt>
- <data>...</data></color_cvt_matrix>
- </opencv_storage>
我们将会在例3-19中将这个配置文件读入。
例3-19:磁盘中的cfg.xml文件
- CvFileStorage* fs = cvOpenFileStorage(
- "cfg.xml",
- 0,
- CV_STORAGE_READ
- );
- int frame_count = cvReadIntByName(
- fs,
- 0,
- "frame_count",
- 5 /* default value */
- );
- CvSeq* s = cvGetFileNodeByName(fs,0,"frame_size")->data.seq;
- int frame_width = cvReadInt(
- (CvFileNode*)cvGetSeqElem(s,0)
- );
- int frame_height = cvReadInt(
- (CvFileNode*)cvGetSeqElem(s,1)
- );
- CvMat* color_cvt_matrix = (CvMat*) cvReadByName(
- fs,
- 0,
- "color_cvt_matrix"
- );
- cvReleaseFileStorage( &fs );
在阅读时,我们像例3-19中那样用cvOpenFileStorage()打开XML配置文件。然后用cvReadlntByName()来读取frame_count,如果有没有读到的数,则赋一个默认值。在这个例子中默认的值是5。然后使用cvGetFileNodeByName()得到结构体frame_size。在这里我们用cvReadlnt()读两个无名称的整型数据。随后使用cvReadByName()读出我们已经定义的色彩转换矩阵。 将本例与例3-15中的cvLoad()进行对比。如果我们只有一个矩阵要读取,那么可以使用cvLoad(),但是如果矩阵是内嵌在一个较大的结构中,必须使用cvRead()。最后,释放CvFileStorage结构。
数据函数存储与CvFileStorage结构相关的表单列在表3-16中。想了解更多细节,请查看CxCore手册。
表3-16:数据存储函数
函数名称 |
描述 |
打开并释放 |
|
cvOpenFileStorage |
为读/写打开存储文件 |
cvReleaseFileStorage |
释放存储的数据 |
写入 |
|
cvStartWriteStruct |
开始写入新的数据结构 |
cvEndWriteStruct |
结束写入数据结构 |
cvWriteInt |
写入整数型 |
cvWriteReal |
写入浮点型 |
cvWriteString |
写入字符串 |
cvWriteComment |
写一个XML或YAML的注释字串 |
cvWrite |
写一个对象,例如CvMat |
cvWriteRawData |
写入多个数值 |
cvWriteFileNode |
将文件节点写入另一个文件存储器 |
读取 |
|
cvGetRootFileNode |
获取存储器最顶层的节点 |
cvGetFileNodeByName |
在映图或存储器中找到相应节点 |
cvGetHashedKey |
为名称返回一个惟一的指针 |
cvGetFileNode |
在映图或文件存储器中找到节点 |
cvGetFileNodeName |
返回文件的节点名 |
cvReadInt |
读取一个无名称的整数型 |
cvReadIntByName |
读取一个有名称的整数型 |
cvReadReal |
读取一个无名称的浮点型 |
续表
函数 |
描述 |
cvReadRealByName |
读取一个有名称的浮点型 |
cvReadString |
从文件节点中寻找字符串 |
cvReadStringByName |
找到一个有名称的 文件节点并返回它 |
cvRead |
将对象解码并返回它的指针 |
cvReadByName |
找到对象并解码 |
cvReadRawData |
读取多个数值 |
cvStartReadRawData |
初始化文件节点序列的读取 |
cvReadRawDataSlice |
读取文件节点的内容 |