【ORB_SLAM2源码解读】从文件或终端读写yaml和txt文件代码实战教程

文章目录

    • yaml文件用法总结
    • 下面是ORB_SLAM2用到的地方
    • 安装yaml-cpp库
    • YAML基本语法
    • 对象
    • 数组
    • 复合结构
    • 常量
    • Opencv中的FileStorage
    • 融合三种方法的yaml读写的程序
    • CMakeLists.txt
    • getline()函数从终端和从文件中读数据方式代码实践
    • 编译源码

yaml文件用法总结

总结一下就是为了读写出多种多样格式并且减少依赖可以选择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用到的地方

ORB_SLAM2LoadImages函数用std::ifstream读取txt文件中的图片。

ORB_SLAM2saved_trajectory函数用std::ofstream保存轨迹和位姿到txt文件。

ORB_SLAM2fsSettings函数用cv::FileStorage::READ读取读取yaml文件中的相机和配置参数。

安装yaml-cpp库

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

YAML基本语法

  • 大小写敏感
  • 使用空格缩进表示层级关系
  • 同层级的元素左侧对齐
  • #表示注释

对象

  • 键值对的集合 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
    
  • YAML 支持多维数组,可以使用行内表示:
    key: [value1, value2, ...]
    
  • 数据结构的子成员是一个数组
    -
     - A
     - B
     - C
    
  • 一个相对复杂的例子,意思是 companies 属性是一个数组,每一个数组元素又是由 id、name、price 三个属性构成。
    companies:
        -
            id: 1
            name: company1
            price: 200W
        -
            id: 2
            name: company2
            price: 500W
    
  • 数组也可以使用流式(flow)的方式表示:
    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();
    

Opencv中的FileStorage

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;

融合三种方法的yaml读写的程序

#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;
}

CMakeLists.txt

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})

getline()函数从终端和从文件中读数据方式代码实践

// 本教程编译环境是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;
}

【ORB_SLAM2源码解读】从文件或终端读写yaml和txt文件代码实战教程_第1张图片
【ORB_SLAM2源码解读】从文件或终端读写yaml和txt文件代码实战教程_第2张图片

编译源码

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;
}


你可能感兴趣的:(从零开始学习SLAM,ORB_SLAM2,ORB_SLAM3)