有时候我们处理完图像后需要保存一下数据到文件上,以供下一步的处理。一个比较广泛的需求场景就是:我们对一幅图像进行特征提取之后,需要把特征点信息保存到文件上,以供后面的机器学习分类操作。那么如果遇到这样的场景,我们有什么好方法,搭建这类的小型数据库文件?我第一时间想到的是把这些数据全写到文件上,下次我们需要这些数据就把他们从文件里读出来就好了。
其实更好的办法是使用xml和yml,因为他们更具有可读性,简直就是为保存数据结构而生的好方法!OpenCV提供了很好用的读写xml/yml的类,我们只要掌握其读写要领,很容易就可以实现这个小型数据库。
xml/yml的写操作
如何将我们的数据写入文件保存下来?
一个简单数据写入的例子
下面是我们最常用的一些数据类型的写入xml的操作。
#include#include using namespace std;using namespace cv;typedef struct{ int x; int y; string s;}test_t;int main(int argc, char** argv){ FileStorage fs("test.xml", FileStorage::WRITE); //填入写操作 //测试数据 int a1 = 2; char a2 = -1; string str = "hello sysu!"; int arr[10] = { 1,2,3,4,5,6,7,8,9,10 }; test_t t = { 3,4,"hi sysu" }; map m; m["kobe"] = 100; m["james"] = 99; m["curry"] = 98; //写入文件操作,先写标注在写数据 fs << "int_data" << a1; fs << "char_data" << a2; fs << "string_data" << str; //写入数组 fs <<"array_data"<< "["; //数组开始 for (int i = 0; i < 10; i++) { fs << arr[i]; } fs << "]"; //数组结束 //写入结构体 fs << "struct_data" << "{"; //结构体开始 fs << "x" << t.x; fs << "y" << t.y; fs << "s" << t.s; fs << "}"; //结构结束 //map的写入 fs << "map_data" << "{"; //map的开始写入 map ::iterator it = m.begin(); for (; it != m.end(); it++) { fs << it->first << it->second; } fs << "}"; //map写入结束 return 0;}
打开test.xml文件,我们看到我们的数据保存是这样子的:
如果我们将文件存为test.yml,即
FileStorage fs("test.yml", FileStorage::WRITE);
那么我们最终的得到的test.yml是这样子的:
我们还可以保存为txt格式
FileStorage fs("test.txt", FileStorage::WRITE);
打开看是这样的:
我们还还可以保存为doc文件!
FileStorage fs("test.doc", FileStorage::WRITE);
打开看是这样子的:
我们可以看出,显然yml文件的排版更加简洁明了,xml文件却显得有点冗余和杂乱了。
一个复杂写入的例子
在这里举一个简易的学生信息系统文件的搭建,以熟悉一下较为复杂的数据结构的写入文件的操作流程。
#include#include using namespace std;using namespace cv;typedef struct{ string phone_num; string address;}contact_t;typedef struct{ string name; int age;}parents_t;typedef struct{ string name; int age; int id; contact_t contact_ways; parents_t parents[2];}student_t;int main(int argc, char** argv){ FileStorage fs("student.xml", FileStorage::WRITE); //填入写操作 student_t st[3]; memset(st, 0, sizeof(st)); //测试数据填入 st[0].name = "Kobe"; st[0].age = 21; st[0].id = 1; st[0].contact_ways.address = "1st building"; st[0].contact_ways.phone_num = "123"; st[0].parents[0].name = "dad"; st[0].parents[1].name = "mum"; st[0].parents[0].age = 40; st[0].parents[1].age = 39; st[1].name = "James"; st[1].age = 20; st[1].id = 2; st[1].contact_ways.address = "2st building"; st[1].contact_ways.phone_num = "12223"; st[1].parents[0].name = "daddy"; st[1].parents[1].name = "mumy"; st[1].parents[0].age = 44; st[1].parents[1].age = 38; fs << "student" << "["; //结构体数组的开始 for (int i = 0; i < 3; i++) { fs <<"{"; //结构体的开始 fs << "name" << st[i].name; fs << "age" << st[i].age; fs << "id" << st[i].id; fs << "contact_ways" << "{"; //嵌套结构体的开始 fs << "phone_number" << st[i].contact_ways.phone_num; fs << "address" << st[i].contact_ways.address; fs << "}"; //结构体结束 fs << "parents"<<"["; //嵌套结构体数组开始 for (int j = 0; j < 2; j++) { fs << "{"; fs << "name" << st[i].parents[j].name; fs << "age" << st[i].parents[j].age; fs << "}"; } fs << "]"; //嵌套结构体数组结束 fs << "}"; //结构体结束 } fs << "]"; // 结构体数组结束 return 0;}
打开student.xml文件,如下
<_> Kobe 21 1 "123" "1st building"<_> <_>dad 40 <_>mum 39 James 20 2 "12223" "2st building"<_> <_>daddy 44 <_>mumy 38 "" 0 0 "" ""<_> "" 0 <_>"" 0
若存储的是yml文件,打开如下:
%YAML:1.0student: - name: Kobe age: 21 id: 1 contact_ways: phone_number: "123" address: "1st building" parents: - name: dad age: 40 - name: mum age: 39 - name: James age: 20 id: 2 contact_ways: phone_number: "12223" address: "2st building" parents: - name: daddy age: 44 - name: mumy age: 38 - name: "" age: 0 id: 0 contact_ways: phone_number: "" address: "" parents: - name: "" age: 0 - name: "" age: 0
xml/yml的读操作
我们的数据已经稳妥地写入文件保存下来了,接下来我们想从该文件中读取出我们的数据,该如何操作呢?我们继续以上述的例子数据为例,讲解读操作。
一个简单读入的例子
我们举个简单例子,读入上面提到test.xml的数据。
#include#include using namespace std;using namespace cv;typedef struct{ int x; int y; string s;}test_t;int a1;int a2;string str;int arr[10];test_t t;map m;//打印出学生资料,来验证读取文件信息是否成功void data_info_dump(){ cout << "a1:" << a1 << endl; cout << "a2:" << a2 << endl; cout << "str:" << str << endl; cout << "t.x:" << t.x << endl; cout << "t.y:" << t.y << endl; cout << "t.s:" << t.s << endl; cout << "curry:" << m["curry"] << endl; cout << "kobe:" << m["kobe"] << endl; cout << "james:" << m["james"] << endl; for (int i = 0; i < 10; i++) { cout << arr[i] << endl; }}int main(int argc, char** argv){ FileStorage fs("test.xml", FileStorage::READ); //填入读操作 a1 = (int)fs["int_data"]; a2 = (int)fs["char_data"]; str = (string)fs["string_data"]; //读入数组 FileNode arr_node = fs["array_data"]; FileNodeIterator fni = arr_node.begin(); FileNodeIterator fniEnd = arr_node.end(); int count = 0; for (; fni != fniEnd; fni++) { arr[count++] = (int)(*fni); } //读入map FileNode map_node = fs["map_data"]; m["curry"] = (int)map_node["curry"]; m["james"] = (int)map_node["james"]; m["kobe"] = (int)map_node["kobe"]; //读入结构体 FileNode struct_node = fs["struct_data"]; t.x = (int)struct_node["x"]; t.y = (int)struct_node["y"]; t.s = (string)struct_node["s"]; data_info_dump(); return 0;}
打印如下:
一个复杂读入的例子
我们以读取上面所提到的student.xml为例,说明如何读取一个xml文件数据到内存。
#include#include using namespace std;using namespace cv;typedef struct{ string phone_num; string address;}contact_t;typedef struct{ string name; int age;}parents_t;typedef struct{ string name; int age; int id; contact_t contact_ways; parents_t parents[2];}student_t;student_t st[3];//打印出学生资料,来验证读取文件信息是否成功void stu_info_dump(){ for (int i = 0; i < 3; i++) { printf("第%d个学生\n",i+1); cout << "name:" << st[i].name << endl; cout << "id:" << st[i].id << endl; cout << "age:" << st[i].age << endl; cout << "contact address:" << st[i].contact_ways.address << endl; cout << "contact number:" << st[i].contact_ways.phone_num << endl; cout << "father name:" << st[i].parents[0].name << endl; cout << "father age:" << st[i].parents[0].age << endl; cout << "mother name:" << st[i].parents[1].name << endl; cout << "mother age:" << st[i].parents[1].age << endl; printf("\n\n"); }}int main(int argc, char** argv){ FileStorage fs("student.xml", FileStorage::READ); //填入读操作 memset(st, 0, sizeof(st)); FileNode student_node = fs["student"];//读取根节点 FileNodeIterator fni = student_node.begin(); //获取结构体数组迭代器 FileNodeIterator fniEnd = student_node.end(); int count = 0; for (; fni != fniEnd; fni++)//遍历 { st[count].name = (string)(*fni)["name"]; st[count].id = (int)(*fni)["id"]; st[count].age = (int)(*fni)["age"]; //contact结构体内容 FileNode contact = (*fni)["contact_ways"]; st[count].contact_ways.address = (string)contact["address"]; st[count].contact_ways.phone_num = (string)contact["phone_number"]; //parents结构体数组内容 FileNode parents = (*fni)["parents"]; FileNodeIterator fni2 = parents.begin(); //获取结构体数组迭代器 FileNodeIterator fniEnd2 = parents.end(); int count2 = 0; for (; fni2 != fniEnd2; fni2++)//遍历 { st[count].parents[count2].name = (string)(*fni2)["name"]; st[count].parents[count2].age = (int)(*fni2)["age"]; count2++; } count++; } stu_info_dump(); return 0;}
打印如下,这表明xml的数据已经成功读入内存了。