2.9 XML和YAML文件的输入输出
对XML和YAML文件的进行串行读写,有两种数据结构可以用:
mappings(类似于STL map)和
element sequence(类似于STL vector)。
1. XML/YAML文件的打开和关闭:
OpenCV中的XML/YAML数据结构是
FileStorage类,使用FileStorage对象绑定文件。文件扩展名是
.xml或
.yaml,(实测.txt也可以)甚至于如果扩展名是.xml.gz的形式,可以直接写入压缩文件中。
string filename = "I.xml";
FileStorage fs(filename, FileStorage::WRITE);
\\...
fs.open(filename, FileStorage::READ);
fs.release();
打开文件方式有两种:使用FileStorage构造函数或者 open()函数。第二个参数指定操作类型,可以是: WRITE, READ, APPEND。
FileStorage对象销毁的时候文件会自动关闭,或者显式调用r
elease() 函数关闭文件。
2. 文本和数字的读写:
使用STL库中的 << 输出操作符
写文件。为了确定写入的数据结构类型,需要给出变量名。读文件用 << 输入操作符。使用 [ ] 操作符寻址,并将地址对应的值通过强制类型转换或者 >> 操作符读出。
fs << "iterationNr" << 100; // 写文件
int itNr; // 读文件
fs["iterationNr"] >> itNr;
itNr = (int) fs["iterationNr"];
同C++基本类型的操作方法。
Mat R = Mat_<uchar >::eye (3, 3),
T = Mat_<double>::zeros(3, 1);
fs << "R" << R; // 写 cv::Mat
fs << "T" << T;
fs["R"] >> R; // 读 cv::Mat
fs["T"] >> T;
4. 对vector(array)和相应map的读写:
前面提到的,可以把mappings和element sequence写入文件。首先给出变量名,对于sequence类型数据,第一个元素是 “[” 最后一个元素是 “]” ,对于map类型数据,首尾是 “{” 和 “}” 。
fs << "strings" << "["; <span style="white-space:pre"> </span>// 文本 - 字符串序列
fs << "image1.jpg" << "Awesomeness" << "baboon.jpg";
fs << "]"; <span style="white-space:pre"> </span>// 序列结束
fs << "Mapping"; <span style="white-space:pre"> </span>// 文本 - mapping
fs << "{" << "One" << 1;
fs << "Two" << 2 << "}";
从文件中读取这两种数据结构,采用 FileNode 和 FileNodeIterator 。此时 FileStorage类的操作符 [ ] 返回的是一个FileNode数据类型。如果结点是序列化的,可以使用 FileNodeIterator来遍历所有元素。
FileNode n = fs["strings"]; // 读取字符串序列 - 获取节点
if (n.type() != FileNode::SEQ)
{
cerr << "strings is not a sequence! FAIL" << endl;
return 1;
}
FileNodeIterator it = n.begin(), it_end = n.end(); // 遍历节点
for (; it != it_end; ++it)
cout << (string)*it << endl;
对于maps,可以用 [ ] 操作符访问指定的元素,并通过强制类型转换赋值或者 >> 操作符读出。
n = fs["Mapping"]; // 从序列中读取map
cout << "Two " << (int)(n["Two"]) << "; ";
cout << "One " << (int)(n["One"]) << endl << endl;
5. 读写自定义数据类型:
对自定义的类,需要在类内定义读写函数,按顺序读写类的数据成员。同时,在类外也定义读写函数,调用类对象的读写方法。
class MyData
{
public:
MyData() : A(0), X(0), id() {}
void write(FileStorage& fs) const //对自定义类进行写序列化
{
fs << "{" << "A" << A << "X" << X << "id" << id << "}";
}
void read(const FileNode& node) //从序列读取自定义类
{
A = (int)node["A"];
X = (double)node["X"];
id = (string)node["id"];
}
public: // 数据成员
int A;
double X;
string id;
};
void write(FileStorage& fs, const std::string&, const MyData& x)
{
x.write(fs);
}
void read(const FileNode& node, MyData& x, const MyData& default_value = MyData())
{
if(node.empty())
x = default_value;
else
x.read(node);
}
接下来就可以用 >> 和 << 操作符进行读写操作了。
MyData m(1);
fs << "MyData" << m; // 写自定义数据结构
fs["MyData"] >> m; // 读自定义数据结构
2.10 兼容OpenCV 1.0
OpenCV 2.0 以上版本中,
Mat 取代了旧版本的
CvMat和
IplImage。转换到新函数有几个原则:
1. 按模块包含头文件:新版本提供许多模块,可按需包含相应模块的头文件。
2. using namespace cv:该命名空间包含新版本的所有C++接口,接口命名遵循驼峰命名法则。
3.将模块链接到程序:将库的路径加入程序执行路径,具体方法参考教程第一章内容。
4.转换对象:可以通过简单的复制操作将Mat对象转换为老版本的IplImage或CvMat对象。
Mat I;
IplImage pI = I;
CvMat mI = I;
5.获取指针:需要显示写出要获得IplImage或者CvMat对象的指针。
Mat I;
IplImage* pI = &I.operator IplImage();
CvMat* mI = &I.operator CvMat();
6.智能指针:OpenCV引入智能指针来防止内存泄漏。需要使用指针时应使用
Ptr模板声明。
Ptr<IplImage> piI = &I.operator IplImage();
7.旧版本图像对象可用来初始化Mat对象:作为Mat构造函数的参数传入。
Mat K(piL), L;
L = Mat(pI);