Skip to content
Snippets Groups Projects
calibrate.cpp 6.1 KiB
Newer Older
Nicolas Pope's avatar
Nicolas Pope committed
/*
 * Copyright 2019 Nicolas Pope
 */

Nicolas Pope's avatar
Nicolas Pope committed
#include <loguru.hpp>
Nicolas Pope's avatar
Nicolas Pope committed
#include <ftl/config.h>
#include <ftl/configuration.hpp>
Sebastian Hahta's avatar
Sebastian Hahta committed
#include <ftl/threads.hpp>
Nicolas Pope's avatar
Nicolas Pope committed

#include <iostream>
#include <sstream>
#include <string>
#include <ctime>

#include "calibrate.hpp"

#include <opencv2/core.hpp>
#include <opencv2/core/utility.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/calib3d.hpp>

using ftl::rgbd::detail::Calibrate;
Nicolas Pope's avatar
Nicolas Pope committed
using cv::FileStorage;
Nicolas Pope's avatar
Nicolas Pope committed
using cv::INTER_LINEAR;
Nicolas Pope's avatar
Nicolas Pope committed
using cv::FileNode;
using cv::FileNodeIterator;
Nicolas Pope's avatar
Nicolas Pope committed
using cv::Mat;
using cv::cuda::GpuMat;
using cv::cuda::Stream;

Nicolas Pope's avatar
Nicolas Pope committed
using cv::Size;
Nicolas Pope's avatar
Nicolas Pope committed
using cv::Point2f;
using cv::Point3f;
using cv::Matx33d;
using cv::Scalar;
Nicolas Pope's avatar
Nicolas Pope committed
using std::string;
using std::vector;

Calibrate::Calibrate(nlohmann::json &config, cv::Size image_size, cv::cuda::Stream &stream) : ftl::Configurable(config) {
	std::pair<Mat, Mat> map1, map2;
	calibrated_ = _loadCalibration(image_size, map1, map2);
	if (calibrated_) {
Sebastian Hahta's avatar
Sebastian Hahta committed
		_updateIntrinsics();
		LOG(INFO) << "Calibration loaded from file";
	}
	else {
		LOG(WARNING) << "Calibration not loaded";
	}
Sebastian Hahta's avatar
Sebastian Hahta committed
	this->on("use_intrinsics", [this](const ftl::config::Event &e) {
		_updateIntrinsics();
	});
}

bool Calibrate::_loadCalibration(cv::Size img_size, std::pair<Mat, Mat> &map1, std::pair<Mat, Mat> &map2) {
	FileStorage fs;
	// reading intrinsic parameters
	auto ifile = ftl::locateFile(value("intrinsics", std::string("intrinsics.yml")));
	if (ifile) {
Nicolas Pope's avatar
Nicolas Pope committed
		fs.open((*ifile).c_str(), FileStorage::READ);
		if (!fs.isOpened()) {
			LOG(WARNING) << "Could not open intrinsics file";
			return false;
Nicolas Pope's avatar
Nicolas Pope committed
		LOG(INFO) << "Intrinsics from: " << *ifile;
	}
	else {
		LOG(WARNING) << "Calibration intrinsics file not found";
Nicolas Pope's avatar
Nicolas Pope committed
		return false;
Sebastian Hahta's avatar
Sebastian Hahta committed

	cv::Size calib_size;
	{
		vector<Mat> K, D;
		fs["K"] >> K;
		fs["D"] >> D;
Sebastian Hahta's avatar
Sebastian Hahta committed
		fs["resolution"] >> calib_size;
Sebastian Hahta's avatar
Sebastian Hahta committed
		K[0].copyTo(K1_);
		K[1].copyTo(K2_);
		D[0].copyTo(D1_);
		D[1].copyTo(D2_);
	}
Sebastian Hahta's avatar
Sebastian Hahta committed
	fs.release();

Sebastian Hahta's avatar
Sebastian Hahta committed
	CHECK(K1_.size() == Size(3, 3));
	CHECK(K2_.size() == Size(3, 3));
	CHECK(D1_.size() == Size(5, 1));
	CHECK(D2_.size() == Size(5, 1));
	auto efile = ftl::locateFile(value("extrinsics", std::string("extrinsics.yml")));
Nicolas Pope's avatar
Nicolas Pope committed
	if (efile) {
		fs.open((*efile).c_str(), FileStorage::READ);
		if (!fs.isOpened()) {
			LOG(WARNING) << "Could not open extrinsics file";
			return false;
Nicolas Pope's avatar
Nicolas Pope committed
		LOG(INFO) << "Extrinsics from: " << *efile;
		LOG(WARNING) << "Calibration extrinsics file not found";
		return false;
Nicolas Pope's avatar
Nicolas Pope committed
	fs["R"] >> R_;
	fs["T"] >> T_;
Sebastian Hahta's avatar
Sebastian Hahta committed
	
	/* re-calculate rectification from camera parameters
Nicolas Pope's avatar
Nicolas Pope committed
	fs["R1"] >> R1_;
	fs["R2"] >> R2_;
	fs["P1"] >> P1_;
	fs["P2"] >> P2_;
	fs["Q"] >> Q_;
Sebastian Hahta's avatar
Sebastian Hahta committed
	*/
Sebastian Hahta's avatar
Sebastian Hahta committed
	fs.release();

Nicolas Pope's avatar
Nicolas Pope committed
	img_size_ = img_size;
Sebastian Hahta's avatar
Sebastian Hahta committed
	if (calib_size.empty())
	{
		LOG(WARNING) << "Calibration resolution missing!";
	}
	else
	{
		double scale_x = ((double) img_size.width) / ((double) calib_size.width);
		double scale_y = ((double) img_size.height) / ((double) calib_size.height);
Sebastian Hahta's avatar
Sebastian Hahta committed
		Mat scale(cv::Size(3, 3), CV_64F, 0.0);
		scale.at<double>(0, 0) = scale_x;
		scale.at<double>(1, 1) = scale_y;
		scale.at<double>(2, 2) = 1.0;

		K1_ = scale * K1_;
		K2_ = scale * K2_;
	}
Sebastian Hahta's avatar
Sebastian Hahta committed
	double alpha = value("alpha", 0.0);
	cv::stereoRectify(K1_, D1_, K2_, D2_, img_size_, R_, T_, R1_, R2_, P1_, P2_, Q_, 0, alpha);
Sebastian Hahta's avatar
Sebastian Hahta committed
	/* scaling not required as rectification is performed from scaled values
Sebastian Hahta's avatar
Sebastian Hahta committed
	Q_.at<double>(0, 3) = Q_.at<double>(0, 3) * scale_x;
	Q_.at<double>(1, 3) = Q_.at<double>(1, 3) * scale_y;
	Q_.at<double>(2, 3) = Q_.at<double>(2, 3) * scale_x; // TODO: scaling?
	Q_.at<double>(3, 3) = Q_.at<double>(3, 3) * scale_x;
Sebastian Hahta's avatar
Sebastian Hahta committed
	*/
	// cv::cuda::remap() works only with CV_32FC1
Sebastian Hahta's avatar
Sebastian Hahta committed
	initUndistortRectifyMap(K1_, D1_, R1_, P1_, img_size_, CV_32FC1, map1.first, map2.first);
	initUndistortRectifyMap(K2_, D2_, R2_, P2_, img_size_, CV_32FC1, map1.second, map2.second);
Nicolas Pope's avatar
Nicolas Pope committed
void Calibrate::updateCalibration(const ftl::rgbd::Camera &p) {
	Q_.at<double>(3, 2) = 1.0 / p.baseline;
	Q_.at<double>(2, 3) = p.fx;
	Q_.at<double>(0, 3) = p.cx;
	Q_.at<double>(1, 3) = p.cy;
Nicolas Pope's avatar
Nicolas Pope committed
	// FIXME:(Nick) Update camera matrix also...
Sebastian Hahta's avatar
Sebastian Hahta committed
	_updateIntrinsics();
}
Sebastian Hahta's avatar
Sebastian Hahta committed
void Calibrate::_updateIntrinsics() {
	// TODO: pass parameters?
Sebastian Hahta's avatar
Sebastian Hahta committed
	Mat R1, R2, P1, P2;
	ftl::rgbd::Camera params();

	if (this->value("use_intrinsics", true)) {
		// rectify
		R1 = R1_;
		R2 = R2_;
		P1 = P1_;
		P2 = P2_;
	}
	else {
		// no rectification
		R1 = Mat::eye(Size(3, 3), CV_64FC1);
		R2 = R1;
Sebastian Hahta's avatar
Sebastian Hahta committed
		P1 = Mat::zeros(Size(4, 3), CV_64FC1);
		P2 = Mat::zeros(Size(4, 3), CV_64FC1);
		K1_.copyTo(Mat(P1, cv::Rect(0, 0, 3, 3)));
		K2_.copyTo(Mat(P2, cv::Rect(0, 0, 3, 3)));
Sebastian Hahta's avatar
Sebastian Hahta committed
	}

	// Set correct camera matrices for
	// getCameraMatrix(), getCameraMatrixLeft(), getCameraMatrixRight()
Sebastian Hahta's avatar
Sebastian Hahta committed
	Kl_ = Mat(P1, cv::Rect(0, 0, 3, 3));
	Kr_ = Mat(P2, cv::Rect(0, 0, 3, 3));
Sebastian Hahta's avatar
Sebastian Hahta committed
	initUndistortRectifyMap(K1_, D1_, R1, P1, img_size_, CV_32FC1, map1_.first, map2_.first);
	initUndistortRectifyMap(K2_, D2_, R2, P2, img_size_, CV_32FC1, map1_.second, map2_.second);
Nicolas Pope's avatar
Nicolas Pope committed
	// CHECK Is this thread safe!!!!
Nicolas Pope's avatar
Nicolas Pope committed
	map1_gpu_.first.upload(map1_.first);
	map1_gpu_.second.upload(map1_.second);
	map2_gpu_.first.upload(map2_.first);
	map2_gpu_.second.upload(map2_.second);
void Calibrate::rectifyStereo(GpuMat &l, GpuMat &r, Stream &stream) {
	// cv::cuda::remap() can not use same Mat for input and output
	GpuMat l_tmp(l.size(), l.type());
	GpuMat r_tmp(r.size(), r.type());
	cv::cuda::remap(l, l_tmp, map1_gpu_.first, map2_gpu_.first, cv::INTER_LINEAR, 0, cv::Scalar(), stream);
	cv::cuda::remap(r, r_tmp, map1_gpu_.second, map2_gpu_.second, cv::INTER_LINEAR, 0, cv::Scalar(), stream);
	stream.waitForCompletion();
	l = l_tmp;
	r = r_tmp;
Nicolas Pope's avatar
Nicolas Pope committed
void Calibrate::rectifyStereo(cv::Mat &l, cv::Mat &r) {
	// cv::cuda::remap() can not use same Mat for input and output

	cv::remap(l, l, map1_.first, map2_.first, cv::INTER_LINEAR, 0, cv::Scalar());
	cv::remap(r, r, map1_.second, map2_.second, cv::INTER_LINEAR, 0, cv::Scalar());

	/*GpuMat l_tmp(l.size(), l.type());
	GpuMat r_tmp(r.size(), r.type());
	cv::cuda::remap(l, l_tmp, map1_gpu_.first, map2_gpu_.first, cv::INTER_LINEAR, 0, cv::Scalar(), stream);
	cv::cuda::remap(r, r_tmp, map1_gpu_.second, map2_gpu_.second, cv::INTER_LINEAR, 0, cv::Scalar(), stream);
	stream.waitForCompletion();
	l = l_tmp;
	r = r_tmp;*/
}

Nicolas Pope's avatar
Nicolas Pope committed
bool Calibrate::isCalibrated() {
	return calibrated_;