1 FileStorage

OpenCV中可以使用FileStorage类对xml,yml等文本文件进行读写。

1.1 重要函数

1.1.1 构造函数FileStorage

函数作用:构造函数,打开待操作的文件;

  1. cv::FileStorage::FileStorage(const cv::String& filename, int flags, const cv::String& encoding = cv::String())
  • filename:待操作的文件名;
  • flags:操作方式,如读READ、写WRITE、追加APPEND等;

flags

含义

READ

以读的形式打开

WRITE

以写的形式打开

APPEND

以追加的形式打开

MEMORY

从内存中读写数据

FORMAT_MASK

格式化数据的掩码

FORMAT_AUTO

自动格式化数据

FORMAT_XML

将文件按照xml方式读写

FORMAT_YAML

将文件按照yaml方式读写

FORMAT_JSON

将文件按照JSON方式读写

  • encoding:文件编码方式,到目前位置(opencv 4.4.0),尚不支持UTF-16,只能使用8位编码方式;
  1. cv::FileStorage::FileStorage()

默认构造函数;
一般与cv::FileStorage::open(const cv::String& filename, int flags, const cv::String& encoding = cv::String())配合使用;

打开文件之前,会先调用 cv::FileStorage::release(),关闭文件并释放所有的缓存;

1.1.2 析构函数~FileStorage

函数作用:析构函数,关闭待操作的文件;
析构函数调用cv::FileStorage::release()

1.1.3 release

函数作用:关闭文件,并释放所有的缓存;

1.1.4 isOpened

函数作用:判断文件是否打开成功;
bool cv::FileStorage::isOpened()

1.1.5 operator<<

重载运算符函数作用:将数据写入到某个节点中;

1.1.6 operator>>

重载运算符函数作用:从某个节点中读取数据,并写入到某个变量中;

1.1.7 operator[]

重载运算符函数作用:取出某个节点的数据;

1.2 数据存储

FileStorage中的数据存储有两种方式:

  1. mapping(键值对,key/value pair)
  2. sequence(没有名称的序列)

文件格式如下:

<?xml version="1.0"?>
<opencv_storage>
  <Style1>
    <day1>Monday</day1>
    <day2>Tuesday</day2>
    <day3>Wednesday</day3>
    <day4>Thursday</day4>
    <day5>Friday</day5>
    <day6>Saturday</day6>
    <day7>Sunday</day7></Style1>
  <Style2>
    1 2 3 4 5 6 7</Style2>
</opencv_storage>

Style1节点按mapping方式存储,Style2节点按sequence方式存储;

代码如下:

cv::FileStorage fs("xmlFile.xml", cv::FileStorage::WRITE);
// mapping
fs << "Style1";
fs << "{";
fs << "day1" << "Monday";
fs << "day2" << "Tuesday";
fs << "day3" << "Wednesday";
fs << "day4" << "Thursday";
fs << "day5" << "Friday";
fs << "day6" << "Saturday";
fs << "day7" << "Sunday";
fs << "}";

// sequence
fs << "Style2";
fs << "[";
fs << 1 << 2 << 3 << 4 << 5 << 6 << 7;
fs << "]";
fs.release();

2 写文件

首先以写的方式打开xml文件,若本地不存在该文件,则自动创建:

// 方法1
cv::FileStorage fs("xmlFile.xml", cv::FileStorage::WRITE);
// 方法2
cv::FileStorage fs;
fs.open("xmlFile.xml", cv::FileStorage::WRITE);

2.1 写入1个节点1个值

向文件中写入1个节点,该节点只包含1个值,示例代码及输出文件如下:

// 代码
std::string key = "day1";
int value = 1;
fs << key << value;

// 输出文件
<?xml version="1.0"?>
<opencv_storage>
<day1>1</day1>
</opencv_storage>

节点名称必须是字符串,且该字符串内容要符合C++变量命名规则;
节点值可以是int,float,double,string等;

2.2 写入1个节点多个值

向文件中写入1个节点,该节点包含多个值,示例代码及输出文件如下:

std::string key = "day1";
int value1 = 1;
std::string value2 = "Monday";
fs << key << "[" << value1 << value2 << "]";

<?xml version="1.0"?>
<opencv_storage>
<day1>
  1 Monday</day1>
</opencv_storage>

上述示例代码中,1个节点中有2个值,代码中必须使用一对中括号[]包含多个值,输出文件中多个值之间通过空格分隔;

2.3 创建多级节点

向文件中创建多个节点,并写入值,示例代码及输出文件如下:

std::string key = "day1";
int value = 1;
fs << "Year2022";
fs << "{" << "December";
fs << "{";
fs << key << value;
fs << "}" << "}" ;

<?xml version="1.0"?>
<opencv_storage>
<Year2022>
  <December>
    <day1>1</day1></December></Year2022>
</opencv_storage>

上述示例代码中,通过使用一对花括号{}来增加节点,多级节点则使用多对{}

2.4 写入cv::Mat对象

FileStorage支持将opencv中的Mat对象直接写入xml文件,示例代码及输出文件如下:

std::string key = "matrix";
cv::Mat value = cv::Mat::eye(3, 4, CV_64FC1);
fs << key << value;

<?xml version="1.0"?>
<opencv_storage>
<matrix type_id="opencv-matrix">
  <rows>3</rows>
  <cols>4</cols>
  <dt>d</dt>
  <data>
    1. 0. 0. 0. 0. 1. 0. 0. 0. 0. 1. 0.</data></matrix>
</opencv_storage>

Mat类型数据在xml文件中的节点名称为_ type_id="opencv-matrix",有4个子节点,rows表示矩阵行数,cols表示矩阵列数,dt表示数据类型(u->uchar,f->float,d->double),data表示数据内容;

2.5 复杂示例代码

下面是一个略复杂的示例代码及对应输出文件:

#include <opencv2/core.hpp>
int main()
{
	cv::FileStorage fs("xmlFile.xml", cv::FileStorage::WRITE);
	fs << "WeekNum" << 2;
	fs << "WeekInfo" << "[" << "ThisWeek" << 1 << "NextWeek" << 2 << "]";
	fs << "ThisWeek" << "{";
	fs << "Monday" << "{";
	fs << "weather" << "Sunny";
	fs << "temperature" << 20;
	fs << "diet" << "{";
	fs << "breakfast" << "[" << "bread" << 2 << "]";
	fs << "lunch" << "[" << "steak" << 1 << "]";
	fs << "dinner" << "milk";
	fs << "}" << "}";

	fs << "Sunday" << "{";
	fs << "weather" << "Rainy";
	fs << "temperature" << 15;
	fs << "diet" << "{";
	fs << "breakfast" << "[" << "porridge" << 1 << "]";
	fs << "lunch" << "[" << "rice" << 2 << "]";
	fs << "dinner" << "coffee";
	fs << "}";
	fs << "plan" << "learning OpenCV FileStorage";
	fs << "}" << "}";

	cv::Mat matEye = cv::Mat::eye(3, 3, CV_8UC1);
	cv::Mat matZeros = cv::Mat::zeros(4, 5, CV_32FC1);
	fs << "NextWeek" << "{";
	fs << "plan" << "{";
	fs << "MatLearning" << "[" << matEye << matZeros << "]";
	fs << "game" << "Plants V.S. JiangShi, ^o^.";
	fs << "}" << "}";
	fs.release();
	std::system("pause");
}
<?xml version="1.0"?>
<opencv_storage>
<WeekNum>2</WeekNum>
<WeekInfo>
  ThisWeek 1 NextWeek 2</WeekInfo>
<ThisWeek>
  <Monday>
    <weather>Sunny</weather>
    <temperature>20</temperature>
    <diet>
      <breakfast>
        bread 2</breakfast>
      <lunch>
        steak 1</lunch>
      <dinner>milk</dinner></diet></Monday>
  <Sunday>
    <weather>Rainy</weather>
    <temperature>15</temperature>
    <diet>
      <breakfast>
        porridge 1</breakfast>
      <lunch>
        rice 2</lunch>
      <dinner>coffee</dinner></diet>
    <plan>"learning OpenCV FileStorage"</plan></Sunday></ThisWeek>
<NextWeek>
  <plan>
    <MatLearning>
      <_ type_id="opencv-matrix">
        <rows>3</rows>
        <cols>3</cols>
        <dt>u</dt>
        <data>
          1 0 0 0 1 0 0 0 1</data></_>
      <_ type_id="opencv-matrix">
        <rows>4</rows>
        <cols>5</cols>
        <dt>f</dt>
        <data>
          0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0. 0.</data></_></MatLearning>
    <game>"Plants V.S. JiangShi, ^o^."</game></plan></NextWeek>
</opencv_storage>

3 读文件

首先以读的方式打开xml文件:

// 方法1
cv::FileStorage fs("xmlFile.xml", cv::FileStorage::READ);
// 方法2
cv::FileStorage fs;
fs.open("xmlFile.xml", cv::FileStorage::READ);

以下示例代码中,读取的xml文件为2.5节中输出的文件;

3.1 读取1个节点中的1个值

从文件中读取1个节点,该节点只包含1个值,示例代码如下:

// 方法1
int value = fs["WeekNum"];

// 方法2
int value;
fs["WeekNum"] >> value;

// 方法3
cv::FileNode fn1 = fs["WeekNum"];
cv::FileNodeIterator fn1_iter = fn1.begin();
int value = *fn1_iter;

// 方法4
// 方法3
cv::FileNode fn1 = fs["WeekNum"];
cv::FileNodeIterator fn1_iter = fn1.begin();
int value;
*fn1_iter >> value;

3.2 读取1个节点中的多个值

从文件中读取1个节点,该节点包含4个值,示例代码如下:

// 方法1
cv::FileNode fn2 = fs["WeekInfo"];
cv::FileNodeIterator fn2_iter = fn2.begin();
// 以下通过赋值操作读取数据,也能使用>>重载运算符
std::string str_value1 = (std::string)*fn2_iter;
int i_value2 = (int)*(++fn2_iter);
std::string str_value3 = (std::string)*(++fn2_iter);
int i_value4 = (int)*(++fn2_iter);

// 方法2
cv::FileNode fn2 = fs["WeekInfo"];
if (cv::FileNode::SEQ == fn2.type()) {
	// 以下通过赋值操作读取数据,也能使用>>重载运算符
  std::string str_value1 = (std::string)fn2[0];
  int i_value2 = (int)fn2[1];
  std::string str_value3 = (std::string)fn2[2];
  int i_value4 = (int)fn2[3];
}

3.3 读取多级节点中的值

从文件中读取多级节点中的值,示例代码如下:

cv::FileNode fn3 = fs["ThisWeek"];
cv::FileNode fn31 = fn3["Monday"];
cv::FileNode fn313 = fn31["diet"];
// 以下通过赋值操作读取数据,也能使用>>重载运算符
std::string str_value = (std::string)fn313["dinner"];

3.4 读取cv::Mat类型的数据

FileStorage支持从xml文件中直接读取opencv中的Mat对象,示例代码及输出文件如下:

cv::FileNode fn4 = fs["NextWeek"];
	cv::FileNode fn41 = fn4["plan"];
	cv::FileNode fn411 = fn41["MatLearning"];
	// 只能使用>>重载运算符
	cv::Mat mat1, mat2;
	fn411[0] >> mat1;
	fn411[1] >> mat2;

3.5 复杂示例代码

下面是一个略复杂的示例代码及对应输出:

int main()
{
	cv::FileStorage fs("xmlFile.xml", cv::FileStorage::READ);
	std::cout << "-WeekNum: ";
	cv::FileNode fn1 = fs["WeekNum"];
	std::cout << (int)fn1 << std::endl;

	std::cout << "-WeekInfo" << std::endl;
	cv::FileNode fn2 = fs["WeekInfo"];
	if (cv::FileNode::SEQ == fn2.type()) {
		std::cout << (std::string)fn2[0] << " ";
		std::cout << (int)fn2[1] << " ";
		std::cout << (std::string)fn2[2] << " ";
		std::cout << (int)fn2[3] << std::endl;
	}
	
	std::cout << "-ThisWeek:" << std::endl;
	cv::FileNode fn3 = fs["ThisWeek"];
	std::cout << "--Monday:" << std::endl;
	cv::FileNode fn31 = fn3["Monday"];
	std::cout << "---weather: ";
	std::cout << (std::string)fn31["weather"] << std::endl;
	std::cout << "---temperature: ";
	std::cout << (int)fn31["temperature"] << std::endl;
	std::cout << "---diet:" << std::endl;
	cv::FileNode fn313 = fn31["diet"];
	std::cout << "----breakfast: ";
	std::cout << (std::string)fn313["breakfast"][0] << " ";
	std::cout << (int)fn313["breakfast"][1] << std::endl;
	std::cout << "----lunch: ";
	std::cout << (std::string)fn313["lunch"][0] << " ";
	std::cout << (int)fn313["lunch"][1] << std::endl;
	std::cout << "----dinner: ";
	std::cout << (std::string)fn313["dinner"] << std::endl;

	std::cout << "--Sunday:" << std::endl;
	cv::FileNode fn32 = fn3["Sunday"];
	std::cout << "---weather: ";
	std::cout << (std::string)fn32["weather"] << std::endl;
	std::cout << "---temperature: ";
	std::cout << (int)fn32["temperature"] << std::endl;
	std::cout << "---diet:" << std::endl;
	cv::FileNode fn323 = fn32["diet"];
	std::cout << "----breakfast: ";
	std::cout << (std::string)fn323["breakfast"][0] << " ";
	std::cout << (int)fn323["breakfast"][1] << std::endl;
	std::cout << "----lunch: ";
	std::cout << (std::string)fn323["lunch"][0] << " ";
	std::cout << (int)fn323["lunch"][1] << std::endl;
	std::cout << "----dinner: ";
	std::cout << (std::string)fn323["dinner"] << std::endl;
	std::cout << "---plan: ";
	std::cout << (std::string)fn32["plan"] << std::endl;

	std::cout << "-NextWeek:" << std::endl;
	cv::FileNode fn4 = fs["NextWeek"];
	std::cout << "--plan:" << std::endl;
	cv::FileNode fn41 = fn4["plan"];
	std::cout << "---MatLearning:" << std::endl;
	cv::FileNode fn411 = fn41["MatLearning"];
	// 只能使用>>重载运算符
	cv::Mat mat1, mat2;
	fn411[0] >> mat1;
	fn411[1] >> mat2;
	std::cout << mat1 << std::endl;
	std::cout << mat2 << std::endl;
	std::cout << "---game: ";
	cv::FileNode fn412 = fn41["game"];
	std::cout << (std::string)fn412 << std::endl;

	fs.release();
	std::system("pause");
	return 0;
}
-WeekInfo
ThisWeek 1 NextWeek 2
-ThisWeek:
--Monday:
---weather: Sunny
---temperature: 20
---diet:
----breakfast: bread 2
----lunch: steak 1
----dinner: milk
--Sunday:
---weather: Rainy
---temperature: 15
---diet:
----breakfast: porridge 1
----lunch: rice 2
----dinner: coffee
---plan: learning OpenCV FileStorage
-NextWeek:
--plan:
---MatLearning:
[  1,   0,   0;
   0,   1,   0;
   0,   0,   1]
[0, 0, 0, 0, 0;
 0, 0, 0, 0, 0;
 0, 0, 0, 0, 0;
 0, 0, 0, 0, 0]
---game: Plants V.S. JiangShi, ^o^.