Skip to content
Snippets Groups Projects
stereovideo.cpp 8.33 KiB
Newer Older
Nicolas Pope's avatar
Nicolas Pope committed
#include <loguru.hpp>
#include "stereovideo.hpp"
Nicolas Pope's avatar
Nicolas Pope committed
#include <ftl/configuration.hpp>
Sebastian Hahta's avatar
Sebastian Hahta committed
#include <ftl/operators/opticalflow.hpp>

Nicolas Pope's avatar
Nicolas Pope committed
#include <ftl/threads.hpp>
Nicolas Pope's avatar
Nicolas Pope committed
#include "calibrate.hpp"
#include "local.hpp"
#include "disparity.hpp"
#include "cuda_algorithms.hpp"
Nicolas Pope's avatar
Nicolas Pope committed

#include "cuda_algorithms.hpp"

using ftl::rgbd::detail::Calibrate;
using ftl::rgbd::detail::LocalSource;
using ftl::rgbd::detail::StereoVideoSource;
using ftl::codecs::Channel;
Nicolas Pope's avatar
Nicolas Pope committed
using std::string;

StereoVideoSource::StereoVideoSource(ftl::rgbd::Source *host)
		: ftl::rgbd::detail::Source(host), ready_(false) {
Nicolas Pope's avatar
Nicolas Pope committed
	init("");
StereoVideoSource::StereoVideoSource(ftl::rgbd::Source *host, const string &file)
		: ftl::rgbd::detail::Source(host), ready_(false) {
Nicolas Pope's avatar
Nicolas Pope committed

Nicolas Pope's avatar
Nicolas Pope committed
	init(file);
}

StereoVideoSource::~StereoVideoSource() {
	delete disp_;
	delete calib_;
	delete lsrc_;
}

void StereoVideoSource::init(const string &file)
{
	capabilities_ = kCapVideo | kCapStereo;

Nicolas Pope's avatar
Nicolas Pope committed
	if (ftl::is_video(file)) {
		// Load video file
		LOG(INFO) << "Using video file...";
		lsrc_ = ftl::create<LocalSource>(host_, "feed", file);
	} else if (ftl::is_directory(file)) {
		// FIXME: This is not an ideal solution...
		ftl::config::addPath(file);

Nicolas Pope's avatar
Nicolas Pope committed
		auto vid = ftl::locateFile("video.mp4");
		if (!vid) {
Nicolas Pope's avatar
Nicolas Pope committed
			LOG(FATAL) << "No video.mp4 file found in provided paths (" << file << ")";
Nicolas Pope's avatar
Nicolas Pope committed
		} else {
			LOG(INFO) << "Using test directory...";
			lsrc_ = ftl::create<LocalSource>(host_, "feed", *vid);
Nicolas Pope's avatar
Nicolas Pope committed
		}
Nicolas Pope's avatar
Nicolas Pope committed
		// Use cameras
		LOG(INFO) << "Using cameras...";
		lsrc_ = ftl::create<LocalSource>(host_, "feed");
Sebastian Hahta's avatar
Sebastian Hahta committed
	// Create the source depth map pipeline
	pipeline_ = ftl::config::create<ftl::operators::Graph>(host_, "disparity");
	/*pipeline1->append<ftl::operators::ColourChannels>("colour");  // Convert BGR to BGRA
	pipeline1->append<ftl::operators::HFSmoother>("hfnoise");  // Remove high-frequency noise
	pipeline1->append<ftl::operators::Normals>("normals");  // Estimate surface normals
	pipeline1->append<ftl::operators::SmoothChannel>("smoothing");  // Generate a smoothing channel
	//pipeline1->append<ftl::operators::ScanFieldFill>("filling");  // Generate a smoothing channel
	pipeline1->append<ftl::operators::ColourMLS>("mls");  // Perform MLS (using smoothing channel)
	*/

	cv::Size size = cv::Size(lsrc_->width(), lsrc_->height());
	frames_ = std::vector<Frame>(2);

#ifdef HAVE_OPTFLOW
	use_optflow_ =  host_->value("use_optflow", false);
	LOG(INFO) << "Using optical flow: " << (use_optflow_ ? "true" : "false");
Sebastian Hahta's avatar
Sebastian Hahta committed
	pipeline_->append<ftl::operators::NVOpticalFlow>("optflow");
	calib_ = ftl::create<Calibrate>(host_, "calibration", size, stream_);
Nicolas Pope's avatar
Nicolas Pope committed
	if (!calib_->isCalibrated()) LOG(WARNING) << "Cameras are not calibrated!";

	// Generate camera parameters from camera matrix
	cv::Mat K = calib_->getCameraMatrix();
Nicolas Pope's avatar
Nicolas Pope committed
	params_ = {
		K.at<double>(0,0),	// Fx
		K.at<double>(1,1),	// Fy
		-K.at<double>(0,2),	// Cx
		-K.at<double>(1,2),	// Cy
		(unsigned int) lsrc_->width(),
		(unsigned int) lsrc_->height(),
Nicolas Pope's avatar
Nicolas Pope committed
		0.0f,	// 0m min
Nicolas Pope's avatar
Nicolas Pope committed
		15.0f,	// 15m max
		1.0 / calib_->getQ().at<double>(3,2), // Baseline
		0.0f  // doffs
Nicolas Pope's avatar
Nicolas Pope committed
	};
	params_.doffs = -calib_->getQ().at<double>(3,3) * params_.baseline;
Nicolas Pope's avatar
Nicolas Pope committed

	// Add calibration to config object
	host_->getConfig()["focal"] = params_.fx;
	host_->getConfig()["centre_x"] = params_.cx;
	host_->getConfig()["centre_y"] = params_.cy;
	host_->getConfig()["baseline"] = params_.baseline;
	host_->getConfig()["doffs"] = params_.doffs;
Nicolas Pope's avatar
Nicolas Pope committed

	// Add event handlers to allow calibration changes...
	host_->on("baseline", [this](const ftl::config::Event &e) {
		params_.baseline = host_->value("baseline", params_.baseline);
Nicolas Pope's avatar
Nicolas Pope committed
		UNIQUE_LOCK(host_->mutex(), lk);
Nicolas Pope's avatar
Nicolas Pope committed
		calib_->updateCalibration(params_);
	});

	host_->on("focal", [this](const ftl::config::Event &e) {
		params_.fx = host_->value("focal", params_.fx);
		params_.fy = params_.fx;
Nicolas Pope's avatar
Nicolas Pope committed
		UNIQUE_LOCK(host_->mutex(), lk);
Nicolas Pope's avatar
Nicolas Pope committed
		calib_->updateCalibration(params_);
	});

	host_->on("doffs", [this](const ftl::config::Event &e) {
		params_.doffs = host_->value("doffs", params_.doffs);
	});
	
	// left and right masks (areas outside rectified images)
	// only left mask used
	cv::cuda::GpuMat mask_r_gpu(lsrc_->height(), lsrc_->width(), CV_8U, 255);
	cv::cuda::GpuMat mask_l_gpu(lsrc_->height(), lsrc_->width(), CV_8U, 255);
	
	calib_->rectifyStereo(mask_l_gpu, mask_r_gpu, stream_);
	stream_.waitForCompletion();

	cv::Mat mask_l;
	mask_l_gpu.download(mask_l);
	mask_l_ = (mask_l == 0);
	
	disp_ = Disparity::create(host_, "disparity");
	if (!disp_) LOG(FATAL) << "Unknown disparity algorithm : " << *host_->get<ftl::config::json_t>("disparity");
Sebastian Hahta's avatar
Sebastian Hahta committed
	disp_->setMask(mask_l_);
Nicolas Pope's avatar
Nicolas Pope committed

	LOG(INFO) << "StereoVideo source ready...";
	ready_ = true;
}

ftl::rgbd::Camera StereoVideoSource::parameters(Channel chan) {
	if (chan == Channel::Right) {
		cv::Mat q = calib_->getCameraMatrixRight();
		ftl::rgbd::Camera params = {
			q.at<double>(0,0),	// Fx
			q.at<double>(1,1),	// Fy
			-q.at<double>(0,2),	// Cx
			-q.at<double>(1,2),	// Cy
			(unsigned int)lsrc_->width(),
			(unsigned int)lsrc_->height(),
			0.0f,	// 0m min
			15.0f,	// 15m max
			1.0 / calib_->getQ().at<double>(3,2), // Baseline
			0.0f  // doffs
		};
		return params;
		//params_.doffs = -calib_->getQ().at<double>(3,3) * params_.baseline;
	} else {
		return params_;
	}
}

static void disparityToDepth(const cv::cuda::GpuMat &disparity, cv::cuda::GpuMat &depth,
							 const cv::Mat &Q, cv::cuda::Stream &stream) {
	// Q(3, 2) = -1/Tx
	// Q(2, 3) = f
Nicolas Pope's avatar
Nicolas Pope committed

	double val = (1.0f / Q.at<double>(3, 2)) * Q.at<double>(2, 3);
	cv::cuda::divide(val, disparity, depth, 1.0f / 1000.0f, -1, stream);
bool StereoVideoSource::capture(int64_t ts) {
	timestamp_ = ts;
	lsrc_->grab();
	return true;
}

bool StereoVideoSource::retrieve() {
	auto &frame = frames_[0];
	frame.reset();
	auto &left = frame.create<cv::cuda::GpuMat>(Channel::Left);
	auto &right = frame.create<cv::cuda::GpuMat>(Channel::Right);
	lsrc_->get(left, right, calib_, stream2_);
Sebastian Hahta's avatar
Sebastian Hahta committed
	pipeline_->apply(frame, frame, (ftl::rgbd::Source*) lsrc_, cv::cuda::StreamAccessor::getStream(stream2_));

#ifdef HAVE_OPTFLOW
	// see comments in https://gitlab.utu.fi/nicolas.pope/ftl/issues/155
	if (use_optflow_)
	{
		auto &left_gray = frame.create<cv::cuda::GpuMat>(Channel::LeftGray);
		auto &right_gray = frame.create<cv::cuda::GpuMat>(Channel::RightGray);

		cv::cuda::cvtColor(left, left_gray, cv::COLOR_BGR2GRAY, 0, stream2_);
		cv::cuda::cvtColor(right, right_gray, cv::COLOR_BGR2GRAY, 0, stream2_);

		if (frames_[1].hasChannel(Channel::LeftGray))
			//frames_[1].download(Channel::LeftGray);
			auto &left_gray_prev = frames_[1].get<cv::cuda::GpuMat>(Channel::LeftGray);
			auto &optflow = frame.create<cv::cuda::GpuMat>(Channel::Flow);
			nvof_->calc(left_gray, left_gray_prev, optflow, stream2_);
			// nvof_->upSampler() isn't implemented with CUDA
			// cv::cuda::resize() does not work wiht 2-channel input
			// cv::cuda::resize(optflow_, optflow, left.size(), 0.0, 0.0, cv::INTER_NEAREST, stream2_);
		}
	stream2_.waitForCompletion();
	return true;
}

void StereoVideoSource::swap() {
	auto tmp = std::move(frames_[0]);
	frames_[0] = std::move(frames_[1]);
	frames_[1] = std::move(tmp);
bool StereoVideoSource::compute(int n, int b) {
	auto &frame = frames_[1];
	auto &left = frame.get<cv::cuda::GpuMat>(Channel::Left);
	auto &right = frame.get<cv::cuda::GpuMat>(Channel::Right);
	const ftl::codecs::Channel chan = host_->getChannel();
	if (left.empty() || right.empty()) return false;
	if (chan == Channel::Depth) {
		disp_->compute(frame, stream_);
		
		auto &disp = frame.get<cv::cuda::GpuMat>(Channel::Disparity);
		auto &depth = frame.create<cv::cuda::GpuMat>(Channel::Depth);
		if (depth.empty()) depth = cv::cuda::GpuMat(left.size(), CV_32FC1);

		ftl::cuda::disparity_to_depth(disp, depth, params_, stream_);
		
		//left.download(rgb_, stream_);
		//depth.download(depth_, stream_);
		//frame.download(Channel::Left + Channel::Depth);
		stream_.waitForCompletion();
		host_->notify(timestamp_, left, depth);
	} else if (chan == Channel::Right) {
		//left.download(rgb_, stream_);
		//right.download(depth_, stream_);
Nicolas Pope's avatar
Nicolas Pope committed
		stream_.waitForCompletion();  // TODO:(Nick) Move to getFrames
		host_->notify(timestamp_, left, right);
		//left.download(rgb_, stream_);
Nicolas Pope's avatar
Nicolas Pope committed
		stream_.waitForCompletion();  // TODO:(Nick) Move to getFrames
		//LOG(INFO) << "NO SECOND CHANNEL: " << (bool)depth_.empty();
		depth_.create(left.size(), left.type());
		host_->notify(timestamp_, left, depth_);
	return true;
}

bool StereoVideoSource::isReady() {
	return ready_;
Nicolas Pope's avatar
Nicolas Pope committed
}