除了标准视频压缩之外,OpenCV还提供了一种机制,用于以YAML或XML格式将其各种数据类型序列化和反序列化到磁盘和从磁盘进行反序列化。这些方法可用于在单个文件中加载或存储任意数量的OpenCV数据对象(包括基本类型,如int,float等)。
下面介绍一般对象的读取和写入矩阵,OpenCV结构,配置和日志文件。
读取和写入文件的基本机制是FileStorage对象。这个对象本质上代表了磁盘上的一个文件,但是这样做的方式使访问文件中表示的数据变得简单自然。
FileStorage::FileStorage();
FileStorage::FileStorage( string fileName, int flag );
FileStorage对象是XML或YAML数据文件的表示。 可以创建并将文件名传递给构造函数,也可以使用默认构造函数创建,然后FileStorage :: open()打开文件,其中flag参数应为FileStorage :: WRITE或FileStorage :: APPEND。
一旦打开了要写入的文件,就可以使用运算符FileStorage :: operator <
如果想要创建新的映射或序列,可以使用特殊字符{(对于映射)或[(对于序列))执行此操作。 一旦创建了一个映射,就可以输入每个元素的名称和数据。 如果创建了一个序列,只需一个接一个地输入新数据直到关闭。
可以使用FileStorage :: release()函数关闭文件。
例1 这来自OpenCV文档的代码示例。
#include "opencv2/opencv.hpp"
#include
using namespace cv;
using namespace std;
int main(int, char** argv)
{
cv::FileStorage fs("test.yml", cv::FileStorage::WRITE);
fs << "frameCount" << 5;
time_t rawtime; time(&rawtime);
fs << "calibrationDate" << asctime(localtime(&rawtime));
cv::Mat cameraMatrix = (
cv::Mat_(3, 3)
<< 1000, 0, 320, 0, 1000, 240, 0, 0, 1
);
cv::Mat distCoeffs = (
cv::Mat_(5, 1)
<< 0.1, 0.01, -0.001, 0, 0
);
fs << "cameraMatrix" << cameraMatrix << "distCoeffs" << distCoeffs;
fs << "features" << "[";
for (int i = 0; i < 3; i++)
{
int x = rand() % 640;
int y = rand() % 480;
uchar lbp = rand() % 256;
fs << "{:" << "x" << x << "y" << y << "lbp" << "[:";
for (int j = 0; j < 8; j++)
fs << ((lbp >> j) & 1);
fs << "]" << "}";
}
fs << "]";
fs.release();
return 0;
}
运行该程序的结果将是一个YAML文件,其中包含以下内容:图1 创建的yml文件
在示例代码中,你会注意到有时映射或序列中的所有数据都存储在一行中,而其他时间则是每行存储一个元素。 这不是一种自动格式化行为。 相反,它是由映射和序列创建字符串的变体创建的:“{:”和“}”映射,以及“[:”和“]”。 此功能仅对YAML输出有意义; 如果输出文件是XML,则会忽略此细微差别,并且映射或序列将按原样存储,而不包含变体。
将上面的代码第一行的“yml”改成“xml”,则得到如图2所示输出结果。
图2 创建的xml文件
可以打开FileStorage对象来读取,这时flag参数应该设置为FileStorage :: READ。也可以使用默认构造函数创建一个未打开的文件存储对象,然后使用FileStorage :: open()打开它。
FileStorage::open( string fileName, int flag );
一旦文件被打开,数据可以用重载数组运算符cv :: FileStorage :: operator []()或用迭代器cv :: FileNodeIterator读取。用File Storage :: release()成员函数关闭文件。
为了读取, FileStorage :: operator []()传递了与所需对象关联的字符串键。要从序列中读取,可以使用整数参数调用同一个运算符。但是,该运算符的返回值不是所需的对象。它是FileNode类型的一个对象,它表示以抽象形式与给定键一起使用的值。FileNode对象可以通过各种方式进行操作。
一旦有一个FileNode对象,如果它表示一个对象(或一个数字或字符串),则可以使用重载的提取操作符FileNode :: operator >>()将其加载到相应类型的变量中。采用如下所示方式:
Mat anArray;
myFileStorage["calibrationMatrix"] >> anArray;
FileNode对象还支持直接转换为任何基本数据类型,如下示例所示。
int aNumber;
myFileStorage["someInteger"] >> aNumber;
这相当于:
int aNumber;
aNumber = (int)myFileStorage["someInteger"];
还有一个遍历文件节点的迭代器可以使用。 给定一个FileNode对象,成员函数File Node :: begin()和FileNode :: end()即为映射提供第一个和最后一个迭代器。迭代器FileNodeIterator :: operator *()将返回另一个FileNode对象。 这种迭代器通常支持递增和递减操作。FileNode :: type()返回值是类FileNode中定义的枚举类型。
例2 来自OpenCV文档,显示了如何读取之前编写的yml文件。
#include "opencv2/opencv.hpp"
#include
using namespace cv;
using namespace std;
int main(int, char** argv)
{
cv::FileStorage fs2("test.yml", cv::FileStorage::READ);
// first method: use (type) operator on FileNode.
int frameCount = (int)fs2["frameCount"];
// second method: use cv::FileNode::operator >>
//
std::string date;
fs2["calibrationDate"] >> date;
cv::Mat cameraMatrix2, distCoeffs2;
fs2["cameraMatrix"] >> cameraMatrix2;
fs2["distCoeffs"] >> distCoeffs2;
cout << "frameCount: " << frameCount << endl
<< "calibration date: " << date << endl
<< "camera matrix: " << cameraMatrix2 << endl
<< "distortion coeffs: " << distCoeffs2 << endl;
cv::FileNode features = fs2["features"];
cv::FileNodeIterator it = features.begin(), it_end = features.end();
int idx = 0;
std::vector lbpval;
// iterate through a sequence using FileNodeIterator
for (; it != it_end; ++it, idx++)
{
cout << "feature #" << idx << ": ";
cout << "x=" << (int)(*it)["x"] << ", y=" << (int)(*it)["y"] << ", lbp: (";
// ( Note: easily read numerical arrays using FileNode >> std::vector. )
//
(*it)["lbp"] >> lbpval;
for (int i = 0; i < (int)lbpval.size(); i++)
cout << " " << (int)lbpval[i];
cout << ")" << endl;
}
fs2.release();
return 0;
}
图3是读取yml文件的结果,如果将代码第一行的“yml”改成“xml”,得到的结果是一样的。图3 读取yml文件或xml文件的结果