/**
* This file is part of ORB-SLAM3
*
* Copyright (C) 2017-2021 Carlos Campos, Richard Elvira, Juan J. Gómez Rodríguez, José M.M. Montiel and Juan D. Tardós, University of Zaragoza.
* Copyright (C) 2014-2016 Raúl Mur-Artal, José M.M. Montiel and Juan D. Tardós, University of Zaragoza.
*
* ORB-SLAM3 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ORB-SLAM3 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with ORB-SLAM3.
* If not, see .
*/
#include "Settings.h"
#include "CameraModels/Pinhole.h"
#include "CameraModels/KannalaBrandt8.h"
#include "System.h"
#include
#include
#include
using namespace std;
namespace ORB_SLAM3 {
template<>
float Settings::readParameter(cv::FileStorage& fSettings, const std::string& name, bool& found, const bool required){
cv::FileNode node = fSettings[name];
if(node.empty()){
if(required){
std::cerr << name << " required parameter does not exist, aborting..." << std::endl;
exit(-1);
}
else{
std::cerr << name << " optional parameter does not exist..." << std::endl;
found = false;
return 0.0f;
}
}
else if(!node.isReal()){
std::cerr << name << " parameter must be a real number, aborting..." << std::endl;
exit(-1);
}
else{
found = true;
return node.real();
}
}
template<>
int Settings::readParameter(cv::FileStorage& fSettings, const std::string& name, bool& found, const bool required){
cv::FileNode node = fSettings[name];
if(node.empty()){
if(required){
std::cerr << name << " required parameter does not exist, aborting..." << std::endl;
exit(-1);
}
else{
std::cerr << name << " optional parameter does not exist..." << std::endl;
found = false;
return 0;
}
}
else if(!node.isInt()){
std::cerr << name << " parameter must be an integer number, aborting..." << std::endl;
exit(-1);
}
else{
found = true;
return node.operator int();
}
}
template<>
string Settings::readParameter(cv::FileStorage& fSettings, const std::string& name, bool& found, const bool required){
cv::FileNode node = fSettings[name];
if(node.empty()){
if(required){
std::cerr << name << " required parameter does not exist, aborting..." << std::endl;
exit(-1);
}
else{
std::cerr << name << " optional parameter does not exist..." << std::endl;
found = false;
return string();
}
}
else if(!node.isString()){
std::cerr << name << " parameter must be a string, aborting..." << std::endl;
exit(-1);
}
else{
found = true;
return node.string();
}
}
template<>
cv::Mat Settings::readParameter(cv::FileStorage& fSettings, const std::string& name, bool& found, const bool required){
cv::FileNode node = fSettings[name];
if(node.empty()){
if(required){
std::cerr << name << " required parameter does not exist, aborting..." << std::endl;
exit(-1);
}
else{
std::cerr << name << " optional parameter does not exist..." << std::endl;
found = false;
return cv::Mat();
}
}
else{
found = true;
return node.mat();
}
}
Settings::Settings(const std::string &configFile, const int& sensor) :
bNeedToUndistort_(false), bNeedToRectify_(false), bNeedToResize1_(false), bNeedToResize2_(false) {
sensor_ = sensor;
//Open settings file
cv::FileStorage fSettings(configFile, cv::FileStorage::READ);
if (!fSettings.isOpened()) {
cerr << "[ERROR]: could not open configuration file at: " << configFile << endl;
cerr << "Aborting..." << endl;
exit(-1);
}
else{
cout << "Loading settings from " << configFile << endl;
}
//Read first camera
readCamera1(fSettings);
cout << "\t-Loaded camera 1" << endl;
//Read second camera if stereo (not rectified)
if(sensor_ == System::STEREO || sensor_ == System::IMU_STEREO){
readCamera2(fSettings);
cout << "\t-Loaded camera 2" << endl;
}
//Read image info
readImageInfo(fSettings);
cout << "\t-Loaded image info" << endl;
if(sensor_ == System::IMU_MONOCULAR || sensor_ == System::IMU_STEREO || sensor_ == System::IMU_RGBD){
readIMU(fSettings);
cout << "\t-Loaded IMU calibration" << endl;
}
if(sensor_ == System::RGBD || sensor_ == System::IMU_RGBD){
readRGBD(fSettings);
cout << "\t-Loaded RGB-D calibration" << endl;
}
readORB(fSettings);
cout << "\t-Loaded ORB settings" << endl;
readViewer(fSettings);
cout << "\t-Loaded viewer settings" << endl;
readLoadAndSave(fSettings);
cout << "\t-Loaded Atlas settings" << endl;
readOtherParameters(fSettings);
cout << "\t-Loaded misc parameters" << endl;
if(bNeedToRectify_){
precomputeRectificationMaps();
cout << "\t-Computed rectification maps" << endl;
}
cout << "----------------------------------" << endl;
}
void Settings::readCamera1(cv::FileStorage &fSettings) {
bool found;
//Read camera model
string cameraModel = readParameter(fSettings,"Camera.type",found);
vector vCalibration;
if (cameraModel == "PinHole") {
cameraType_ = PinHole;
//Read intrinsic parameters
float fx = readParameter(fSettings,"Camera1.fx",found);
float fy = readParameter(fSettings,"Camera1.fy",found);
float cx = readParameter(fSettings,"Camera1.cx",found);
float cy = readParameter(fSettings,"Camera1.cy",found);
vCalibration = {fx, fy, cx, cy};
calibration1_ = new Pinhole(vCalibration);
originalCalib1_ = new Pinhole(vCalibration);
//Check if it is a distorted PinHole
readParameter(fSettings,"Camera1.k1",found,false);
if(found){
readParameter(fSettings,"Camera1.k3",found,false);
if(found){
vPinHoleDistorsion1_.resize(5);
vPinHoleDistorsion1_[4] = readParameter(fSettings,"Camera1.k3",found);
}
else{
vPinHoleDistorsion1_.resize(4);
}
vPinHoleDistorsion1_[0] = readParameter(fSettings,"Camera1.k1",found);
vPinHoleDistorsion1_[1] = readParameter(fSettings,"Camera1.k2",found);
vPinHoleDistorsion1_[2] = readParameter(fSettings,"Camera1.p1",found);
vPinHoleDistorsion1_[3] = readParameter(fSettings,"Camera1.p2",found);
}
//Check if we need to correct distortion from the images
if((sensor_ == System::MONOCULAR || sensor_ == System::IMU_MONOCULAR) && vPinHoleDistorsion1_.size() != 0){
bNeedToUndistort_ = true;
}
}
else if(cameraModel == "Rectified"){
cameraType_ = Rectified;
//Read intrinsic parameters
float fx = readParameter(fSettings,"Camera1.fx",found);
float fy = readParameter(fSettings,"Camera1.fy",found);
float cx = readParameter(fSettings,"Camera1.cx",found);
float cy = readParameter(fSettings,"Camera1.cy",found);
vCalibration = {fx, fy, cx, cy};
calibration1_ = new Pinhole(vCalibration);
originalCalib1_ = new Pinhole(vCalibration);
//Rectified images are assumed to be ideal PinHole images (no distortion)
}
else if(cameraModel == "KannalaBrandt8"){
cameraType_ = KannalaBrandt;
//Read intrinsic parameters
float fx = readParameter(fSettings,"Camera1.fx",found);
float fy = readParameter(fSettings,"Camera1.fy",found);
float cx = readParameter(fSettings,"Camera1.cx",found);
float cy = readParameter(fSettings,"Camera1.cy",found);
float k0 = readParameter(fSettings,"Camera1.k1",found);
float k1 = readParameter(fSettings,"Camera1.k2",found);
float k2 = readParameter(fSettings,"Camera1.k3",found);
float k3 = readParameter(fSettings,"Camera1.k4",found);
vCalibration = {fx,fy,cx,cy,k0,k1,k2,k3};
calibration1_ = new KannalaBrandt8(vCalibration);
originalCalib1_ = new KannalaBrandt8(vCalibration);
if(sensor_ == System::STEREO || sensor_ == System::IMU_STEREO){
int colBegin = readParameter(fSettings,"Camera1.overlappingBegin",found);
int colEnd = readParameter(fSettings,"Camera1.overlappingEnd",found);
vector vOverlapping = {colBegin, colEnd};
static_cast(calibration1_)->mvLappingArea = vOverlapping;
}
}
else{
cerr << "Error: " << cameraModel << " not known" << endl;
exit(-1);
}
}
void Settings::readCamera2(cv::FileStorage &fSettings) {
bool found;
vector vCalibration;
if (cameraType_ == PinHole) {
bNeedToRectify_ = true;
//Read intrinsic parameters
float fx = readParameter(fSettings,"Camera2.fx",found);
float fy = readParameter(fSettings,"Camera2.fy",found);
float cx = readParameter(fSettings,"Camera2.cx",found);
float cy = readParameter(fSettings,"Camera2.cy",found);
vCalibration = {fx, fy, cx, cy};
calibration2_ = new Pinhole(vCalibration);
originalCalib2_ = new Pinhole(vCalibration);
//Check if it is a distorted PinHole
readParameter(fSettings,"Camera2.k1",found,false);
if(found){
readParameter(fSettings,"Camera2.k3",found,false);
if(found){
vPinHoleDistorsion2_.resize(5);
vPinHoleDistorsion2_[4] = readParameter(fSettings,"Camera2.k3",found);
}
else{
vPinHoleDistorsion2_.resize(4);
}
vPinHoleDistorsion2_[0] = readParameter(fSettings,"Camera2.k1",found);
vPinHoleDistorsion2_[1] = readParameter(fSettings,"Camera2.k2",found);
vPinHoleDistorsion2_[2] = readParameter(fSettings,"Camera2.p1",found);
vPinHoleDistorsion2_[3] = readParameter(fSettings,"Camera2.p2",found);
}
}
else if(cameraType_ == KannalaBrandt){
//Read intrinsic parameters
float fx = readParameter(fSettings,"Camera2.fx",found);
float fy = readParameter(fSettings,"Camera2.fy",found);
float cx = readParameter(fSettings,"Camera2.cx",found);
float cy = readParameter(fSettings,"Camera2.cy",found);
float k0 = readParameter(fSettings,"Camera1.k1",found);
float k1 = readParameter(fSettings,"Camera1.k2",found);
float k2 = readParameter(fSettings,"Camera1.k3",found);
float k3 = readParameter(fSettings,"Camera1.k4",found);
vCalibration = {fx,fy,cx,cy,k0,k1,k2,k3};
calibration2_ = new KannalaBrandt8(vCalibration);
originalCalib2_ = new KannalaBrandt8(vCalibration);
int colBegin = readParameter(fSettings,"Camera2.overlappingBegin",found);
int colEnd = readParameter(fSettings,"Camera2.overlappingEnd",found);
vector vOverlapping = {colBegin, colEnd};
static_cast(calibration2_)->mvLappingArea = vOverlapping;
}
//Load stereo extrinsic calibration
if(cameraType_ == Rectified){
b_ = readParameter(fSettings,"Stereo.b",found);
bf_ = b_ * calibration1_->getParameter(0);
}
else{
cv::Mat cvTlr = readParameter(fSettings,"Stereo.T_c1_c2",found);
Tlr_ = Converter::toSophus(cvTlr);
//TODO: also search for Trl and invert if necessary
b_ = Tlr_.translation().norm();
bf_ = b_ * calibration1_->getParameter(0);
}
thDepth_ = readParameter(fSettings,"Stereo.ThDepth",found);
}
void Settings::readImageInfo(cv::FileStorage &fSettings) {
bool found;
//Read original and desired image dimensions
int originalRows = readParameter(fSettings,"Camera.height",found);
int originalCols = readParameter(fSettings,"Camera.width",found);
originalImSize_.width = originalCols;
originalImSize_.height = originalRows;
newImSize_ = originalImSize_;
int newHeigh = readParameter(fSettings,"Camera.newHeight",found,false);
if(found){
bNeedToResize1_ = true;
newImSize_.height = newHeigh;
if(!bNeedToRectify_){
//Update calibration
float scaleRowFactor = (float)newImSize_.height / (float)originalImSize_.height;
calibration1_->setParameter(calibration1_->getParameter(1) * scaleRowFactor, 1);
calibration1_->setParameter(calibration1_->getParameter(3) * scaleRowFactor, 3);
if((sensor_ == System::STEREO || sensor_ == System::IMU_STEREO) && cameraType_ != Rectified){
calibration2_->setParameter(calibration2_->getParameter(1) * scaleRowFactor, 1);
calibration2_->setParameter(calibration2_->getParameter(3) * scaleRowFactor, 3);
}
}
}
int newWidth = readParameter(fSettings,"Camera.newWidth",found,false);
if(found){
bNeedToResize1_ = true;
newImSize_.width = newWidth;
if(!bNeedToRectify_){
//Update calibration
float scaleColFactor = (float)newImSize_.width /(float) originalImSize_.width;
calibration1_->setParameter(calibration1_->getParameter(0) * scaleColFactor, 0);
calibration1_->setParameter(calibration1_->getParameter(2) * scaleColFactor, 2);
if((sensor_ == System::STEREO || sensor_ == System::IMU_STEREO) && cameraType_ != Rectified){
calibration2_->setParameter(calibration2_->getParameter(0) * scaleColFactor, 0);
calibration2_->setParameter(calibration2_->getParameter(2) * scaleColFactor, 2);
if(cameraType_ == KannalaBrandt){
static_cast(calibration1_)->mvLappingArea[0] *= scaleColFactor;
static_cast(calibration1_)->mvLappingArea[1] *= scaleColFactor;
static_cast(calibration2_)->mvLappingArea[0] *= scaleColFactor;
static_cast(calibration2_)->mvLappingArea[1] *= scaleColFactor;
}
}
}
}
fps_ = readParameter(fSettings,"Camera.fps",found);
bRGB_ = (bool) readParameter(fSettings,"Camera.RGB",found);
}
void Settings::readIMU(cv::FileStorage &fSettings) {
bool found;
noiseGyro_ = readParameter(fSettings,"IMU.NoiseGyro",found);
noiseAcc_ = readParameter(fSettings,"IMU.NoiseAcc",found);
gyroWalk_ = readParameter(fSettings,"IMU.GyroWalk",found);
accWalk_ = readParameter(fSettings,"IMU.AccWalk",found);
imuFrequency_ = readParameter(fSettings,"IMU.Frequency",found);
cv::Mat cvTbc = readParameter(fSettings,"IMU.T_b_c1",found);
Tbc_ = Converter::toSophus(cvTbc);
readParameter(fSettings,"IMU.InsertKFsWhenLost",found,false);
if(found){
insertKFsWhenLost_ = (bool) readParameter(fSettings,"IMU.InsertKFsWhenLost",found,false);
}
else{
insertKFsWhenLost_ = true;
}
}
void Settings::readRGBD(cv::FileStorage& fSettings) {
bool found;
depthMapFactor_ = readParameter(fSettings,"RGBD.DepthMapFactor",found);
thDepth_ = readParameter(fSettings,"Stereo.ThDepth",found);
b_ = readParameter(fSettings,"Stereo.b",found);
bf_ = b_ * calibration1_->getParameter(0);
}
void Settings::readORB(cv::FileStorage &fSettings) {
bool found;
nFeatures_ = readParameter(fSettings,"ORBextractor.nFeatures",found);
scaleFactor_ = readParameter(fSettings,"ORBextractor.scaleFactor",found);
nLevels_ = readParameter(fSettings,"ORBextractor.nLevels",found);
initThFAST_ = readParameter(fSettings,"ORBextractor.iniThFAST",found);
minThFAST_ = readParameter(fSettings,"ORBextractor.minThFAST",found);
}
void Settings::readViewer(cv::FileStorage &fSettings) {
bool found;
keyFrameSize_ = readParameter(fSettings,"Viewer.KeyFrameSize",found);
keyFrameLineWidth_ = readParameter(fSettings,"Viewer.KeyFrameLineWidth",found);
graphLineWidth_ = readParameter(fSettings,"Viewer.GraphLineWidth",found);
pointSize_ = readParameter(fSettings,"Viewer.PointSize",found);
cameraSize_ = readParameter(fSettings,"Viewer.CameraSize",found);
cameraLineWidth_ = readParameter(fSettings,"Viewer.CameraLineWidth",found);
viewPointX_ = readParameter(fSettings,"Viewer.ViewpointX",found);
viewPointY_ = readParameter(fSettings,"Viewer.ViewpointY",found);
viewPointZ_ = readParameter(fSettings,"Viewer.ViewpointZ",found);
viewPointF_ = readParameter(fSettings,"Viewer.ViewpointF",found);
imageViewerScale_ = readParameter(fSettings,"Viewer.imageViewScale",found,false);
if(!found)
imageViewerScale_ = 1.0f;
}
void Settings::readLoadAndSave(cv::FileStorage &fSettings) {
bool found;
sLoadFrom_ = readParameter(fSettings,"System.LoadAtlasFromFile",found,false);
sSaveto_ = readParameter(fSettings,"System.SaveAtlasToFile",found,false);
}
void Settings::readOtherParameters(cv::FileStorage& fSettings) {
bool found;
thFarPoints_ = readParameter(fSettings,"System.thFarPoints",found,false);
}
void Settings::precomputeRectificationMaps() {
//Precompute rectification maps, new calibrations, ...
cv::Mat K1 = static_cast(calibration1_)->toK();
K1.convertTo(K1,CV_64F);
cv::Mat K2 = static_cast(calibration2_)->toK();
K2.convertTo(K2,CV_64F);
cv::Mat cvTlr;
cv::eigen2cv(Tlr_.inverse().matrix3x4(),cvTlr);
cv::Mat R12 = cvTlr.rowRange(0,3).colRange(0,3);
R12.convertTo(R12,CV_64F);
cv::Mat t12 = cvTlr.rowRange(0,3).col(3);
t12.convertTo(t12,CV_64F);
cv::Mat R_r1_u1, R_r2_u2;
cv::Mat P1, P2, Q;
cv::stereoRectify(K1,camera1DistortionCoef(),K2,camera2DistortionCoef(),newImSize_,
R12, t12,
R_r1_u1,R_r2_u2,P1,P2,Q,
cv::CALIB_ZERO_DISPARITY,-1,newImSize_);
cv::initUndistortRectifyMap(K1, camera1DistortionCoef(), R_r1_u1, P1.rowRange(0, 3).colRange(0, 3),
newImSize_, CV_32F, M1l_, M2l_);
cv::initUndistortRectifyMap(K2, camera2DistortionCoef(), R_r2_u2, P2.rowRange(0, 3).colRange(0, 3),
newImSize_, CV_32F, M1r_, M2r_);
//Update calibration
calibration1_->setParameter(P1.at(0,0), 0);
calibration1_->setParameter(P1.at(1,1), 1);
calibration1_->setParameter(P1.at(0,2), 2);
calibration1_->setParameter(P1.at(1,2), 3);
//Update bf
bf_ = b_ * P1.at(0,0);
//Update relative pose between camera 1 and IMU if necessary
if(sensor_ == System::IMU_STEREO){
Eigen::Matrix3f eigenR_r1_u1;
cv::cv2eigen(R_r1_u1,eigenR_r1_u1);
Sophus::SE3f T_r1_u1(eigenR_r1_u1,Eigen::Vector3f::Zero());
Tbc_ = Tbc_ * T_r1_u1.inverse();
}
}
ostream &operator<<(std::ostream& output, const Settings& settings){
output << "SLAM settings: " << endl;
output << "\t-Camera 1 parameters (";
if(settings.cameraType_ == Settings::PinHole || settings.cameraType_ == Settings::Rectified){
output << "Pinhole";
}
else{
output << "Kannala-Brandt";
}
output << ")" << ": [";
for(size_t i = 0; i < settings.originalCalib1_->size(); i++){
output << " " << settings.originalCalib1_->getParameter(i);
}
output << " ]" << endl;
if(!settings.vPinHoleDistorsion1_.empty()){
output << "\t-Camera 1 distortion parameters: [ ";
for(float d : settings.vPinHoleDistorsion1_){
output << " " << d;
}
output << " ]" << endl;
}
if(settings.sensor_ == System::STEREO || settings.sensor_ == System::IMU_STEREO){
output << "\t-Camera 2 parameters (";
if(settings.cameraType_ == Settings::PinHole || settings.cameraType_ == Settings::Rectified){
output << "Pinhole";
}
else{
output << "Kannala-Brandt";
}
output << "" << ": [";
for(size_t i = 0; i < settings.originalCalib2_->size(); i++){
output << " " << settings.originalCalib2_->getParameter(i);
}
output << " ]" << endl;
if(!settings.vPinHoleDistorsion2_.empty()){
output << "\t-Camera 1 distortion parameters: [ ";
for(float d : settings.vPinHoleDistorsion2_){
output << " " << d;
}
output << " ]" << endl;
}
}
output << "\t-Original image size: [ " << settings.originalImSize_.width << " , " << settings.originalImSize_.height << " ]" << endl;
output << "\t-Current image size: [ " << settings.newImSize_.width << " , " << settings.newImSize_.height << " ]" << endl;
if(settings.bNeedToRectify_){
output << "\t-Camera 1 parameters after rectification: [ ";
for(size_t i = 0; i < settings.calibration1_->size(); i++){
output << " " << settings.calibration1_->getParameter(i);
}
output << " ]" << endl;
}
else if(settings.bNeedToResize1_){
output << "\t-Camera 1 parameters after resize: [ ";
for(size_t i = 0; i < settings.calibration1_->size(); i++){
output << " " << settings.calibration1_->getParameter(i);
}
output << " ]" << endl;
if((settings.sensor_ == System::STEREO || settings.sensor_ == System::IMU_STEREO) &&
settings.cameraType_ == Settings::KannalaBrandt){
output << "\t-Camera 2 parameters after resize: [ ";
for(size_t i = 0; i < settings.calibration2_->size(); i++){
output << " " << settings.calibration2_->getParameter(i);
}
output << " ]" << endl;
}
}
output << "\t-Sequence FPS: " << settings.fps_ << endl;
//Stereo stuff
if(settings.sensor_ == System::STEREO || settings.sensor_ == System::IMU_STEREO){
output << "\t-Stereo baseline: " << settings.b_ << endl;
output << "\t-Stereo depth threshold : " << settings.thDepth_ << endl;
if(settings.cameraType_ == Settings::KannalaBrandt){
auto vOverlapping1 = static_cast(settings.calibration1_)->mvLappingArea;
auto vOverlapping2 = static_cast(settings.calibration2_)->mvLappingArea;
output << "\t-Camera 1 overlapping area: [ " << vOverlapping1[0] << " , " << vOverlapping1[1] << " ]" << endl;
output << "\t-Camera 2 overlapping area: [ " << vOverlapping2[0] << " , " << vOverlapping2[1] << " ]" << endl;
}
}
if(settings.sensor_ == System::IMU_MONOCULAR || settings.sensor_ == System::IMU_STEREO || settings.sensor_ == System::IMU_RGBD) {
output << "\t-Gyro noise: " << settings.noiseGyro_ << endl;
output << "\t-Accelerometer noise: " << settings.noiseAcc_ << endl;
output << "\t-Gyro walk: " << settings.gyroWalk_ << endl;
output << "\t-Accelerometer walk: " << settings.accWalk_ << endl;
output << "\t-IMU frequency: " << settings.imuFrequency_ << endl;
}
if(settings.sensor_ == System::RGBD || settings.sensor_ == System::IMU_RGBD){
output << "\t-RGB-D depth map factor: " << settings.depthMapFactor_ << endl;
}
output << "\t-Features per image: " << settings.nFeatures_ << endl;
output << "\t-ORB scale factor: " << settings.scaleFactor_ << endl;
output << "\t-ORB number of scales: " << settings.nLevels_ << endl;
output << "\t-Initial FAST threshold: " << settings.initThFAST_ << endl;
output << "\t-Min FAST threshold: " << settings.minThFAST_ << endl;
return output;
}
};