总结一下就是为了读写出多种多样格式并且减少依赖可以选择std::fstream的ifstream和ofstream)
。
使用yaml-cpp
库可以将yaml文件以节点YAML::Node
的形式载入YAML::LoadFile
,然后用std::fstream的ofstream
写入文件。
读文件的时候首选cv::fileStorage
因为可以很好的提取矩阵,其次选择std::fstream的ifstream
麻烦在于要一行一行的读取解析耗费心力。
整齐划一的格式例如文件路径适合std::fstream
例如ORB_SLAM2中载入图片和字典。
多种多样的配置参数和相机参数适合用cv::fileStorage
可以更好的解析供OpenCV使用。
ORB_SLAM2
的LoadImages
函数用std::ifstream
读取txt
文件中的图片。
ORB_SLAM2
的saved_trajectory
函数用std::ofstream
保存轨迹和位姿到txt
文件。
ORB_SLAM2
的fsSettings
函数用cv::FileStorage::READ
读取读取yaml
文件中的相机和配置参数。
git clone https://github.com/jbeder/yaml-cpp.git
cd yaml-cpp
mkdir build && cd build
cmake -DYAML_BUILD_SHARED_LIBS=ON ..
make -j4
sudo make install
#
表示注释key: value
冒号后面要加一个空格hash: { name: Steve, foo: bar }
key:
child-key: value
child-key2: value2
key
,配合一个冒号加一个空格代表一个 value:
意思即对象的属性是一个数组 [complexkey1,complexkey2]
,对应的值也是一个数组 [complexvalue1,complexvalue2]
?
- complexkey1
- complexkey2
:
- complexvalue1
- complexvalue2
-
开头按次序排列的值,构成一个数组。- A
- B
- C
key: [value1, value2, ...]
-
- A
- B
- C
companies:
-
id: 1
name: company1
price: 200W
-
id: 2
name: company2
price: 500W
companies: [{id: 1,name: company1,price: 200W},{id: 2,name: company2,price: 500W}]
languages:
- Ruby
- Perl
- Python
websites:
YAML: yaml.org
Ruby: ruby-lang.org
Python: python.org
Perl: use.perl.org
纯量是最基本的不可再分的值
布尔值
boolean:
- TRUE #true,True都可以
- FALSE #false,False都可以
浮点数
float:
- 3.14
- 6.8523015e+5 #可以使用科学计数法
整数
int:
- 123
- 0b1010_0111_0100_1010_1110 #二进制表示
Null
null:
nodeName: 'node'
parent: ~ #使用~表示null
字符串
string:
- 哈哈
- 'Hello world' #可以使用双引号或者单引号包裹特殊字符
- newline
newline2 #字符串可以拆成多行,每一行会被转化成一个空格
日期
date:
- 2018-02-17 #日期必须使用ISO 8601格式,即yyyy-MM-dd
时间
datetime:
- 2018-02-17T15:02:31+08:00 #时间使用ISO 8601格式,时间和日期之间使用T连接,最后使用+代表时区
Node 是 yaml-cpp 中的核心概念,是最重要的数据结构,它用于存储解析后的 yaml 信息。Node一共有以下几种type
Null 空节点
Sequence 对应YAML格式中的数组
Map 对应YAML格式中的对象
Scalar 对应YAML格式中的常量
生成 Node 的形式有很多种, loadFile() 是最常见的一种。
Node LoadFile(const std::string& filename)
filename 就是配置文件的路径。
有了 Node 之后,所有的信息都可以检索到。
cout << "name:" << config["name"].as<string>() << endl;
`as<string>()`表示将解析的内容使用模板方法转换成 `string` 类型
skills 信息读取
skills:
c++: 1
java: 1
android: 1
python: 1
cout << "skills c++:" << config["skills"]["c++"].as<int>() << endl;
cout << "skills java:" << config["skills"]["java"].as<int>() << endl;
yaml-cpp 通过迭代的方式,访问 Node 中的内容,比如访问 skills 下面的各个元素
for(YAML::const_iterator it= config["skills"].begin(); it != config["skills"].end();++it)
{
cout << it->first.as<string>() << ":" << it->second.as<int>() << endl;
}
用 begin() 获取迭代器,用 end() 判断迭代器是否结束。
Node可以使用文件流的方式写入文件
std::ofstream fout("config.yaml");
fout << node <<std::endl;
fout.close();
cv::FileStorage fsi(fileName , cv::FileStorage::READ);
cv::FileStorage fso(fileName , cv::FileStorage::WRITE);
FileStorage的文件操作模式一共分为四种:READ,WRITE,APPEND,MEMORY。
文档打开后很关心的一件事就是确认是否成功,FileStorage有自己的成员函数返回文件打开状态:
// failed
if ( !fs.isOpened() ){
std::cout<<"Save File Failed!"<<std::endl;
return ;
}
// succeed
else {
...
}
FileStorage文件关闭比较简单
fs.release();
FileStorage文件读与写的方法与C++语言中的文件流对象的使用很像,对>>
和<<
进行了重载,分别用于文件读取和写入。很棒的是,FileStorage支持一些常用格式的直接读写,例如字符、字符串、数字、cv::Mat等。对于不支持的数据结构,只能按照规则自己去写啦。
// 字符和数字
fs << "frameCount" << 5;
cv::Mat_<double> cameraMat = cv::Mat_<double>::zeros(3, 3);
// cv::Mat
fs << "Camera Intrinsic Matrix" << cameraMat;
写入的时候要注意不可以写"."
这个符号,YAML有效字符是:[a-z],[A-Z],[0-9],”-“,”_”和空格。
文件读取的方法有两种:
// first method: use operator on FileNode.
int frameCount = (int)fs2["frameCount"];
// second second method: use cv::FileNode::operator >>
int frameCount;
fs2["frameCount"] >> frameCount;
Mat的操作
这一点真的很不错,而且与C++的输入输出方法很接近:
cv::Mat_<double> cameraMat = cv::Mat_<double>::zeros(3, 3);
cv::Mat_<double> distCoeffes = ( cv::Mat_<double>(5, 1)<< 0.1, 0.01, -0.001, 0.0, 0.0 );
// C++
std::cout<<"Camera Matrix"<<std::endl<<cv::Mat::Mat(cameraMat)<<std::endl;
std::cout<<"Distortion Coefficients"<<std::endl<<cv::Mat::Mat(distCoeffes)<<std::endl;
// cv::FileStorage
fs << "Camera Matrix" << cameraMat;
fs << "Distortion Coefficients"<<distCoeffes;
集合的操作
// Mappings write
int x(1.0), y(0.0);
fs << "features" << "["; // also can be "[:"
fs <<"{:" << "x" << x << "y" << "}" << "]";
// Mappings read
cv::FileNode features = fs2["features"];
// 遍历查看
cv::FileNodeIterator it = features.begin();
std::cout<<
"x="<<(int)(*it)["x"]<<
" y="<<(int)(*it)["y"]<<
" z="<<(int)(*it)["z"]<<std::endl;
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main() {
/*********************************************************
* std::ifstream Input file stream 读取文件
* std::ofstream Output file stream 写入文件
*********************************************************/
std::ifstream std_in_file_txt("../rgb.txt");
std::ofstream std_out_file_txt("../std_out_file_txt.txt");
std::ofstream std_out_file_yaml("../std_out_file_yaml.yaml");
vector<double> vTimestamps;
// 前三行是注释,跳过
string s0;
getline(std_in_file_txt, s0);
getline(std_in_file_txt, s0);
getline(std_in_file_txt, s0);
while (!std_in_file_txt.eof()) {
string s;
getline(std_in_file_txt, s);
if (!s.empty()) {
stringstream ss;
ss << s;
double t;
ss >> t;
// 把时间戳写入文件
// std::setprecision(9) c++浮点数位数
// std::fixed 一般的方式输出浮点数 不是科学计数法
std_out_file_txt << std::setprecision(19) << std::fixed;
std_out_file_txt << t << std::endl;
// 虽然可以生成 .yaml 文件但是格式不是yaml的格式因为没有文件头"%YAML:1.0"
std_out_file_yaml << t << endl;
std_out_file_yaml << t << endl;
}
}
std::ifstream std_in_file_yaml("../read_no_yaml_head.yaml");
std::ofstream std_out_file_yaml_txt("../std_out_file_yaml_txt.txt");
std::ofstream std_out_file_yaml_yaml("../std_out_file_yaml_yaml.yaml");
while (!std_in_file_yaml.eof()) {
string s;
getline(std_in_file_yaml, s);
std_out_file_yaml_txt << s << endl;
std_out_file_yaml_yaml << s << endl;
}
/***********************************************************************************
* cv::FileStorage cv_file_in("cv_file_read.yaml", FileStorage::READ); 读取文件
* cv::FileStorage cv_file_out("cv_file_out.yaml", FileStorage::WRITE); 写入文件
***********************************************************************************/
// --------------------------------- FileStorage 读取文件的方式 --------------------------------
cv::FileStorage fsSettings("../read_with_yaml_head.yaml", cv::FileStorage::READ);
// 用 std::ofstream 生成的 yaml 文件是不能被 cv::FileStorage 正常读取的
// 因为使用 cv::FileStorage 加载的yaml文件第一行必须是 %YAML:1.0
if(!fsSettings.isOpened()){
cerr << "ERROR: Wrong path at : " << endl;
exit(-1);
}
// first method: use (type) operator on FileNode.
float fx = fsSettings["Camera.fx"];
float fy = fsSettings["Camera.fy"];
float cx = fsSettings["Camera.cx"];
float cy = fsSettings["Camera.cy"];
cout << fx << fy << cx << cy << endl;
// second method: use FileNode::operator ">>"
cv::Mat DistCoef(5,1,CV_32F);
fsSettings["Camera.k1"] >> DistCoef.at<float>(0);
fsSettings["Camera.k2"] >> DistCoef.at<float>(1);
fsSettings["Camera.p1"] >> DistCoef.at<float>(2);
fsSettings["Camera.p2"] >> DistCoef.at<float>(3);
fsSettings["Camera.k3"] >> DistCoef.at<float>(4);
cout << DistCoef << endl;
cv::Mat cameraMatrix, camera_matrix;
fsSettings["cameraMatrix"] >> cameraMatrix;
cout << "cameraMatrix\n" << cameraMatrix << endl;
fsSettings["camera_matrix"] >> camera_matrix;
std::cout << camera_matrix << std::endl;
fsSettings.release();
// --------------------------------- FileStorage 写入文件的方式 -----------------------------------
cv::FileStorage cv_file_write("../out_with_yaml_head.yaml", cv::FileStorage::WRITE);
float fx_out = fsSettings["Camera.fx"];
float fy_out = fsSettings["Camera.fy"];
float cx_out = fsSettings["Camera.cx"];
float cy_out = fsSettings["Camera.cy"];
cv::Mat K = cv::Mat::eye(3, 3, CV_32F);
K.at<float>(0, 0) = fx_out;
K.at<float>(1, 1) = fy_out;
K.at<float>(0, 2) = cx_out;
K.at<float>(1, 2) = cy_out;
// 写入的时候一定要注意"键"的格式不能有英文的符号"." 写入的时候一定要同时有键和值
cv_file_write << "Camera_fx" << fx;
// 构造矩阵写入
Mat cameraMatrix_out = (Mat_<double>(3, 3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1);
Mat distCoeffs = (Mat_<double>(5, 1) << 0.1, 0.01, -0.001, 0, 0);
cv_file_write << "cameraMatrix" << cameraMatrix << "distCoeffs" << distCoeffs;
cv_file_write.release();
/*******************************************************************************************
* YAML::Node config = YAML::LoadFile("../read_no_yaml_head.yaml"); 读取文件
*
* std::fstream fstream("/home/q/CLionProjects/4-yaml/yaml/read_no_head_out.yaml"); 写入文件
* fstream << node << std::endl; 写入文件
*******************************************************************************************/
YAML::Node config = YAML::LoadFile("../read_no_yaml_head.yaml");
// 这里要注意 文件内容要严格遵守yaml语法特别是key: value之间的空格
cout << "image_width: " << config["image_width"].as<string>() << endl;
cout << "age: " << config["age"].as<int>() << endl;
cout << "skills c++: " << config["skills"]["c++"].as<int>() << endl;
cout << "camera_matrix: \n" << config["camera_matrix"] << endl;
cout << "data: \n" << config["camera_matrix"]["data"] << endl;
cout << "distortion_coefficients: " << config["distortion_coefficients"] << endl;
cout << "rectification_matrix: " << config["rectification_matrix"] << endl;
cout << "projection_matrix: " << config["projection_matrix"] << endl;
// 创建节点准备写入文件
YAML::Node node;
// 创建一个字典 value的值是常量
node["number"] = 66;
// 创建一个字典字典的值是序列
node["seq"].push_back("xiaoqiu");
node["seq"].push_back("slambiji");
cout << node << endl;
// std::fstream 写入文件--------------------------------------------------------------------
std::ofstream ofstream("../read_no_head_out.yaml");
// 设置配置文件node数据
ofstream << "camera_matrix:" << std::endl;
ofstream << config["camera_matrix"] << endl;
ofstream << node << std::endl;
ofstream.close();
return 0;
}
cmake_minimum_required(VERSION 2.8.3)
SET(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_STANDARD 11)
project(yaml)
find_package(OpenCV)
# Installing: /usr/local/lib/libyaml-cpp.so
set(YAML_CPP_LIBRARIES /usr/local/lib/libyaml-cpp.so)
add_executable(main main.cpp)
target_link_libraries(main ${OpenCV_LIBS} ${YAML_CPP_LIBRARIES})
// 本教程编译环境是ubuntu16.04 clion
#include
#include
#include
using namespace std;
int main()
{
string name;
string city;
cout << "Please enter your name: ";
// 这个函数是来完成读入一行数据
// getline()函数的功能是读入一行数据
// std::cin ; 以终端键盘输入的方式读入内容来写入文件
getline(cin, name);
cout << "Hello, " << name << endl;
// ifstream f; 以从文件中读取的方式读入内容来写入文件
ifstream f;
string s0;
f.open("/home/q/CLionProjects/cpp_tutorials/rgb.txt");
getline(f,s0);
cout << s0 << endl;
return 0;
}
cmake_minimum_required(VERSION 2.8.3)
project(yaml)
SET(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_STANDARD 11)
find_package(OpenCV)
# sudo apt-get install libyaml-cpp-dev
# locate libyaml-cpp.so
# /usr/lib/x86_64-linux-gnu/libyaml-cpp.so
set(YAML_CPP_LIBRARIES /usr/lib/x86_64-linux-gnu/libyaml-cpp.so)
add_executable(yaml yaml.cpp)
target_link_libraries(yaml ${OpenCV_LIBS} ${YAML_CPP_LIBRARIES})
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
int main() {
/*********************************************************
* std::ifstream Input file stream 读取文件
* std::ofstream Output file stream 写入文件
*********************************************************/
std::ifstream std_in_file_txt("../rgb.txt");
std::ofstream std_out_file_txt("../std_out_file_txt.txt");
std::ofstream std_out_file_yaml("../std_out_file_yaml.a-yaml");
vector<double> vTimestamps;
// 前三行是注释,跳过
string s0;
getline(std_in_file_txt, s0);
getline(std_in_file_txt, s0);
getline(std_in_file_txt, s0);
while (!std_in_file_txt.eof()) {
string s;
getline(std_in_file_txt, s);
if (!s.empty()) {
stringstream ss;
ss << s;
double t;
ss >> t;
// 把时间戳写入文件
// std::setprecision(9) c++浮点数位数
// std::fixed 一般的方式输出浮点数 不是科学计数法
std_out_file_txt << std::setprecision(19) << std::fixed;
std_out_file_txt << t << std::endl;
// 虽然可以生成 .a-yaml 文件但是格式不是yaml的格式因为没有文件头"%YAML:1.0"
std_out_file_yaml << t << endl;
std_out_file_yaml << t << endl;
}
}
std::ifstream std_in_file_yaml("../read_no_yaml_head.a-yaml");
std::ofstream std_out_file_yaml_txt("../std_out_file_yaml_txt.txt");
std::ofstream std_out_file_yaml_yaml("../std_out_file_yaml_yaml.a-yaml");
while (!std_in_file_yaml.eof()) {
string s;
getline(std_in_file_yaml, s);
std_out_file_yaml_txt << s << endl;
std_out_file_yaml_yaml << s << endl;
}
/***********************************************************************************
* cv::FileStorage cv_file_in("cv_file_read.a-yaml", FileStorage::READ); 读取文件
* cv::FileStorage cv_file_out("cv_file_out.a-yaml", FileStorage::WRITE); 写入文件
***********************************************************************************/
// --------------------------------- FileStorage 读取文件的方式 --------------------------------
cv::FileStorage fsSettings("../read_with_yaml_head.a-yaml", cv::FileStorage::READ);
// 用 std::ofstream 生成的 a-yaml 文件是不能被 cv::FileStorage 正常读取的
// 因为使用 cv::FileStorage 加载的yaml文件第一行必须是 %YAML:1.0
if(!fsSettings.isOpened()){
cerr << "ERROR: Wrong path at : " << endl;
exit(-1);
}
// first method: use (type) operator on FileNode.
float fx = fsSettings["Camera.fx"];
float fy = fsSettings["Camera.fy"];
float cx = fsSettings["Camera.cx"];
float cy = fsSettings["Camera.cy"];
cout << fx << fy << cx << cy << endl;
// second method: use FileNode::operator ">>"
cv::Mat DistCoef(5,1,CV_32F);
fsSettings["Camera.k1"] >> DistCoef.at<float>(0);
fsSettings["Camera.k2"] >> DistCoef.at<float>(1);
fsSettings["Camera.p1"] >> DistCoef.at<float>(2);
fsSettings["Camera.p2"] >> DistCoef.at<float>(3);
fsSettings["Camera.k3"] >> DistCoef.at<float>(4);
cout << DistCoef << endl;
cv::Mat cameraMatrix, camera_matrix;
fsSettings["cameraMatrix"] >> cameraMatrix;
cout << "cameraMatrix\n" << cameraMatrix << endl;
fsSettings["camera_matrix"] >> camera_matrix;
std::cout << camera_matrix << std::endl;
fsSettings.release();
// --------------------------------- FileStorage 写入文件的方式 -----------------------------------
cv::FileStorage cv_file_write("../out_with_yaml_head.a-yaml", cv::FileStorage::WRITE);
float fx_out = fsSettings["Camera.fx"];
float fy_out = fsSettings["Camera.fy"];
float cx_out = fsSettings["Camera.cx"];
float cy_out = fsSettings["Camera.cy"];
cv::Mat K = cv::Mat::eye(3, 3, CV_32F);
K.at<float>(0, 0) = fx_out;
K.at<float>(1, 1) = fy_out;
K.at<float>(0, 2) = cx_out;
K.at<float>(1, 2) = cy_out;
// 写入的时候一定要注意"键"的格式不能有英文的符号"." 写入的时候一定要同时有键和值
cv_file_write << "Camera_fx" << fx;
// 构造矩阵写入
Mat cameraMatrix_out = (Mat_<double>(3, 3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1);
Mat distCoeffs = (Mat_<double>(5, 1) << 0.1, 0.01, -0.001, 0, 0);
cv_file_write << "cameraMatrix" << cameraMatrix << "distCoeffs" << distCoeffs;
cv_file_write.release();
/*******************************************************************************************
* YAML::Node config = YAML::LoadFile("../read_no_yaml_head.a-yaml"); 读取文件
*
* std::fstream fstream("/home/q/CLionProjects/4-a-yaml/a-yaml/read_no_head_out.a-yaml"); 写入文件
* fstream << node << std::endl; 写入文件
*******************************************************************************************/
YAML::Node config = YAML::LoadFile("../read_no_yaml_head.a-yaml");
// 这里要注意 文件内容要严格遵守yaml语法特别是key: value之间的空格
cout << "image_width: " << config["image_width"].as<string>() << endl;
cout << "age: " << config["age"].as<int>() << endl;
cout << "skills c++: " << config["skills"]["c++"].as<int>() << endl;
cout << "camera_matrix: \n" << config["camera_matrix"] << endl;
cout << "data: \n" << config["camera_matrix"]["data"] << endl;
cout << "distortion_coefficients: " << config["distortion_coefficients"] << endl;
cout << "rectification_matrix: " << config["rectification_matrix"] << endl;
cout << "projection_matrix: " << config["projection_matrix"] << endl;
// 创建节点准备写入文件
YAML::Node node;
// 创建一个字典 value的值是常量
node["number"] = 66;
// 创建一个字典字典的值是序列
node["seq"].push_back("xiaoqiu");
node["seq"].push_back("slambiji");
cout << node << endl;
// std::fstream 写入文件--------------------------------------------------------------------
std::ofstream ofstream("../read_no_head_out.a-yaml");
// 设置配置文件node数据
ofstream << "camera_matrix:" << std::endl;
ofstream << config["camera_matrix"] << endl;
ofstream << node << std::endl;
ofstream.close();
return 0;
}