yaml-cpp の使い方

しばらくぶりにブログ書いたと思ったら,「こんな小ネタかよ!!」って突っ込まれそうで,すみませぬ...
ちょくちょく遊びもはさみながら,割とマンスリーマンションに籠もってプログラムとにらめっこしてるんですが,思いのほかハマってしまい,悶々としてました....が,月も変わって6月になったので(ついに無職になったので(笑)),少しずつ溜まったネタをブログにもアップしていきたいと思います.

キャリブレーション結果のyamlファイルへの出力

で,ひとまず小ネタなんですが,三次元復元をするために一眼レフで写真をとってたんですが,画角が狭すぎてイマイチだったので魚眼カメラを買いました.このキャリブにはまってたんですが,出口が見えたのでその結果を yaml ファイルに出力しようと思い,ちょっと調べたのでそのメモです.

OpenCV とか ROS とかでカメラのキャリブ結果って下記のようなフォーマットで保存されると思うんですが,どうやったらそんな感じに出力できるかを調べました.

ROS のキャリブレーション結果

image_width: 640
image_height: 480
camera_name: camera
camera_matrix:
  rows: 3
  cols: 3
  data: [318.898697, 0, 311.613177, 0, 319.855587, 291.248501, 0, 0, 1]
distortion_model: plumb_bob
distortion_coefficients:
  rows: 1
  cols: 5
  data: [0.016615, -0.025819, 0.002167, 0.003753, 0]
rectification_matrix:
  rows: 3
  cols: 3
  data: [1, 0, 0, 0, 1, 0, 0, 0, 1]
projection_matrix:
  rows: 3
  cols: 4
  data: [315.782593, 0, 314.915759, 0, 0, 318.99234, 293.472369, 0, 0, 0, 1, 0]

Vectoryamlファイルへの出力フォーマット設定

Vectoryaml ファイルに出力した時に,上記のフォーマットのように "[xx, xx, xx ,xx]" とならず...ここでちょっとはまってたんですが,yaml のノードの一つ一つの要素に Style を設定できるんですね.これを使ったらすんなり行きました.

SetStyle の該当部
    cam["distortion_coefficients"]["data"] = std::vector<float>(
        dist_coeffs.data(), dist_coeffs.data() + dist_coeffs.size());
    cam["distortion_coefficients"]["data"].SetStyle(YAML::EmitterStyle::Flow);

最終的なテストコード


// Self Header
#include <fileutil/yamlfile_util.h>

// STL
#include <fstream>
#include <map>

// Glog
#include <glog/logging.h>

// yaml-cpp
#include <yaml-cpp/yaml.h>

namespace fileutil {

void GenerateCailbfileInYaml(const std::string& path,
                             const Eigen::Vector2i& size,
                             const Eigen::Matrix3d& K,
                             const Eigen::Matrix3d& new_K,
                             const Eigen::VectorXd& dist_coeffs) {
  YAML::Node cam;
  {
    cam["image_width"] = size(0);
    cam["image_height"] = size(1);
    cam["camera_name"] = "insta_front";

    // Camera Matrix
    cam["camera_matrix"]["rows"] = 3;
    cam["camera_matrix"]["cols"] = 3;
    cam["camera_matrix"]["data"] =
        std::vector<float>(K.data(), K.data() + K.rows() * K.cols());
    cam["camera_matrix"]["data"].SetStyle(YAML::EmitterStyle::Flow);

    // Distortion Model
    cam["distortion_model"] = "fisheye";

    // Distortion Coefficient
    cam["distortion_coefficients"]["rows"] = 1;
    cam["distortion_coefficients"]["cols"] = 4;
    cam["distortion_coefficients"]["data"] = std::vector<float>(
        dist_coeffs.data(), dist_coeffs.data() + dist_coeffs.size());
    cam["distortion_coefficients"]["data"].SetStyle(YAML::EmitterStyle::Flow);

    // New Camera Matrix
    cam["new_camera_matrix"]["rows"] = 3;
    cam["new_camera_matrix"]["cols"] = 3;
    cam["new_camera_matrix"]["data"] = std::vector<float>(
        new_K.data(), new_K.data() + new_K.rows() * new_K.cols());
    cam["new_camera_matrix"]["data"].SetStyle(YAML::EmitterStyle::Flow);
  }

  // Distortion Coefficient

  YAML::Emitter out;
  out << cam;

  std::ofstream file(path);
  file << out.c_str();
  file.close();
}

void ReadCalibfileInYaml(const std::string& path, Eigen::Vector2i& size,
                         Eigen::Matrix3d& K, Eigen::Matrix3d& new_K,
                         Eigen::VectorXd& dist_coeffs) {
  YAML::Node cam = YAML::LoadFile(path);

  CHECK(!cam.IsNull()) << "Specified yaml file can not be read.";

  // Size
  {
    size(0) = cam["image_width"].as<int>();
    size(1) = cam["image_height"].as<int>();
    CHECK(size(0) > 0 && size(1) > 0) << "The image size has to be defined.";
  }

  // Camera Model
  {
    std::string camera_model = cam["distortion_model"].as<std::string>();
    CHECK(camera_model == std::string("fisheye"))
        << "Currently only fisheye is supported.";
  }

  // Camera Matrix.
  {
    int rows = cam["camera_matrix"]["rows"].as<int>();
    int cols = cam["camera_matrix"]["cols"].as<int>();
    CHECK(rows == 3 && cols == 3) << "Size of the camera matrix has to be 3x3";

    std::vector<double> cam_mat =
        cam["camera_matrix"]["data"].as<std::vector<double>>();
    K = Eigen::Matrix3d(cam_mat.data());
  }

  // New Camera Matrix
  {
    int rows = cam["new_camera_matrix"]["rows"].as<int>();
    int cols = cam["new_camera_matrix"]["cols"].as<int>();
    CHECK(rows == 3 && cols == 3) << "Size of the camera matrix has to be 3x3";

    std::vector<double> cam_mat =
        cam["new_camera_matrix"]["data"].as<std::vector<double>>();
    new_K = Eigen::Matrix3d(cam_mat.data());
  }

  // Distortion Coefficient
  {
    std::vector<double> tmp_dist_coeffs =
        cam["distortion_coefficients"]["data"].as<std::vector<double>>();
    dist_coeffs =
        Eigen::VectorXd::Map(tmp_dist_coeffs.data(), tmp_dist_coeffs.size());
  }
}

}  // namespace fileutil

int main() {
  std::string path = "/home/koichi/workspace/tools/tools/calib.yml";

  {
    Eigen::Vector2i size;
    size(0) = 1400;
    size(1) = 1500;

    Eigen::Matrix3d K;
    K(0, 0) = 700.11;
    K(1, 1) = 750.12;
    K(2, 2) = 1.0;
    K(0, 2) = 700.13;
    K(1, 2) = 750.14;

    Eigen::Matrix3d new_K = K;

    Eigen::VectorXd dist_coeffs(4);
    dist_coeffs(0) = 1.00;
    dist_coeffs(1) = 1.10;
    dist_coeffs(2) = 1.20;
    dist_coeffs(3) = 1.30;

    fileutil::GenerateCailbfileInYaml(path, size, K, new_K, dist_coeffs);
  }

  {
    Eigen::Vector2i size;
    Eigen::Matrix3d K, new_K;
    Eigen::VectorXd dist_coeffs;
    fileutil::ReadCalibfileInYaml(path, size, K, new_K, dist_coeffs);

    LOG(INFO) << "Size : " << std::endl << size << std::endl;
    LOG(INFO) << "K : " << std::endl << K << std::endl;
    LOG(INFO) << "new_K : " << std::endl << new_K << std::endl;
    LOG(INFO) << "dist_coeffs : " << std::endl << dist_coeffs << std::endl;
  }

  return 0;
}