Data Persistence

OpenCV provides a mechanism for serializing and de-serializing its various data types to and from disk in either YAML or XML format. In the chapter on HighGUI, which addresses user interface functions, we will cover specific functions that store and recall our most common object: IplImages (these functions are cvSaveImage() and cvLoadImage()).

In addition, the HighGUI chapter will discuss read and write functions specific to movies: cvGrabFrame(), which reads from file or from camera; and cvCreateVideoWriter() and cvWriteFrame(). In this section, we will focus on general object persistence: reading and writing matrices, OpenCV structures, and configuration and log files.

First we start with specific and convenient functions that save and load OpenCV matrices. These functions are cvSave() and cvLoad(). Suppose you had a 5-by-5 identity matrix (0 everywhere except for 1s on the diagonal). Example 3-15 shows how to accomplish this.

Example 3-15. Saving and loading a 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" );

The CxCore reference manual contains an entire section on data persistence. What you really need to know is that general data persistence in OpenCV consists of creating a CvFileStorage structure, as in Example 3-16, that stores memory objects in a tree structure. You can create and fill this structure by reading from disk via cvOpenFileStorage() with CV_STORAGE_READ, or you can create and open CvFileStorage via cvOpenFileStorage() with CV_STORAGE_WRITE for writing and then fill it using the appropriate data persistence functions. On disk, the data is stored in an XML or YAML format.

Example 3-16. CvFileStorage structure; data is accessed by CxCore data persistence functions

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

The internal data inside the CvFileStorage tree may consist of a hierarchical collection of scalars, CxCore objects (matrices, sequences, and graphs) and/or user-defined objects.

Let's say you have a configuration or logging file. For example, consider the case of a movie configuration file that tells us how many frames we want (10), what their size is (320 by 240) and a 3-by-3 color conversion matrix that should be applied. We want to call the file "cfg.xml" on disk. Example 3-17 shows how to do this.

Example 3-17. Writing a configuration file "cfg.xml" to disk

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 );

Note some of the key functions in this example. We can give a name to integers that we write to the structure using cvWriteInt(). We can create an arbitrary structure, using cvStartWriteStruct(), which is also given an optional name (pass a 0 or NULL if there is no name). This structure has two ints that have no name and so we pass a 0 for them in the name field, after which we use cvEndWriteStruct() to end the writing of that structure. If there were more structures, we'd Start and End each of them similarly; the structures may be nested to arbitrary depth. We then use cvWrite() to write out the color conversion matrix. Contrast this fairly complex matrix write procedure with the simpler cvSave() in Example 3-15. The cvSave() function is just a convenient shortcut for cvWrite() when you have only one matrix to write. When we are finished writing the data, the CvFileStorage handle is released in cvReleaseFileStorage(). The output (here, in XML form) would look like Example 3-18.

Example 3-18. XML version of cfg.xml on disk

<?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>

We may then read this configuration file as shown in Example 3-19.

Example 3-19. Reading cfg.xml from disk

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 );

When reading, we open the XML configuration file with cvOpenFileStorage() as in Example 3-19. We then read the frame_count using cvReadIntByName(), which allows for a default value to be given if no number is read. In this case the default is 5. We then get the structure that we named "frame_size" using cvGetFileNodeByName(). From here, we read our two unnamed integers using cvReadInt(). Next we read our named color conversion matrix using cvReadByName().[36] Again, contrast this with the short form cvLoad() in Example 3-15. We can use cvLoad() if we only have one matrix to read, but we must use cvRead() if the matrix is embedded within a larger structure. Finally, we release the CvFileStorage structure.

The list of relevant data persistence functions associated with the CvFileStorage structure is shown in Table 3-16. See the CxCore manual for more details.

Table 3-16. Data persistence functions

Function

Description

Open and Release

cvOpenFileStorage

Opens file storage for reading or writing

cvReleaseFileStorage

Releases data storage

Writing

cvStartWriteStruct

Starts writing a new structure

cvEndWriteStruct

Ends writing a structure

cvWriteInt

Writes integer

cvWriteReal

Writes float

cvWriteString

Writes text string

cvWriteComment

Writes an XML or YAML comment string

cvWrite

Writes an object such as a CvMat

cvWriteRawData

Writes multiple numbers

cvWriteFileNode

Writes file node to another file storage

Reading

cvGetRootFileNode

Gets the top-level nodes of the file storage

cvGetFileNodeByName

Finds node in the map or file storage

cvGetHashedKey

Returns a unique pointer for given name

cvGetFileNode

Finds node in the map or file storage

cvGetFileNodeName

Returns name of file node

cvReadInt

Reads unnamed int

cvReadIntByName

Reads named int

cvReadReal

Reads unnamed float

cvReadRealByName

Reads named float

cvReadString

Retrieves text string from file node

cvReadStringByName

Finds named file node and returns its value

cvRead

Decodes object and returns pointer to it

cvReadByName

Finds object and decodes it

cvReadRawData

Reads multiple numbers

cvStartReadRawData

Initializes file node sequence reader

cvReadRawDataSlice

Reads data from sequence reader above



[36] One could also use cvRead() to read in the matrix, but it can only be called after the appropriate CvFileNode{} is located, e.g., using cvGetFileNodeByName().