Opencv的xml⽂件操作
⾃定义存取⽅式、xml、数据库、数据库系统
(从EEPROM到云服务器的数据存取)
xml是Extensible Markup Language可扩展标记语⾔的简写,实际上可以把xml⽂件当成⼀个微型的数据库,也就是说它是⽤来存取数据的。我们知道内存⾥⾯的数据掉电了就没有了,在单⽚机开发的时候如果有数据需要掉电保存我们通常会保存在内部或者外部的EEPROM或者flash中,由于单⽚机开发的数据量⾮常⼩,所以我们读写数据的时候都是按⾃⼰的规则来规定那个字节是什么数据。在⼀些linux或者其他系统的应⽤程序中也需要保存⼀些数据,⽐如配置⽂件或者断点记忆等,这个时候我们也可以⽤⼀个纯⽂本⽂件按照我们⾃⼰的规则来存取数据,但是为了统⼀接⼝,我们必须约定⼀个统⼀的规则或者格式,这便产⽣了xml⽂件。然⽽xml⽂件虽然能够快速⽅便的存取数据,但是它并不能描述数据之间的相互关系,也不能在存取的时候对数据进⾏⼀些控制和筛选等操作,所以对于⼀些数据量很⼤并且数据之间的关系错综复杂⽽⼜需要这种数据间的关系和对数据进⾏⼀些控制的应⽤,如果仍然使⽤xml⽂件来进⾏数据存储的话,那么应⽤程序的数据处理部分就会相当复杂。为了解决这个问题,数据库就应运⽽⽣,数据库不仅可以存取数据,⽽且可以很好的描述数据间的相互关系,还可以对数据进⾏控制操作。不过数据库虽然弥补了xml的不⾜,并拥有了诸多优点,但是处理数据间的关系,对数据进⾏遍历查等
⼯作使得计算量⼤⼤增加,并且很多时候为了节省存储空间通常会把数据库部署在服务器上,很多客户端的程序共⽤⼀个数据库,这样⼀来多⽤户的并发控制⼜是⼀个问题了,为了解决这些问题,于是⼜出现了数据库系统。⾃定义存取⽅式、xml、数据库和数据库系统并没有孰优孰劣之分,具体使⽤哪个要根据实际需求来进⾏选择。【个⼈观点】
xml⽂件结构说明:
每个xml⽂件的第⼀⾏都是xml⽂件说明,⼀般说明了xml的版本和编码信息;
如:<? xmlversion="1.0" encoding="UTF-8"?>
第⼆⾏为xml的起始节点,也是根节点
Opencv的xml⽂件默认的根节点为:<opencv_storage>……</opencv_storage>
每个xml⽂件有且只有⼀个根节点,其他节点都包含在这个根节点之内,每个节点⼜可以包含若⼲个⼦节点。这⼀点跟linux的⽬录结构很相似,linux⽂件系统有且仅有⼀个跟⽬录"/",其他⽬录都是挂载在这个根⽬录中的,每个⽬录⼜可以包含若⼲个⼦⽬录。
下⾯代码写数据部分⾸先以可写的⽅式打开了⼀个l⽂件,然后写⼊⼀个Mat矩阵,最后关闭。读数据部分⾸先以可读⽅式打开了⼀个l⽂件,然后读出“src1”节点⾥⾯的中间,最后关闭。
//========建⽴节点(写数据)=========
Mat src=(Mat_<double>(3,3)<<1,2,3,4,5,6,7,8,9);
FileStorage fswrite("l",FileStorage::WRITE);
fswrite<<"src1"<<src;
cout<<"Write Finished"<<endl;
//========遍历节点(读数据)=========
FileStorage fsread("l",FileStorage::READ);
Mat dst;
fsread["src1"]>>dst;
cout<<dst<<endl;
cout<<"Reaed Finished"<<endl;
其中读和写打开⽂件也可以是下⾯那样:
FileStorage fswrite;
fswrite.open("l",FileStorage::WRITE);
FileStorage fsread;
fsread.open("l",FileStorage::READ);
需要注意的是,FileStorage::WRITE每次都会新建⼀个⽂件,如果⽂件已经存在就会被覆盖掉,如果不想被覆盖,⽽是继续在已有的⽂件基础上添加内容,那么可以以FileStorage::APPEND的⽅式来打开!
2.vector和maps结构
vector:xml⽂件节点,不包含⼦节点。
map:xml的节点,包含⼦节点。
xml文件怎么打开
在输⼊vector的开始和结尾要分别输⼊“[”,“]”,⽽在输⼊map结构的开始和结尾要分别写⼊“{”,“}”,此外在输⼊vector和map前都要先输⼊标签名称。
下⾯代码为⼿动创建⼀个xml⽗节点(map),包含三个⼦节点(vector)。
//========建⽴节点(写数据)=========
FileStorage fswrite;
fswrite.open("l",FileStorage::WRITE);
fswrite<<"src"<<"{"<<"src1"<<"["<<1<<2<<3<<"]"//⼦节点
<<"src2"<<"["<<1<<2<<3<<"]"
<<"src3"<<"["<<1<<2<<3<<"]"<<"}";
cout<<"Write Finished"<<endl;
产⽣的xml⽂件如下:
<?xmlversion="1.0"?>
<opencv_storage>
<src>
<src1>
1 2 3</src1>
<src2>
1 2 3</src2>
<src3>
1 2 3</src3></src>
</opencv_storage>
3.遍历xml节点
下⾯代码⾸先创建了⼀个l的⽂件,然后像⾥⾯写⼊了五个Mat矩阵,最后打开这个⽂件,再遍历每⼀个节点并打印出来。
遍历的时候,先获取了⽂件的根节点,然后定义了⼀个迭代器,从根节点的第⼀个节点开始遍历,直到最后⼀个节点。
//========建⽴节点(写数据)=========
Mat src=(Mat_<double>(3,3)<<1,2,3,4,5,6,7,8,9);
FileStorage fswrite("l",FileStorage::WRITE);
fswrite<<"src1"<<src;
fswrite<<"src2"<<src;
fswrite<<"src3"<<src;
fswrite<<"src4"<<src;
fswrite<<"src5"<<src;
cout<<"Write Finished"<<endl;
//========遍历节点(读数据)=========
FileStorage fsread("l",FileStorage::READ);
FileNode ();
FileNodeIterator it;
for(it=rootnode.begin();it&d();it++){
Mat dst;
(*it)>>dst;
/
/it>>dst;//也可以
cout<<dst<<endl;
}
cout<<"Reaed Finished"<<endl;
结果分析:
下⾯是l的内容,其中<?xml version="1.0"?>为⽂件信息,说明了xml的版本为1.0;<opencv_storage>...</opencv_storage>为根节点,<src1 type_id="opencv-matrix">...</src1>为第⼀级节点(根节点的⼦节点),<rows>3</rows>为第⼆级节点,它是<src1 type_id="opencv-matrix">...</src1>的⼦节点。
<?xmlversion="1.0"?>
<opencv_storage>
<src1type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>
1. 2. 3. 4. 5. 6.7. 8. 9.</data></src1>
<src2type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>
1. 2. 3. 4. 5. 6.7. 8. 9.</data></src2>
<src3type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>
1. 2. 3. 4. 5. 6.7. 8. 9.</data></src3>
<src4type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>
1. 2. 3. 4. 5. 6.7. 8. 9.</data></src4>
<src5type_id="opencv-matrix">
<rows>3</rows>
<cols>3</cols>
<dt>d</dt>
<data>
1. 2. 3. 4. 5. 6.7. 8. 9.</data></src5>
</opencv_storage>
下⾯为终端打印信息:
4.⾃定义结构存储
Opencv的xml可以轻松实现int,float,double,string,mat等数据类型的存储和读取,但是往往我们需要保存⾃定义类型的数据,要实现⾃定义数据的存取,需要完成以下步骤,第⼀⾃定义数据,第⼆操作符重载。
/*************************
//⾃定义类
*************************/
class faceInfo{
public:
//写⼊数据【1】
void write(FileStorage& fs) const
{
fs<<"{"<<"matrix"<<matrix<<"label"<<label<<"}";
}
//读出数据【2】
void read(const FileNode& node)
{
node["matrix"]>>matrix;
node["label"]>>label;
}
public:
Mat matrix;
string label;
};
/*************************
//由操作符“<<”调⽤【3】
*************************/
void write(FileStorage& fs, const std::string&, const faceInfo& x)
{
x.write(fs);
}
/*************************
//由操作符“>>”调⽤【4】
*************************/
void read(const FileNode& node, faceInfo& x, const faceInfo& default_value = faceInfo())
{
pty())
x = default_value;
else
}
以上代码定义了⼀个faceInfo类,为了⽤”>>”,”<<”这两个符号进⾏读写操作,代码中【1】【2】【3】【4】4个函数是必须要实现的,定义好了之后就可以进⾏读写了,有⼀点⾮常值得注意,先看代码在分析。