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: IplImage
s (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 int
s 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 | |
|
Opens file storage for reading or writing |
|
Releases data storage |
Writing | |
|
Starts writing a new structure |
|
Ends writing a structure |
|
Writes integer |
|
Writes float |
|
Writes text string |
|
Writes an XML or YAML comment string |
|
Writes an object such as a CvMat |
|
Writes multiple numbers |
|
Writes file node to another file storage |
Reading | |
|
Gets the top-level nodes of the file storage |
|
Finds node in the map or file storage |
|
Returns a unique pointer for given name |
|
Finds node in the map or file storage |
|
Returns name of file node |
|
Reads unnamed int |
|
Reads named int |
|
Reads unnamed float |
|
Reads named float |
|
Retrieves text string from file node |
|
Finds named file node and returns its value |
|
Decodes object and returns pointer to it |
|
Finds object and decodes it |
|
Reads multiple numbers |
|
Initializes file node sequence reader |
|
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()
.