Skip to content
Snippets Groups Projects
camera.cpp 10.9 KiB
Newer Older
Nicolas Pope's avatar
Nicolas Pope committed
#include "camera.hpp"
#include "pose_window.hpp"
#include "screen.hpp"

#include <nanogui/glutil.h>

using ftl::rgbd::isValidDepth;
using ftl::gui::GLTexture;
using ftl::gui::PoseWindow;
using ftl::rgbd::Channel;
using ftl::rgbd::Channels;
Nicolas Pope's avatar
Nicolas Pope committed

// TODO(Nick) MOVE
class StatisticsImage {
private:
	cv::Mat data_;	// CV_32FC3, channels: m, s, f
	cv::Size size_;	// image size
	float n_;		// total number of samples
Nicolas Pope's avatar
Nicolas Pope committed

public:
	explicit StatisticsImage(cv::Size size);
	StatisticsImage(cv::Size size, float max_f);
	/* @brief reset all statistics to 0
	 */

	/* @brief update statistics with new values
	 */
Nicolas Pope's avatar
Nicolas Pope committed
	void update(const cv::Mat &in);
	
	/* @brief variance (depth)
	 */
	void getVariance(cv::Mat &out);

	/* @brief standard deviation (depth)
	 */
Nicolas Pope's avatar
Nicolas Pope committed
	void getStdDev(cv::Mat &out);
	
	/* @brief mean value (depth)
	 */
Nicolas Pope's avatar
Nicolas Pope committed
	void getMean(cv::Mat &out);

	/* @brief percent of samples having valid depth value
	 */
	void getValidRatio(cv::Mat &out);
StatisticsImage::StatisticsImage(cv::Size size) :
	StatisticsImage(size, std::numeric_limits<float>::infinity()) {}

StatisticsImage::StatisticsImage(cv::Size size, float max_f) {
	size_ = size;
	data_ = cv::Mat(size, CV_32FC3, cv::Scalar(0.0, 0.0, 0.0));
	if (!std::isinf(max_f)) {
		LOG(WARNING) << "TODO: max_f_ not used. Values calculated for all samples";
void StatisticsImage::reset() {
	data_ = cv::Scalar(0.0, 0.0, 0.0);
void StatisticsImage::update(const cv::Mat &in) {
Nicolas Pope's avatar
Nicolas Pope committed
	DCHECK(in.type() == CV_32F);
	DCHECK(in.size() == size_);
	for (int row = 0; row < in.rows; row++) {
		float* ptr_data = data_.ptr<float>(row);
		const float* ptr_in = in.ptr<float>(row);
		for (int col = 0; col < in.cols; col++, ptr_in++) {
			float x = *ptr_in;
			float &m = *ptr_data++;
			float &s = *ptr_data++;
			float &f = *ptr_data++;
Nicolas Pope's avatar
Nicolas Pope committed
			float m_prev = m;

			if (!ftl::rgbd::isValidDepth(x)) continue;
			f = f + 1.0f;
			m = m + (x - m) / f;
			s = s + (x - m) * (x - m_prev);
}

void StatisticsImage::getVariance(cv::Mat &out) {
	std::vector<cv::Mat> channels(3);
	cv::split(data_, channels);
	cv::divide(channels[1], channels[2], out);
}
void StatisticsImage::getStdDev(cv::Mat &out) {
	getVariance(out);
	cv::sqrt(out, out);
void StatisticsImage::getMean(cv::Mat &out) {
	std::vector<cv::Mat> channels(3);
	cv::split(data_, channels);
	out = channels[0];
}
void StatisticsImage::getValidRatio(cv::Mat &out) {
	std::vector<cv::Mat> channels(3);
	cv::split(data_, channels);
	cv::divide(channels[2], n_, out);
}

Nicolas Pope's avatar
Nicolas Pope committed
static Eigen::Affine3d create_rotation_matrix(float ax, float ay, float az) {
  Eigen::Affine3d rx =
      Eigen::Affine3d(Eigen::AngleAxisd(ax, Eigen::Vector3d(1, 0, 0)));
  Eigen::Affine3d ry =
      Eigen::Affine3d(Eigen::AngleAxisd(ay, Eigen::Vector3d(0, 1, 0)));
  Eigen::Affine3d rz =
      Eigen::Affine3d(Eigen::AngleAxisd(az, Eigen::Vector3d(0, 0, 1)));
  return rz * rx * ry;
}

ftl::gui::Camera::Camera(ftl::gui::Screen *screen, ftl::rgbd::Source *src) : screen_(screen), src_(src) {
	eye_ = Eigen::Vector3d(0.0f, 0.0f, 0.0f);
	neye_ = Eigen::Vector4d(0.0f, 0.0f, 0.0f, 0.0f);
	rotmat_.setIdentity();
	//up_ = Eigen::Vector3f(0,1.0f,0);
	lerpSpeed_ = 0.999f;
	sdepth_ = false;
Nicolas Pope's avatar
Nicolas Pope committed
	ftime_ = (float)glfwGetTime();
	pause_ = false;

	channel_ = Channel::Left;
	channels_ += Channel::Left;
	channels_ += Channel::Depth;
Nicolas Pope's avatar
Nicolas Pope committed

	// Create pose window...
	posewin_ = new PoseWindow(screen, src_->getURI());
	posewin_->setTheme(screen->windowtheme);
	posewin_->setVisible(false);

	src->setCallback([this](int64_t ts, cv::Mat &rgb, cv::Mat &depth) {
		UNIQUE_LOCK(mutex_, lk);
		rgb_.create(rgb.size(), rgb.type());
		depth_.create(depth.size(), depth.type());
		cv::swap(rgb_,rgb);
		cv::swap(depth_, depth);
Nicolas Pope's avatar
Nicolas Pope committed
		cv::flip(rgb_,rgb_,0);
		cv::flip(depth_,depth_,0);
Nicolas Pope's avatar
Nicolas Pope committed
}

ftl::gui::Camera::~Camera() {

}

ftl::rgbd::Source *ftl::gui::Camera::source() {
	return src_;
}

void ftl::gui::Camera::setPose(const Eigen::Matrix4d &p) {
	eye_[0] = p(0,3);
	eye_[1] = p(1,3);
	eye_[2] = p(2,3);

	double sx = Eigen::Vector3d(p(0,0), p(1,0), p(2,0)).norm();
	double sy = Eigen::Vector3d(p(0,1), p(1,1), p(2,1)).norm();
	double sz = Eigen::Vector3d(p(0,2), p(1,2), p(2,2)).norm();

	Eigen::Matrix4d rot = p;
	rot(0,3) = 0.0;
	rot(1,3) = 0.0;
	rot(2,3) = 0.0;
	rot(0,0) = rot(0,0) / sx;
	rot(1,0) = rot(1,0) / sx;
	rot(2,0) = rot(2,0) / sx;
	rot(0,1) = rot(0,1) / sy;
	rot(1,1) = rot(1,1) / sy;
	rot(2,1) = rot(2,1) / sy;
	rot(0,2) = rot(0,2) / sz;
	rot(1,2) = rot(1,2) / sz;
	rot(2,2) = rot(2,2) / sz;
	rotmat_ = rot;
}

void ftl::gui::Camera::mouseMovement(int rx, int ry, int button) {
	if (!src_->hasCapabilities(ftl::rgbd::kCapMovable)) return;
Nicolas Pope's avatar
Nicolas Pope committed
	if (button == 1) {
		float rrx = ((float)ry * 0.2f * delta_);
		//orientation_[2] += std::cos(orientation_[1])*((float)rel[1] * 0.2f * delta_);
		float rry = (float)rx * 0.2f * delta_;
		float rrz = 0.0;


		Eigen::Affine3d r = create_rotation_matrix(rrx, -rry, rrz);
		rotmat_ = rotmat_ * r.matrix();
	}
}

void ftl::gui::Camera::keyMovement(int key, int modifiers) {
	if (!src_->hasCapabilities(ftl::rgbd::kCapMovable)) return;
Nicolas Pope's avatar
Nicolas Pope committed
	if (key == 263 || key == 262) {
		float mag = (modifiers & 0x1) ? 0.01f : 0.1f;
		float scalar = (key == 263) ? -mag : mag;
		neye_ += rotmat_*Eigen::Vector4d(scalar,0.0,0.0,1.0);
		return;
	} else if (key == 264 || key == 265) {
		float mag = (modifiers & 0x1) ? 0.01f : 0.1f;
		float scalar = (key == 264) ? -mag : mag;
		neye_ += rotmat_*Eigen::Vector4d(0.0,0.0,scalar,1.0);
		return;
	} else if (key == 266 || key == 267) {
		float mag = (modifiers & 0x1) ? 0.01f : 0.1f;
		float scalar = (key == 266) ? -mag : mag;
		neye_ += rotmat_*Eigen::Vector4d(0.0,scalar,0.0,1.0);
		return;
	}
}

void ftl::gui::Camera::showPoseWindow() {
	posewin_->setVisible(true);
}

void ftl::gui::Camera::showSettings() {

}

void ftl::gui::Camera::setChannel(Channel c) {
	channel_ = c;
	switch (c) {
	case Channel::Energy:
	case Channel::Flow:
	case Channel::Confidence:
	case Channel::Normals:
	case Channel::Right:
	case Channel::Deviation:
		if (stats_) { stats_->reset(); }
		src_->setChannel(Channel::Depth);
	case Channel::Depth:
	default: src_->setChannel(Channel::None);
Nicolas Pope's avatar
Nicolas Pope committed
static Eigen::Matrix4d ConvertSteamVRMatrixToMatrix4( const vr::HmdMatrix34_t &matPose )
{
	Eigen::Matrix4d matrixObj;
	matrixObj <<
		matPose.m[0][0], matPose.m[1][0], matPose.m[2][0], 0.0,
		matPose.m[0][1], matPose.m[1][1], matPose.m[2][1], 0.0,
		matPose.m[0][2], matPose.m[1][2], matPose.m[2][2], 0.0,
		matPose.m[0][3], matPose.m[1][3], matPose.m[2][3], 1.0f;
	return matrixObj;
}

static void visualizeDepthMap(	const cv::Mat &depth, cv::Mat &out,
								const float max_depth)
{
	DCHECK(max_depth > 0.0);

	depth.convertTo(out, CV_8U, 255.0f / max_depth);
	out = 255 - out;
	cv::Mat mask = (depth >= 39.0f); // TODO (mask for invalid pixels)
	
	applyColorMap(out, out, cv::COLORMAP_JET);
	out.setTo(cv::Scalar(255, 255, 255), mask);
}

static void visualizeEnergy(	const cv::Mat &depth, cv::Mat &out,
								const float max_depth)
{
	DCHECK(max_depth > 0.0);

	depth.convertTo(out, CV_8U, 255.0f / max_depth);
	//out = 255 - out;
	cv::Mat mask = (depth >= 39.0f); // TODO (mask for invalid pixels)
	
	applyColorMap(out, out, cv::COLORMAP_JET);
	out.setTo(cv::Scalar(255, 255, 255), mask);
}

static void drawEdges(	const cv::Mat &in, cv::Mat &out,
						const int ksize = 3, double weight = -1.0, const int threshold = 32,
						const int threshold_type = cv::THRESH_TOZERO)
{
	cv::Mat edges;
	cv::Laplacian(in, edges, 8, ksize);
	cv::threshold(edges, edges, threshold, 255, threshold_type);

	cv::Mat edges_color(in.size(), CV_8UC3);
	cv::addWeighted(edges, weight, out, 1.0, 0.0, out, CV_8UC3);
}

bool ftl::gui::Camera::thumbnail(cv::Mat &thumb) {
	UNIQUE_LOCK(mutex_, lk);
	src_->grab(1,9);
	if (rgb_.empty()) return false;
	cv::resize(rgb_, thumb, cv::Size(320,180));
	return true;
}

Nicolas Pope's avatar
Nicolas Pope committed
const GLTexture &ftl::gui::Camera::captureFrame() {
	float now = (float)glfwGetTime();
	delta_ = now - ftime_;
	ftime_ = now;

	if (src_ && src_->isReady()) {
		UNIQUE_LOCK(mutex_, lk);
Nicolas Pope's avatar
Nicolas Pope committed
		if (screen_->hasVR()) {
			#ifdef HAVE_OPENVR
Nicolas Pope's avatar
Nicolas Pope committed
			src_->setChannel(Channel::Right);
Nicolas Pope's avatar
Nicolas Pope committed
			vr::VRCompositor()->WaitGetPoses(rTrackedDevicePose_, vr::k_unMaxTrackedDeviceCount, NULL, 0 );

			if ( rTrackedDevicePose_[vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid )
			{
				auto pose = ConvertSteamVRMatrixToMatrix4( rTrackedDevicePose_[vr::k_unTrackedDeviceIndex_Hmd].mDeviceToAbsoluteTracking );
				pose.inverse();

				// Lerp the Eye
				eye_[0] += (neye_[0] - eye_[0]) * lerpSpeed_ * delta_;
				eye_[1] += (neye_[1] - eye_[1]) * lerpSpeed_ * delta_;
				eye_[2] += (neye_[2] - eye_[2]) * lerpSpeed_ * delta_;

				Eigen::Translation3d trans(eye_);
				Eigen::Affine3d t(trans);
				Eigen::Matrix4d viewPose = t.matrix() * pose;

				if (src_->hasCapabilities(ftl::rgbd::kCapMovable)) src_->setPose(viewPose);
Nicolas Pope's avatar
Nicolas Pope committed
			} else {
				LOG(ERROR) << "No VR Pose";
			}
			#endif
		} else {
			// Lerp the Eye
			eye_[0] += (neye_[0] - eye_[0]) * lerpSpeed_ * delta_;
			eye_[1] += (neye_[1] - eye_[1]) * lerpSpeed_ * delta_;
			eye_[2] += (neye_[2] - eye_[2]) * lerpSpeed_ * delta_;

			Eigen::Translation3d trans(eye_);
			Eigen::Affine3d t(trans);
			Eigen::Matrix4d viewPose = t.matrix() * rotmat_;

			if (src_->hasCapabilities(ftl::rgbd::kCapMovable)) src_->setPose(viewPose);
		}
Nicolas Pope's avatar
Nicolas Pope committed

		src_->grab();
		//src_->getFrames(rgb, depth);
		// When switching from right to depth, client may still receive
		// right images from previous batch (depth.channels() == 1 check)
		if (channel_ == Channel::Deviation &&
			depth_.rows > 0 && depth_.channels() == 1)
				stats_ = new StatisticsImage(depth_.size());
			stats_->update(depth_);
Nicolas Pope's avatar
Nicolas Pope committed
		}

		cv::Mat tmp;

		switch(channel_) {
Nicolas Pope's avatar
Nicolas Pope committed
			case Channel::Confidence:
Nicolas Pope's avatar
Nicolas Pope committed
				if (depth_.rows == 0) { break; }
				visualizeEnergy(depth_, tmp, 1.0);
				texture_.update(tmp);
				break;
			case Channel::Energy:
				if (depth_.rows == 0) { break; }
				visualizeEnergy(depth_, tmp, 10.0);
				texture_.update(tmp);
				break;
			case Channel::Depth:
				if (depth_.rows == 0) { break; }
				visualizeDepthMap(depth_, tmp, 7.0);
				if (screen_->root()->value("showEdgesInDepth", false)) drawEdges(rgb_, tmp);
Nicolas Pope's avatar
Nicolas Pope committed
				texture_.update(tmp);
				break;
			
			case Channel::Deviation:
				if (depth_.rows == 0) { break; }
Nicolas Pope's avatar
Nicolas Pope committed
				//imageSize = Vector2f(depth.cols, depth.rows);
				stats_->getStdDev(tmp);
				tmp.convertTo(tmp, CV_8U, 1000.0);
Nicolas Pope's avatar
Nicolas Pope committed
				applyColorMap(tmp, tmp, cv::COLORMAP_HOT);
				texture_.update(tmp);
				break;

		case Channel::Flow:
		case Channel::Normals:
		case Channel::Right:
				if (depth_.rows == 0 || depth_.type() != CV_8UC3) { break; }
				texture_.update(depth_);
Nicolas Pope's avatar
Nicolas Pope committed
			default:
				if (rgb_.rows == 0) { break; }
Nicolas Pope's avatar
Nicolas Pope committed
				//imageSize = Vector2f(rgb.cols,rgb.rows);
				texture_.update(rgb_);
Nicolas Pope's avatar
Nicolas Pope committed

				#ifdef HAVE_OPENVR
Nicolas Pope's avatar
Nicolas Pope committed
				if (screen_->hasVR() && depth_.channels() >= 3) {
Nicolas Pope's avatar
Nicolas Pope committed
					LOG(INFO) << "DRAW RIGHT";
Nicolas Pope's avatar
Nicolas Pope committed
					textureRight_.update(depth_);
Nicolas Pope's avatar
Nicolas Pope committed
				}
				#endif
Nicolas Pope's avatar
Nicolas Pope committed
		}
	}

	return texture_;
}

nlohmann::json ftl::gui::Camera::getMetaData() {