From a30e27703473b7ce3d9a5f688248a5c6c39638ad Mon Sep 17 00:00:00 2001 From: Nicolas Pope <nicolas.pope@utu.fi> Date: Fri, 7 Aug 2020 10:25:44 +0300 Subject: [PATCH] Add pose adjustment and toolbar --- applications/gui2/src/modules/camera.cpp | 171 +++++++++- applications/gui2/src/modules/camera.hpp | 18 ++ .../gui2/src/modules/camera_tools.hpp | 40 +++ applications/gui2/src/modules/themes.cpp | 18 ++ applications/gui2/src/views/camera.cpp | 294 +++++++++++++++++- applications/gui2/src/views/camera.hpp | 48 +++ applications/gui2/src/views/camera3d.cpp | 63 +++- components/calibration/CMakeLists.txt | 1 + .../include/ftl/calibration/stereorectify.hpp | 51 +++ .../include/ftl/calibration/structures.hpp | 11 +- components/calibration/src/stereorectify.cpp | 43 +++ components/calibration/src/structures.cpp | 12 +- .../codecs/include/ftl/codecs/channels.hpp | 3 +- components/control/cpp/src/master.cpp | 3 +- components/operators/src/operator.cpp | 11 + .../cpp/include/ftl/render/overlay.hpp | 2 +- components/renderers/cpp/src/overlay.cpp | 7 +- .../rgbd-sources/include/ftl/rgbd/camera.hpp | 4 +- .../rgbd-sources/include/ftl/rgbd/frame.hpp | 7 + components/rgbd-sources/src/camera.cpp | 11 + components/rgbd-sources/src/frame.cpp | 10 +- .../src/sources/stereovideo/rectification.cpp | 10 +- .../src/sources/stereovideo/rectification.hpp | 3 - .../src/sources/stereovideo/stereovideo.cpp | 24 +- components/rgbd-sources/test/CMakeLists.txt | 2 +- .../streams/include/ftl/streams/feed.hpp | 3 + .../streams/include/ftl/streams/renderer.hpp | 2 + .../streams/include/ftl/streams/sender.hpp | 3 +- components/streams/src/baserender.hpp | 2 + components/streams/src/renderer.cpp | 5 + .../streams/src/renderers/openvr_render.hpp | 2 + .../streams/src/renderers/screen_render.hpp | 2 + components/streams/src/sender.cpp | 34 +- .../structures/include/ftl/data/new_frame.hpp | 36 ++- 34 files changed, 886 insertions(+), 70 deletions(-) create mode 100644 applications/gui2/src/modules/camera_tools.hpp create mode 100644 components/calibration/include/ftl/calibration/stereorectify.hpp create mode 100644 components/calibration/src/stereorectify.cpp diff --git a/applications/gui2/src/modules/camera.cpp b/applications/gui2/src/modules/camera.cpp index 1ed0d23a5..f527a4cc1 100644 --- a/applications/gui2/src/modules/camera.cpp +++ b/applications/gui2/src/modules/camera.cpp @@ -5,10 +5,14 @@ #include <ftl/rgbd/capabilities.hpp> #include <ftl/streams/renderer.hpp> #include <chrono> +#include <ftl/utility/matrix_conversion.hpp> +#include <ftl/calibration/structures.hpp> +#include <ftl/calibration/parameters.hpp> #include <opencv2/imgproc.hpp> #include <opencv2/imgcodecs.hpp> #include <opencv2/cudaarithm.hpp> +#include <opencv2/core/eigen.hpp> #include <loguru.hpp> @@ -235,6 +239,9 @@ void Camera::activate(ftl::data::FrameID id) { touch_ = false; movable_ = false; vr_ = false; + cursor_pos_.setZero(); + cursor_normal_.setZero(); + cursor_normal_[2] = 1.0f; //std::mutex m; //std::condition_variable cv; @@ -331,6 +338,10 @@ std::string Camera::getActiveSourceURI() { return ""; } +void Camera::toggleOverlay() { + overlay_->set("enabled", !overlay_->value<bool>("enabled", false)); +} + ftl::audio::StereoMixerF<100> *Camera::mixer() { if (mixer_) return mixer_; if (movable_) { @@ -416,10 +427,18 @@ bool Camera::hasFrame() { return false; } +Eigen::Matrix4d Camera::cursor() const { + if (cursor_normal_.norm() > 0.0f) return nanogui::lookAt(cursor_pos_, cursor_target_, cursor_normal_).cast<double>(); + + Eigen::Matrix4d ident; + ident.setIdentity(); + return ident; +} + void Camera::drawOverlay(NVGcontext *ctx, const nanogui::Vector2f &s, const nanogui::Vector2f &is, const Eigen::Vector2f &offset) { auto ptr = std::atomic_load(&latest_); // TODO: Need all the source framesets here or all data dumped in by renderer - overlay_->draw(ctx, *ptr, ptr->frames[frame_idx].cast<ftl::rgbd::Frame>(), s, is, offset); // , view->size().cast<float>() + overlay_->draw(ctx, *ptr, ptr->frames[frame_idx].cast<ftl::rgbd::Frame>(), s, is, offset, cursor()); // , view->size().cast<float>() } void Camera::sendPose(const Eigen::Matrix4d &pose) { @@ -461,7 +480,7 @@ void Camera::touch(int id, ftl::codecs::TouchType t, int x, int y, float d, int } float Camera::depthAt(int x, int y) { - if (value("show_depth", true)) { + //if (value("show_depth", true)) { auto ptr = std::atomic_load(&latest_); if (ptr) { @@ -474,6 +493,152 @@ float Camera::depthAt(int x, int y) { } } } - } + //} return 0.0f; } + +static float3 getWorldPoint(const cv::Mat &depth, int x, int y, const ftl::rgbd::Camera &intrins, const Eigen::Matrix4f &pose) { + if (x >= 0 && y >= 0 && x < depth.cols && y < depth.rows) { + float d = depth.at<float>(y, x); + + if (d > intrins.minDepth && d < intrins.maxDepth) { + float3 cam = intrins.screenToCam(x, y, d); + float3 world = MatrixConversion::toCUDA(pose) * cam; + return world; + } + } + return make_float3(0.0f,0.0f,0.0f); +} + + +Eigen::Vector3f Camera::worldAt(int x, int y) { + auto ptr = std::atomic_load(&latest_); + + Eigen::Vector3f res; + res.setZero(); + + if (ptr) { + const auto &frame = ptr->frames[frame_idx].cast<ftl::rgbd::Frame>(); + + if (frame.hasChannel(Channel::Depth)) { + const auto &depth = frame.get<cv::Mat>(Channel::Depth); + const auto &intrins = frame.getLeft(); + Eigen::Matrix4f posef = frame.getPose().cast<float>(); + + float3 CC = getWorldPoint(depth, x, y, intrins, posef); + res[0] = CC.x; + res[1] = CC.y; + res[2] = CC.z; + } + } + + return res; +} + +void Camera::setCursor(int x, int y) { + auto ptr = std::atomic_load(&latest_); + + cursor_pos_.setZero(); + + if (ptr) { + const auto &frame = ptr->frames[frame_idx].cast<ftl::rgbd::Frame>(); + + if (frame.hasChannel(Channel::Depth)) { + const auto &depth = frame.get<cv::Mat>(Channel::Depth); + const auto &intrins = frame.getLeft(); + Eigen::Matrix4f posef = frame.getPose().cast<float>(); + + + float3 CC = getWorldPoint(depth, x, y, intrins, posef); + cursor_pos_[0] = CC.x; + cursor_pos_[1] = CC.y; + cursor_pos_[2] = CC.z; + + // Now find normal + float3 PC = getWorldPoint(depth, x, y+4, intrins, posef); + float3 CP = getWorldPoint(depth, x+4, y, intrins, posef); + float3 MC = getWorldPoint(depth, x, y-4, intrins, posef); + float3 CM = getWorldPoint(depth, x-4, y, intrins, posef); + const float3 n = cross(PC-MC, CP-CM); + const float l = length(n); + + if (l > 0.0f) { + cursor_normal_[0] = n.x / -l; + cursor_normal_[1] = n.y / -l; + cursor_normal_[2] = n.z / -l; + } + + cursor_target_[0] = CP.x; + cursor_target_[1] = CP.y; + cursor_target_[2] = CP.z; + } + } +} + +void Camera::setOriginToCursor() { + using ftl::calibration::transform::inverse; + + // Check for valid cursor + if (cursor_normal_.norm() == 0.0f) return; + float cursor_length = (cursor_target_ - cursor_pos_).norm(); + float cursor_dist = cursor_pos_.norm(); + if (cursor_length < 0.01f || cursor_length > 5.0f) return; + if (cursor_dist > 10.0f) return; + + if (movable_) { + auto *rend = io->feed()->getRenderer(frame_id_); + if (rend) { + auto *filter = rend->filter(); + if (filter) { + cv::Mat cur; + cv::eigen2cv(cursor(), cur); + auto fss = filter->getLatestFrameSets(); + for (auto &fs : fss) { + if (fs->frameset() == frame_id_.frameset()) continue; + + for (auto &f : fs->frames) { + auto response = f.response(); + auto &rgbdf = response.cast<ftl::rgbd::Frame>(); + auto &calib = rgbdf.setCalibration(); + calib = f.cast<ftl::rgbd::Frame>().getCalibration(); + // apply correction to existing one + calib.origin = cur*calib.origin; + } + }; + } + } + } + + cursor_target_ = Eigen::Vector3f(0.0f,0.0f,0.0f); + cursor_pos_ = Eigen::Vector3f(0.0f,0.0f,0.0f); + cursor_normal_ = Eigen::Vector3f(0.0f,0.0f,0.0f); +} + +void Camera::resetOrigin() { + cursor_target_ = Eigen::Vector3f(0.0f,0.0f,0.0f); + cursor_pos_ = Eigen::Vector3f(0.0f,0.0f,0.0f); + cursor_normal_ = Eigen::Vector3f(0.0f,0.0f,0.0f); + + if (movable_) { + auto *rend = io->feed()->getRenderer(frame_id_); + if (rend) { + auto *filter = rend->filter(); + if (filter) { + cv::Mat cur; + cv::eigen2cv(cursor(), cur); + auto fss = filter->getLatestFrameSets(); + for (auto &fs : fss) { + if (fs->frameset() == frame_id_.frameset()) continue; + + for (auto &f : fs->frames) { + auto response = f.response(); + auto &rgbdf = response.cast<ftl::rgbd::Frame>(); + auto &calib = rgbdf.setCalibration(); + calib = f.cast<ftl::rgbd::Frame>().getCalibration(); + calib.origin = cur; + } + }; + } + } + } +} diff --git a/applications/gui2/src/modules/camera.hpp b/applications/gui2/src/modules/camera.hpp index c22675f7e..d314f0a7a 100644 --- a/applications/gui2/src/modules/camera.hpp +++ b/applications/gui2/src/modules/camera.hpp @@ -24,6 +24,8 @@ public: void setPaused(bool set) { paused_ = set; }; bool isPaused() { return paused_; } + void toggleOverlay(); + float volume(); void setVolume(float v); @@ -60,6 +62,7 @@ public: std::string getActiveSourceURI(); float depthAt(int x, int y); + Eigen::Vector3f worldAt(int x, int y); bool isRecording(); void stopRecording(); @@ -68,6 +71,18 @@ public: void snapshot(const std::string &filename); + Eigen::Matrix4d cursor() const; + + void setCursorPosition(const Eigen::Vector3f &pos) { cursor_pos_ = pos; } + void setCursorNormal(const Eigen::Vector3f &norm) { cursor_normal_ = norm; } + void setCursorTarget(const Eigen::Vector3f &targ) { cursor_target_ = targ; } + void setCursor(int x, int y); + + const Eigen::Vector3f getCursorPosition() const { return cursor_pos_; } + + void setOriginToCursor(); + void resetOrigin(); + private: int frame_idx = -1; ftl::data::FrameID frame_id_; @@ -84,6 +99,9 @@ private: std::atomic_int16_t nframes_=0; std::atomic_int64_t latency_=0; int update_fps_freq_=30; // fps counter update frequency (frames) + Eigen::Vector3f cursor_pos_; + Eigen::Vector3f cursor_target_; + Eigen::Vector3f cursor_normal_; ftl::data::FrameSetPtr current_fs_; ftl::data::FrameSetPtr latest_; diff --git a/applications/gui2/src/modules/camera_tools.hpp b/applications/gui2/src/modules/camera_tools.hpp new file mode 100644 index 000000000..8e665b842 --- /dev/null +++ b/applications/gui2/src/modules/camera_tools.hpp @@ -0,0 +1,40 @@ +#ifndef _FTL_GUI_CAMERA_TOOLS_HPP_ +#define _FTL_GUI_CAMERA_TOOLS_HPP_ + +namespace ftl { +namespace gui2 { + +enum class Tools { + NONE, + SELECT_POINT, // Touch 2D + MOVEMENT, // 3D first person camera controls + PAN, // 2D Panning + CENTRE_VIEW, + ZOOM_FIT, + ZOOM_IN, + ZOOM_OUT, + CLIPPING, + OVERLAY, + LAYOUT, + MOVE_CURSOR, // Move 3D Cursor + ROTATE_CURSOR, + ORIGIN_TO_CURSOR, + RESET_ORIGIN, + SAVE_CURSOR, + ROTATE_X, + ROTATE_Y, + ROTATE_Z, + INSPECT_POINT +}; + +enum class ToolGroup { + MOUSE_MOTION, + VIEW_2D_ACTIONS, + VIEW_3D_LAYERS, + VIEW_3D_ACTIONS +}; + +} +} + +#endif \ No newline at end of file diff --git a/applications/gui2/src/modules/themes.cpp b/applications/gui2/src/modules/themes.cpp index 67f7f7760..8b0d0ef06 100644 --- a/applications/gui2/src/modules/themes.cpp +++ b/applications/gui2/src/modules/themes.cpp @@ -94,6 +94,24 @@ void Themes::init() { mediatheme->mButtonFontSize = 30; mediatheme->mStandardFontSize = 20; + auto* mediatheme2 = screen->getTheme("media_small"); + mediatheme2->mIconScale = 1.2f; + mediatheme2->mWindowDropShadowSize = 0; + mediatheme2->mWindowFillFocused = nanogui::Color(45, 150); + mediatheme2->mWindowFillUnfocused = nanogui::Color(45, 80); + mediatheme2->mButtonGradientTopUnfocused = nanogui::Color(0,0); + mediatheme2->mButtonGradientBotUnfocused = nanogui::Color(0,0); + mediatheme2->mButtonGradientTopFocused = nanogui::Color(80,230); + mediatheme2->mButtonGradientBotFocused = nanogui::Color(80,230); + mediatheme2->mIconColor = nanogui::Color(255,255); + mediatheme2->mTextColor = nanogui::Color(1.0f,1.0f,1.0f,1.0f); + mediatheme2->mBorderDark = nanogui::Color(0,0); + mediatheme2->mBorderMedium = nanogui::Color(0,0); + mediatheme2->mBorderLight = nanogui::Color(0,0); + mediatheme2->mDropShadow = nanogui::Color(0,0); + mediatheme2->mButtonFontSize = 16; + mediatheme2->mStandardFontSize = 14; + // https://flatuicolors.com/palette/defo screen->setColor("highlight1", nanogui::Color(231, 76, 60, 255)); // red screen->setColor("highlight2", nanogui::Color(52, 152, 219, 255)); // blue diff --git a/applications/gui2/src/views/camera.cpp b/applications/gui2/src/views/camera.cpp index b70f82da0..451a032ee 100644 --- a/applications/gui2/src/views/camera.cpp +++ b/applications/gui2/src/views/camera.cpp @@ -19,9 +19,12 @@ using ftl::gui2::Camera; using ftl::gui2::FixedWindow; using ftl::gui2::MediaPanel; +using ftl::gui2::ToolPanel; using ftl::gui2::CameraView; using ftl::gui2::PopupButton; using ftl::gui2::VolumeButton; +using ftl::gui2::Tools; +using ftl::gui2::ToolGroup; using ftl::codecs::Channel; @@ -368,6 +371,237 @@ nanogui::Button* MediaPanel::addButton(int pos) { return button; } +// === ToolPanel =============================================================== + +ToolPanel::ToolPanel(nanogui::Widget *parent, ftl::gui2::Camera* ctrl, CameraView* view) : + ftl::gui2::FixedWindow(parent, ""), ctrl_(ctrl), view_(view) { + + LOG(INFO) << __func__ << " (" << this << ")"; + using namespace nanogui; + + setLayout(new BoxLayout(Orientation::Vertical, + Alignment::Middle, 5, 10)); + + container_ = new Widget(this); + container_->setLayout(new BoxLayout(Orientation::Vertical, + Alignment::Middle, 0, 10)); + + auto theme = dynamic_cast<ftl::gui2::Screen*>(screen())->getTheme("media_small"); + this->setTheme(theme); + + auto *mouse_group = _addGroup(ToolGroup::MOUSE_MOTION, Button::Flags::RadioButton, { + Tools::SELECT_POINT, + Tools::MOVEMENT, + Tools::MOVE_CURSOR, + Tools::ROTATE_CURSOR, + Tools::PAN, + Tools::INSPECT_POINT, + Tools::ZOOM_IN, + Tools::ZOOM_OUT + }); + _addButton(mouse_group, Tools::SELECT_POINT, ENTYPO_ICON_MOUSE_POINTER, "Select Point"); + _addButton(mouse_group, Tools::MOVEMENT, ENTYPO_ICON_MAN, "First Person Camera"); + _addButton(mouse_group, Tools::MOVE_CURSOR, ENTYPO_ICON_DIRECTION, "Move 3D Cursor"); + _addButton(mouse_group, Tools::PAN, ENTYPO_ICON_MOUSE, "Pan Image"); + _addButton(mouse_group, Tools::INSPECT_POINT, ENTYPO_ICON_MAGNIFYING_GLASS, "Inspect Point"); + _addButton(mouse_group, Tools::ZOOM_IN, ENTYPO_ICON_CIRCLE_WITH_PLUS, "Zoom In (+)"); + _addButton(mouse_group, Tools::ZOOM_OUT, ENTYPO_ICON_CIRCLE_WITH_MINUS, "Zoom Out (-)"); + + auto *view2d_group = _addGroup(ToolGroup::VIEW_2D_ACTIONS, Button::Flags::NormalButton, { + Tools::CENTRE_VIEW, + Tools::ZOOM_FIT + }); + _addButton(view2d_group, Tools::CENTRE_VIEW, ENTYPO_ICON_ALIGN_HORIZONTAL_MIDDLE, "Centre the View (c)"); + _addButton(view2d_group, Tools::ZOOM_FIT, ENTYPO_ICON_RESIZE_FULL_SCREEN, "Zoom to Fit (f)"); + + + //_addButton(CameraTools::ORIGIN_TO_CURSOR, ENTYPO_ICON_LOCATION, "Origin to 3D Cursor"); + auto *action3d_group = _addGroup(ToolGroup::VIEW_3D_ACTIONS, Button::Flags::NormalButton, { + Tools::ORIGIN_TO_CURSOR, + Tools::RESET_ORIGIN, + Tools::SAVE_CURSOR + }); + auto *cur_but = _addButton(action3d_group, { + Tools::ORIGIN_TO_CURSOR, + Tools::RESET_ORIGIN, + Tools::SAVE_CURSOR + }, ENTYPO_ICON_LOCATION, "Use Cursor"); + _addButton(cur_but, Tools::ORIGIN_TO_CURSOR, "Origin to Cursor"); + _addButton(cur_but, Tools::RESET_ORIGIN, "Reset Origin"); + _addButton(cur_but, Tools::SAVE_CURSOR, "Save Cursor as Pose"); + + auto *view3d_group = _addGroup(ToolGroup::VIEW_3D_LAYERS, Button::Flags::ToggleButton, { + Tools::OVERLAY, + Tools::CLIPPING + }); + _addButton(view3d_group, Tools::OVERLAY, ENTYPO_ICON_LINE_GRAPH, "Show/Hide Overlay"); + _addButton(view3d_group, Tools::CLIPPING, ENTYPO_ICON_SCISSORS, "Enable/Disable Clipping"); + + auto *b = new Button(this, "", ENTYPO_ICON_CHEVRON_THIN_UP); + b->setTooltip("Show/Hide Tools"); + b->setCallback([this, b]() { + if (container_->visible()) { + container_->setVisible(false); + b->setIcon(ENTYPO_ICON_CHEVRON_THIN_UP); + screen()->performLayout(); + } else { + container_->setVisible(true); + b->setIcon(ENTYPO_ICON_CHEVRON_THIN_DOWN); + screen()->performLayout(); + } + }); + container_->setVisible(false); +} + +ToolPanel::~ToolPanel() { + +} + +bool ToolPanel::isActive(ftl::gui2::Tools tool) { + if (group_map_.count(tool)) { + auto &grp = group_data_[group_map_[tool]]; + return grp.active.count(tool) > 0; + } + return false; +} + +void ToolPanel::setTool(ftl::gui2::Tools tool) { + if (group_map_.count(tool)) { + auto &grp = group_data_[group_map_[tool]]; + + if (grp.type == nanogui::Button::Flags::RadioButton) { + for (auto t : grp.active) { + if (t != tool) { + if (buttons_.count(t)) { + auto *b = buttons_[t]; + b->setTextColor(nanogui::Color(255,255,255,255)); + b->setPushed(false); + } + } + } + + grp.active.clear(); + grp.active.insert(tool); + + if (buttons_.count(tool)) { + auto *b = buttons_[tool]; + b->setTextColor(dynamic_cast<Screen*>(screen())->getColor("highlight1")); + b->setPushed(true); + } + } else if (grp.type == nanogui::Button::Flags::ToggleButton) { + grp.active.insert(tool); + + if (buttons_.count(tool)) { + auto *b = buttons_[tool]; + b->setTextColor(dynamic_cast<Screen*>(screen())->getColor("highlight1")); + b->setPushed(true); + } + } else { + + } + + for (auto &f : callbacks_) { + if (f(tool)) break; + } + } +} + +nanogui::Widget *ToolPanel::_addGroup(ftl::gui2::ToolGroup group, nanogui::Button::Flags type, const std::unordered_set<ftl::gui2::Tools> &tools) { + auto &grp = group_data_[group]; + grp.tools = tools; + grp.type = type; + for (auto t : tools) group_map_[t] = group; + + auto *w = new nanogui::Widget(container_); + w->setLayout(new nanogui::BoxLayout(nanogui::Orientation::Vertical, nanogui::Alignment::Middle, 0, 10)); + return w; +} + +void ToolPanel::_addButton(nanogui::Widget *g, ftl::gui2::Tools tool, int icon, const std::string &tooltip) { + auto *b = new nanogui::Button(g, "", icon); + b->setTooltip(tooltip); + b->setCallback([this, tool]() { + setTool(tool); + }); + buttons_[tool] = b; +} + +void ToolPanel::_addButton(ftl::gui2::PopupButton *parent, ftl::gui2::Tools tool, const std::string &label) { + auto *b = new nanogui::Button(parent->popup(), label); + b->setCallback([this, parent, tool]() { + parent->setPushed(false); + setTool(tool); + }); + //buttons_[tool] = b; +} + +ftl::gui2::PopupButton *ToolPanel::_addButton(nanogui::Widget *g, std::unordered_set<ftl::gui2::Tools> tools, int icon, const std::string &tooltip) { + auto *b = new ftl::gui2::PopupButton(g, "", icon); + b->setTooltip(tooltip); + b->setSide(nanogui::Popup::Side::Left); + b->setChevronIcon(0); + + for (auto t : tools) { + buttons_[t] = b; + } + + auto *popup = b->popup(); + popup->setLayout(new nanogui::BoxLayout + (nanogui::Orientation::Vertical, nanogui::Alignment::Fill, 10, 6)); + + auto theme = dynamic_cast<ftl::gui2::Screen*>(screen())->getTheme("media_small"); + popup->setTheme(theme); + + return b; +} + +void ToolPanel::setAvailable(const std::unordered_set<ftl::gui2::Tools> &s) { + for (auto &b : buttons_) { + if (s.count(b.first)) { + b.second->setVisible(true); + } else { + b.second->setVisible(false); + } + } +} + +void ToolPanel::setEnabled(const std::unordered_set<ftl::gui2::Tools> &s) { + for (auto &b : buttons_) { + if (s.count(b.first)) { + b.second->setVisible(true); + b.second->setEnabled(true); + } else { + b.second->setEnabled(false); + } + } +} + +void ToolPanel::enable(const std::unordered_set<ftl::gui2::Tools> &s) { + for (auto &b : buttons_) { + if (s.count(b.first)) { + b.second->setVisible(true); + b.second->setEnabled(true); + } + } +} + +void ToolPanel::disable(const std::unordered_set<ftl::gui2::Tools> &s) { + for (auto &b : buttons_) { + if (s.count(b.first)) { + b.second->setEnabled(false); + } + } +} + +void ToolPanel::draw(NVGcontext *ctx) { + auto size = this->size(); + setPosition( + nanogui::Vector2i( screen()->width() - 30 - size[0], + screen()->height() - 30 - size[1])); + + FixedWindow::draw(ctx); +} + // ==== CameraView ============================================================= CameraView::CameraView(ftl::gui2::Screen* parent, ftl::gui2::Camera* ctrl) : @@ -376,6 +610,7 @@ CameraView::CameraView(ftl::gui2::Screen* parent, ftl::gui2::Camera* ctrl) : imview_ = new ftl::gui2::FTLImageView(this); panel_ = new ftl::gui2::MediaPanel(screen(), ctrl, this); + tools_ = new ftl::gui2::ToolPanel(screen(), ctrl, this); imview_->setFlipped(ctrl->isVR()); @@ -410,6 +645,28 @@ CameraView::CameraView(ftl::gui2::Screen* parent, ftl::gui2::Camera* ctrl) : context_menu_->setVisible(false); ctrl_->screen->getModule<ftl::gui2::ConfigCtrl>()->show(ctrl_->getID()); }); + + tools_->setAvailable({ + Tools::SELECT_POINT, + Tools::OVERLAY, + Tools::PAN, + Tools::ZOOM_FIT, + Tools::ZOOM_IN, + Tools::ZOOM_OUT, + Tools::CENTRE_VIEW, + Tools::INSPECT_POINT + }); + + tools_->addCallback([this](ftl::gui2::Tools tool) { + switch (tool) { + case Tools::OVERLAY : ctrl_->toggleOverlay(); return true; + case Tools::ZOOM_FIT : imview_->fit(); return true; + case Tools::CENTRE_VIEW : imview_->center(); return true; + //case CameraTools::ZOOM_OUT : imview_->zoom(-1, imview_->sizeF() / 2); return true; + //case CameraTools::ZOOM_IN : imview_->zoom(1, imview_->sizeF() / 2); return true; + default: return false; + } + }); } CameraView::~CameraView() { @@ -417,6 +674,7 @@ CameraView::~CameraView() { // segfault without this check; nanogui already deleted windows? // should be fixed in nanogui panel_->dispose(); + tools_->dispose(); } if (context_menu_->parent()->getRefCount() > 0) { @@ -473,11 +731,14 @@ void CameraView::setPan(bool v) { bool CameraView::mouseMotionEvent(const Eigen::Vector2i &p, const Eigen::Vector2i &rel, int button, int modifiers) { //if (button == 1) { - auto pos = imview_->imageCoordinateAt((p - mPos + rel).cast<float>()); - if (pos.x() >= 0.0f && pos.y() >= 0.0f) { - ctrl_->touch(0, ftl::codecs::TouchType::MOUSE_LEFT, pos.x(), pos.y(), 0.0f, (button > 0) ? 255 : 0); - //LOG(INFO) << "Depth at " << pos.x() << "," << pos.y() << " = " << ctrl_->depthAt(pos.x(), pos.y()); + if (tools_->isActive(Tools::SELECT_POINT)) { + auto pos = imview_->imageCoordinateAt((p - mPos + rel).cast<float>()); + if (pos.x() >= 0.0f && pos.y() >= 0.0f) { + ctrl_->touch(0, ftl::codecs::TouchType::MOUSE_LEFT, pos.x(), pos.y(), 0.0f, (button > 0) ? 255 : 0); + + //LOG(INFO) << "Depth at " << pos.x() << "," << pos.y() << " = " << ctrl_->depthAt(pos.x(), pos.y()); + } } return true; //} @@ -487,10 +748,17 @@ bool CameraView::mouseMotionEvent(const Eigen::Vector2i &p, const Eigen::Vector2 bool CameraView::mouseButtonEvent(const Eigen::Vector2i &p, int button, bool down, int modifiers) { //LOG(INFO) << "mouseButtonEvent: " << p << " - " << button; if (button == 0) { - auto pos = imview_->imageCoordinateAt((p - mPos).cast<float>()); - if (pos.x() >= 0.0f && pos.y() >= 0.0f) { - ctrl_->touch(0, ftl::codecs::TouchType::MOUSE_LEFT, pos.x(), pos.y(), 0.0f, (down) ? 255 : 0); + if (tools_->isActive(Tools::SELECT_POINT)) { + auto pos = imview_->imageCoordinateAt((p - mPos).cast<float>()); + if (pos.x() >= 0.0f && pos.y() >= 0.0f) { + ctrl_->touch(0, ftl::codecs::TouchType::MOUSE_LEFT, pos.x(), pos.y(), 0.0f, (down) ? 255 : 0); + } + } else if (tools_->isActive(Tools::ZOOM_IN)) { + imview_->zoom(1, p.cast<float>()); + } else if (tools_->isActive(Tools::ZOOM_OUT)) { + imview_->zoom(-1, p.cast<float>()); } + context_menu_->setVisible(false); return true; } else if (button == 1) { @@ -525,12 +793,14 @@ void CameraView::draw(NVGcontext*ctx) { auto osize = imview_->scaledImageSizeF(); ctrl_->drawOverlay(ctx, screen()->size().cast<float>(), osize, imview_->offset()); - auto mouse = screen()->mousePos(); - auto pos = imview_->imageCoordinateAt((mouse - mPos).cast<float>()); - float d = ctrl_->depthAt(pos.x(), pos.y()); + if (tools_->isActive(Tools::INSPECT_POINT)) { + auto mouse = screen()->mousePos(); + auto pos = imview_->imageCoordinateAt((mouse - mPos).cast<float>()); + float d = ctrl_->depthAt(pos.x(), pos.y()); - if (d > 0.0f) { - nvgText(ctx, mouse.x()+25.0f, mouse.y()+20.0f, (to_string_with_precision(d,2) + std::string("m")).c_str(), nullptr); + if (d > 0.0f) { + nvgText(ctx, mouse.x()+25.0f, mouse.y()+20.0f, (to_string_with_precision(d,2) + std::string("m")).c_str(), nullptr); + } } } diff --git a/applications/gui2/src/views/camera.hpp b/applications/gui2/src/views/camera.hpp index 3c1c27a81..179f4eff2 100644 --- a/applications/gui2/src/views/camera.hpp +++ b/applications/gui2/src/views/camera.hpp @@ -7,12 +7,59 @@ #include "../widgets/window.hpp" #include "../widgets/soundctrl.hpp" #include "../widgets/imageview.hpp" +#include "../widgets/popupbutton.hpp" +#include "../modules/camera_tools.hpp" namespace ftl { namespace gui2 { class Camera; class MediaPanel; +class CameraView; + + +struct ToolGroupData { + nanogui::Button::Flags type; + std::unordered_set<ftl::gui2::Tools> active; + std::unordered_set<ftl::gui2::Tools> tools; +}; + +class ToolPanel : public FixedWindow { +public: + ToolPanel(nanogui::Widget *parent, Camera* ctrl, CameraView* view); + virtual ~ToolPanel(); + + void setAvailable(const std::unordered_set<ftl::gui2::Tools> &); + void setEnabled(const std::unordered_set<ftl::gui2::Tools> &); + void enable(const std::unordered_set<ftl::gui2::Tools> &); + void disable(const std::unordered_set<ftl::gui2::Tools> &); + + void draw(NVGcontext *ctx) override; + + //inline ftl::gui2::Tools activeTool() const { return active_; } + bool isActive(ftl::gui2::Tools); + void setTool(ftl::gui2::Tools tool); + + inline void addCallback(const std::function<bool(ftl::gui2::Tools)> &cb) { callbacks_.push_back(cb); } + +private: + Camera* ctrl_; + CameraView* view_; + nanogui::Widget *container_; + std::unordered_map<ftl::gui2::Tools, nanogui::Button*> buttons_; + std::unordered_map<ftl::gui2::ToolGroup, ftl::gui2::ToolGroupData> group_data_; + std::unordered_map<ftl::gui2::Tools, ftl::gui2::ToolGroup> group_map_; + + std::list<std::function<bool(ftl::gui2::Tools)>> callbacks_; + + nanogui::Widget *_addGroup(ftl::gui2::ToolGroup group, nanogui::Button::Flags type, const std::unordered_set<ftl::gui2::Tools> &tools); + void _addButton(nanogui::Widget *, ftl::gui2::Tools, int icon, const std::string &tooltip); + void _addButton(ftl::gui2::PopupButton *parent, ftl::gui2::Tools, const std::string &label); + ftl::gui2::PopupButton *_addButton(nanogui::Widget *, std::unordered_set<ftl::gui2::Tools> tools, int icon, const std::string &tooltip); + +public: + EIGEN_MAKE_ALIGNED_OPERATOR_NEW +}; class CameraView : public View { public: @@ -35,6 +82,7 @@ protected: bool enable_pan_; Camera* ctrl_; MediaPanel* panel_; + ToolPanel* tools_; FTLImageView* imview_; nanogui::Window *context_menu_; diff --git a/applications/gui2/src/views/camera3d.cpp b/applications/gui2/src/views/camera3d.cpp index 9d1c0fea2..29bb0ae4d 100644 --- a/applications/gui2/src/views/camera3d.cpp +++ b/applications/gui2/src/views/camera3d.cpp @@ -35,8 +35,35 @@ CameraView3D::CameraView3D(ftl::gui2::Screen *parent, ftl::gui2::Camera *ctrl) : pose_up_to_date_.test_and_set(); + tools_->setAvailable({ + Tools::SELECT_POINT, + Tools::MOVEMENT, + Tools::OVERLAY, + Tools::INSPECT_POINT, + Tools::CLIPPING, + Tools::MOVE_CURSOR, + Tools::ORIGIN_TO_CURSOR, + Tools::RESET_ORIGIN, + Tools::SAVE_CURSOR + }); + setZoom(false); setPan(false); + + tools_->setTool(Tools::MOVEMENT); + + tools_->addCallback([this](ftl::gui2::Tools tool) { + if (tool == Tools::ORIGIN_TO_CURSOR) { + ctrl_->setOriginToCursor(); + tools_->setTool(Tools::MOVEMENT); + return true; + } else if (tool == Tools::RESET_ORIGIN) { + ctrl_->resetOrigin(); + tools_->setTool(Tools::MOVEMENT); + return true; + } + return false; + }); } bool CameraView3D::keyboardEvent(int key, int scancode, int action, int modifiers) { @@ -65,21 +92,45 @@ bool CameraView3D::keyboardEvent(int key, int scancode, int action, int modifier } bool CameraView3D::mouseButtonEvent(const Eigen::Vector2i &p, int button, bool down, int modifiers) { + if (button == 0 && !down) { + if (tools_->isActive(Tools::MOVE_CURSOR)) { + auto mouse = screen()->mousePos(); + auto pos = imview_->imageCoordinateAt((mouse - mPos).cast<float>()); + //Eigen::Vector3f world = ctrl_->worldAt(pos.x(), pos.y()); + + ctrl_->setCursor(pos.x(), pos.y()); + tools_->setTool(Tools::ROTATE_CURSOR); + return true; + } else if (tools_->isActive(Tools::ROTATE_CURSOR)) { + tools_->setTool(Tools::MOVEMENT); + } + } + return CameraView::mouseButtonEvent(p, button, down, modifiers); } bool CameraView3D::mouseMotionEvent(const Eigen::Vector2i &p, const Eigen::Vector2i &rel, int button, int modifiers) { - if (button != 1) { + //if (button != 1) { + // return true; + //} + + if (button == 1 && tools_->isActive(Tools::MOVEMENT)) { + rx_ += rel[0]; + ry_ += rel[1]; + pose_up_to_date_.clear(); return true; - } + } else if (tools_->isActive(Tools::ROTATE_CURSOR)) { + auto mouse = screen()->mousePos(); + auto pos = imview_->imageCoordinateAt((mouse - mPos).cast<float>()); - rx_ += rel[0]; - ry_ += rel[1]; - pose_up_to_date_.clear(); + Eigen::Vector3f world = ctrl_->worldAt(pos.x(), pos.y()); + ctrl_->setCursorTarget(world); + return true; + } //LOG(INFO) << "New pose: \n" << getUpdatedPose(); //ctrl_->sendPose(getUpdatedPose()); - return true; + return false; } bool CameraView3D::scrollEvent(const Eigen::Vector2i &p, const Eigen::Vector2f &rel) { diff --git a/components/calibration/CMakeLists.txt b/components/calibration/CMakeLists.txt index 68d658842..1f619a6e0 100644 --- a/components/calibration/CMakeLists.txt +++ b/components/calibration/CMakeLists.txt @@ -4,6 +4,7 @@ set(CALIBSRC src/structures.cpp src/visibility.cpp src/object.cpp + src/stereorectify.cpp ) if (WITH_CERES) diff --git a/components/calibration/include/ftl/calibration/stereorectify.hpp b/components/calibration/include/ftl/calibration/stereorectify.hpp new file mode 100644 index 000000000..47586b0ed --- /dev/null +++ b/components/calibration/include/ftl/calibration/stereorectify.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include <opencv2/core.hpp> +#include <ftl/calibration/structures.hpp> + +namespace ftl { +namespace calibration { + +/** Stereo rectification parameters. Wrapper for cv::stereoRectify() */ +struct StereoRectify { + /** Calculate rectification parameters. c1 and c2 contain valid calibration + * (extrinsic parameters for translation from world to camera) */ + StereoRectify(const CalibrationData::Calibration &c1, const CalibrationData::Calibration& c2, const cv::Size size={0, 0}, double alpha=0.0, int flags=0); + + /** stereo pair baseline (same unit as extrinsic paramters) */ + double baseline() const; + + /** calculate maps (cv::remap()) for camera 1 */ + void map1(cv::Mat &m1, cv::Mat &m2, int format=CV_16SC2); + /** calculate maps (cv::remap()) for camera 2 */ + void map2(cv::Mat &m1, cv::Mat &m2, int format=CV_16SC2); + + cv::Size size; + /** unrectified params */ + cv::Mat K1; + cv::Mat K2; + cv::Mat distCoeffs1; + cv::Mat distCoeffs2; + + /** 3x4 projection matrix for first camera */ + cv::Mat P1; + /** 3x4 projection matrix for second camera */ + cv::Mat P2; + /** rotation matrix for first camera (unrectified to rectified) */ + cv::Mat R1; + /** rotation matrix for second camera (unrectified to rectified) */ + cv::Mat R2; + /** disparity to depth matrix */ + cv::Mat Q; + /** rotation from first camera to second camera (unrectified) */ + cv::Mat R; + /** translation from first camera to second camera (unrectified) */ + cv::Mat t; + /** largest ROI containing only valid pixels in rectified image for first camera */ + cv::Rect roi1; + /** largest ROI containing only valid pixels in rectified image for second camera */ + cv::Rect roi2; +}; + +} +} \ No newline at end of file diff --git a/components/calibration/include/ftl/calibration/structures.hpp b/components/calibration/include/ftl/calibration/structures.hpp index 976614374..fffbe5035 100644 --- a/components/calibration/include/ftl/calibration/structures.hpp +++ b/components/calibration/include/ftl/calibration/structures.hpp @@ -115,6 +115,7 @@ struct CalibrationData { struct Calibration { Intrinsic intrinsic; Extrinsic extrinsic; + MSGPACK_DEFINE(intrinsic, extrinsic); }; @@ -129,12 +130,16 @@ struct CalibrationData { Calibration& get(ftl::codecs::Channel channel); bool hasCalibration(ftl::codecs::Channel channel) const; -private: // TODO: identify cameras with unique ID string instead of channel. - std::map<ftl::codecs::Channel, Calibration> data_; + std::map<ftl::codecs::Channel, Calibration> data; + + /** Correction to be applied (inverse) to extrinsic parameters + * (calibrated to new origin); Applied to rectified pose at the moment + */ + cv::Mat origin = cv::Mat::eye(4, 4, CV_64FC1); public: - MSGPACK_DEFINE(enabled, data_); + MSGPACK_DEFINE(enabled, data, origin); }; } diff --git a/components/calibration/src/stereorectify.cpp b/components/calibration/src/stereorectify.cpp new file mode 100644 index 000000000..7612a3554 --- /dev/null +++ b/components/calibration/src/stereorectify.cpp @@ -0,0 +1,43 @@ +#include <ftl/calibration/stereorectify.hpp> +#include <ftl/calibration/parameters.hpp> +#include <opencv2/calib3d.hpp> + +namespace ftl { +namespace calibration { + +// ==== StereoRectify ========================================================== + +StereoRectify::StereoRectify(const CalibrationData::Calibration& c1, const CalibrationData::Calibration& c2, cv::Size sz, double alpha, int flags) { + size = sz; + + if (size == cv::Size{0, 0}) { + size = c1.intrinsic.resolution; + } + K1 = c1.intrinsic.matrix(size); + K2 = c2.intrinsic.matrix(size); + c1.intrinsic.distCoeffs.Mat().copyTo(distCoeffs1); + c2.intrinsic.distCoeffs.Mat().copyTo(distCoeffs2); + + cv::Mat T1 = c1.extrinsic.matrix(); + cv::Mat T2 = c2.extrinsic.matrix(); + cv::Mat T = T2 * transform::inverse(T1); + + transform::getRotationAndTranslation(T, R, t); + cv::stereoRectify( K1, distCoeffs1, K2, distCoeffs2, size, R, t, + R1, R2, P1, P2, Q, flags, alpha, size, &roi1, &roi2); +} + +double StereoRectify::baseline() const { + return cv::norm(t); +} + +void StereoRectify::map1(cv::Mat &m1, cv::Mat &m2, int format) { + cv::initUndistortRectifyMap(K1, distCoeffs1, R1, P1, size, format, m1, m2); +} + +void StereoRectify::map2(cv::Mat &m1, cv::Mat &m2, int format) { + cv::initUndistortRectifyMap(K2, distCoeffs2, R2, P2, size, format, m1, m2); +} + +} +} \ No newline at end of file diff --git a/components/calibration/src/structures.cpp b/components/calibration/src/structures.cpp index 36d3ff4fe..8c2c9ec5f 100644 --- a/components/calibration/src/structures.cpp +++ b/components/calibration/src/structures.cpp @@ -187,6 +187,9 @@ CalibrationData CalibrationData::readFile(const std::string &path) { } CalibrationData calibration; fs["enabled"] >> calibration.enabled; + if (!fs["origin"].isNone()) { + fs["origin"] >> calibration.origin; + } for (auto it = fs["calibration"].begin(); it != fs["calibration"].end(); it++) { Calibration calib; @@ -203,7 +206,7 @@ CalibrationData CalibrationData::readFile(const std::string &path) { (*it)["rvec"] >> calib.extrinsic.rvec; (*it)["tvec"] >> calib.extrinsic.tvec; - calibration.data_[channel] = calib; + calibration.data[channel] = calib; } return calibration; @@ -216,8 +219,9 @@ void CalibrationData::writeFile(const std::string &path) const { } fs << "enabled" << enabled; + fs << "origin" << origin; fs << "calibration" << "["; - for (auto &[channel, data] : data_) { + for (auto &[channel, data] : data) { fs << "{" << "channel" << int(channel) << "resolution" << data.intrinsic.resolution @@ -237,9 +241,9 @@ void CalibrationData::writeFile(const std::string &path) const { } CalibrationData::Calibration& CalibrationData::get(ftl::codecs::Channel channel) { - return data_[channel]; + return data[channel]; } bool CalibrationData::hasCalibration(ftl::codecs::Channel channel) const { - return data_.count(channel) != 0; + return data.count(channel) != 0; } diff --git a/components/codecs/include/ftl/codecs/channels.hpp b/components/codecs/include/ftl/codecs/channels.hpp index b5a03877b..90e3d3cec 100644 --- a/components/codecs/include/ftl/codecs/channels.hpp +++ b/components/codecs/include/ftl/codecs/channels.hpp @@ -64,7 +64,8 @@ enum struct Channel : int { Transforms = 2050, // Transformation matrices for framesets Shapes3D = 2051, // Labeled 3D shapes Messages = 2052, // Vector of Strings - Touch = 2053 // List of touch data type (each touch point) + Touch = 2053, // List of touch data type (each touch point) + Pipelines = 2054, // List of pipline URIs that have been applied }; inline bool isVideo(Channel c) { return (int)c < 32; }; diff --git a/components/control/cpp/src/master.cpp b/components/control/cpp/src/master.cpp index d6a2f1cac..9e8b85e4c 100644 --- a/components/control/cpp/src/master.cpp +++ b/components/control/cpp/src/master.cpp @@ -57,7 +57,8 @@ Master::Master(Configurable *root, Universe *net) }); net->bind("get_configurable", [](const std::string &uri) -> std::string { - return ftl::config::find(uri)->getConfig().dump(); + auto *cfg = ftl::config::find(uri); + return (cfg) ? cfg->getConfig().dump() : "{}"; }); net->bind("list_configurables", []() { diff --git a/components/operators/src/operator.cpp b/components/operators/src/operator.cpp index 7eabcdc70..cdbeb0c00 100644 --- a/components/operators/src/operator.cpp +++ b/components/operators/src/operator.cpp @@ -8,6 +8,7 @@ using ftl::operators::Graph; using ftl::rgbd::Frame; using ftl::rgbd::FrameSet; using ftl::rgbd::Source; +using ftl::codecs::Channel; Operator::Operator(ftl::Configurable *config) : config_(config) { enabled_ = config_->value("enabled", true); @@ -64,6 +65,12 @@ bool Graph::apply(FrameSet &in, FrameSet &out, cudaStream_t stream) { return false; } + for (auto &f : out.frames) { + if (!f.hasOwn(Channel::Pipelines)) f.create<std::list<std::string>>(Channel::Pipelines); + auto pls = f.set<std::list<std::string>>(Channel::Pipelines); + pls = getID(); + } + for (auto &i : operators_) { if (i.instances.size() < 1) { i.instances.push_back(i.maker->make()); @@ -147,6 +154,10 @@ bool Graph::apply(Frame &in, Frame &out, cudaStream_t stream) { return false; } + if (!out.hasOwn(Channel::Pipelines)) out.create<std::list<std::string>>(Channel::Pipelines); + auto pls = out.set<std::list<std::string>>(Channel::Pipelines); + pls = getID(); + for (auto &i : operators_) { // Make sure there are enough instances if (i.instances.size() < 1) { diff --git a/components/renderers/cpp/include/ftl/render/overlay.hpp b/components/renderers/cpp/include/ftl/render/overlay.hpp index 945af363f..4bb01d799 100644 --- a/components/renderers/cpp/include/ftl/render/overlay.hpp +++ b/components/renderers/cpp/include/ftl/render/overlay.hpp @@ -26,7 +26,7 @@ class Overlay : public ftl::Configurable { //void apply(ftl::rgbd::FrameSet &fs, cv::Mat &out, ftl::rgbd::FrameState &state); - void draw(NVGcontext *, ftl::data::FrameSet &fs, ftl::rgbd::Frame &frame, const Eigen::Vector2f &, const Eigen::Vector2f &, const Eigen::Vector2f &offset); + void draw(NVGcontext *, ftl::data::FrameSet &fs, ftl::rgbd::Frame &frame, const Eigen::Vector2f &, const Eigen::Vector2f &, const Eigen::Vector2f &offset, const Eigen::Matrix4d &cursor); private: nanogui::GLShader oShader; diff --git a/components/renderers/cpp/src/overlay.cpp b/components/renderers/cpp/src/overlay.cpp index 864fbddef..611e9737c 100644 --- a/components/renderers/cpp/src/overlay.cpp +++ b/components/renderers/cpp/src/overlay.cpp @@ -244,7 +244,7 @@ void Overlay::_drawAxis(const Eigen::Matrix4d &pose, const Eigen::Vector3f &scal (const void *)(loffset * sizeof(uint32_t))); } -void Overlay::draw(NVGcontext *ctx, ftl::data::FrameSet &fs, ftl::rgbd::Frame &frame, const Eigen::Vector2f &screenSize, const Eigen::Vector2f &imageSize, const Eigen::Vector2f &offset) { +void Overlay::draw(NVGcontext *ctx, ftl::data::FrameSet &fs, ftl::rgbd::Frame &frame, const Eigen::Vector2f &screenSize, const Eigen::Vector2f &imageSize, const Eigen::Vector2f &offset, const Eigen::Matrix4d &cursor) { if (!value("enabled", false)) return; double zfar = 8.0f; @@ -333,6 +333,11 @@ void Overlay::draw(NVGcontext *ctx, ftl::data::FrameSet &fs, ftl::rgbd::Frame &f _drawAxis(frame.getPose().inverse(), Eigen::Vector3f(0.5f, 0.5f, 0.5f)); } + if (value("show_cursor", true)) { + _drawAxis(frame.getPose().inverse() * cursor.inverse(), Eigen::Vector3f(0.2f, 0.2f, 0.2f)); + _drawOutlinedShape(Shape::XZPLANE, frame.getPose().inverse() * cursor.inverse(), Eigen::Vector3f(0.05f, 0.05f, 0.05f), make_uchar4(200,200,200,50), make_uchar4(255,255,255,100)); + } + if (value("show_shapes", true)) { /*if (fs.hasChannel(Channel::Shapes3D)) { std::vector<ftl::codecs::Shape3D> shapes; diff --git a/components/rgbd-sources/include/ftl/rgbd/camera.hpp b/components/rgbd-sources/include/ftl/rgbd/camera.hpp index f68de82d0..3698a32ca 100644 --- a/components/rgbd-sources/include/ftl/rgbd/camera.hpp +++ b/components/rgbd-sources/include/ftl/rgbd/camera.hpp @@ -66,6 +66,8 @@ struct __align__(16) Camera { */ __host__ __device__ float3 screenToCam(int ux, int uy, float depth) const; + //Eigen::Vector4f eigenScreenToCam(int ux, int uy, float depth) const; + /** * Convert screen plus depth into camera coordinates. */ @@ -185,7 +187,7 @@ inline float3 ftl::rgbd::Camera::screenToCam(uint ux, uint uy, float depth) cons return make_float3(depth*x, depth*y, depth); } -__device__ +__device__ __host__ inline float3 ftl::rgbd::Camera::screenToCam(int ux, int uy, float depth) const { const float x = static_cast<float>(((float)ux+cx) / fx); const float y = static_cast<float>(((float)uy+cy) / fy); diff --git a/components/rgbd-sources/include/ftl/rgbd/frame.hpp b/components/rgbd-sources/include/ftl/rgbd/frame.hpp index 509c4bec8..e3a77ce2f 100644 --- a/components/rgbd-sources/include/ftl/rgbd/frame.hpp +++ b/components/rgbd-sources/include/ftl/rgbd/frame.hpp @@ -26,6 +26,10 @@ #include <Eigen/Eigen> namespace ftl { +namespace calibration { +struct CalibrationData; +} + namespace rgbd { //typedef ftl::data::Frame Frame; @@ -91,6 +95,9 @@ class Frame : public ftl::data::Frame { ftl::rgbd::Camera &setRight(); Eigen::Matrix4d &setPose(); + ftl::calibration::CalibrationData& setCalibration(); + const ftl::calibration::CalibrationData& getCalibration() const; + std::string serial() const; std::string device() const; diff --git a/components/rgbd-sources/src/camera.cpp b/components/rgbd-sources/src/camera.cpp index 3876fa1ed..a4f540ffd 100644 --- a/components/rgbd-sources/src/camera.cpp +++ b/components/rgbd-sources/src/camera.cpp @@ -65,3 +65,14 @@ Camera Camera::scaled(int width, int height) const { return newcam; } + +/*Eigen::Vector4f ftl::rgbd::Camera::eigenScreenToCam(int ux, int uy, float depth) const { + const float x = static_cast<float>(((float)ux+cx) / fx); + const float y = static_cast<float>(((float)uy+cy) / fy); + Eigen::Vector4f v; + v[0] = depth*x; + v[1] = depth*y; + v[2] = depth; + v[3] = 1.0f; + return v; +}*/ diff --git a/components/rgbd-sources/src/frame.cpp b/components/rgbd-sources/src/frame.cpp index 10bacd8f0..a4ace9e46 100644 --- a/components/rgbd-sources/src/frame.cpp +++ b/components/rgbd-sources/src/frame.cpp @@ -1,5 +1,6 @@ #include <ftl/rgbd/frame.hpp> +#include <ftl/calibration/structures.hpp> #define LOGURU_REPLACE_GLOG 1 #include <loguru.hpp> @@ -138,6 +139,14 @@ Eigen::Matrix4d &ftl::rgbd::Frame::setPose() { return this->create<Eigen::Matrix4d>(ftl::codecs::Channel::Pose); } +const ftl::calibration::CalibrationData& ftl::rgbd::Frame::getCalibration() const { + return this->get<ftl::calibration::CalibrationData>(Channel::CalibrationData); +} + +ftl::calibration::CalibrationData& ftl::rgbd::Frame::setCalibration() { + return this->create<ftl::calibration::CalibrationData>(Channel::CalibrationData); +} + std::string ftl::rgbd::Frame::serial() const { if (hasChannel(Channel::MetaData)) { const auto &meta = get<std::map<std::string,std::string>>(Channel::MetaData); @@ -199,4 +208,3 @@ cv::cuda::GpuMat &ftl::data::Frame::set<cv::cuda::GpuMat, 0>(ftl::codecs::Channe return set<ftl::rgbd::VideoFrame>(c).setGPU(); } - diff --git a/components/rgbd-sources/src/sources/stereovideo/rectification.cpp b/components/rgbd-sources/src/sources/stereovideo/rectification.cpp index e495ef263..39e91193c 100644 --- a/components/rgbd-sources/src/sources/stereovideo/rectification.cpp +++ b/components/rgbd-sources/src/sources/stereovideo/rectification.cpp @@ -69,14 +69,6 @@ void StereoRectification::updateCalibration_() { valid_ = false; - // create temporary buffers for rectification - if (tmp_l_.size() != image_resolution_) { - tmp_l_ = cv::Mat(image_resolution_, CV_8UC4); - } - if (tmp_l_.size() != image_resolution_) { - tmp_r_ = cv::Mat(image_resolution_, CV_8UC4); - } - // calculate rotation and translation from left to right using calibration cv::Mat T_l = calib_left_.extrinsic.matrix(); cv::Mat T_r = calib_right_.extrinsic.matrix(); @@ -106,7 +98,6 @@ void StereoRectification::calculateParameters_() { map_format_, map_l_.first, map_l_.second); cv::initUndistortRectifyMap(K_r, dc_r, R_r_, P_r_, image_resolution_, map_format_, map_r_.first, map_r_.second); - } void StereoRectification::rectify(cv::InputArray im, cv::OutputArray im_out, Channel c) { @@ -165,6 +156,7 @@ cv::Mat StereoRectification::getPose(Channel c) { } } else { + // unrectified pose not used anywhere (and isn't necessarily valid). if (c == Channel::Left) { return inverse(calib_left_.extrinsic.matrix()); } diff --git a/components/rgbd-sources/src/sources/stereovideo/rectification.hpp b/components/rgbd-sources/src/sources/stereovideo/rectification.hpp index 756747e04..edbb66fc4 100644 --- a/components/rgbd-sources/src/sources/stereovideo/rectification.hpp +++ b/components/rgbd-sources/src/sources/stereovideo/rectification.hpp @@ -96,9 +96,6 @@ private: std::pair<cv::Mat,cv::Mat> map_l_; std::pair<cv::Mat,cv::Mat> map_r_; - // temporary buffers for left/right - cv::Mat tmp_l_; - cv::Mat tmp_r_; }; } diff --git a/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp b/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp index ecd902da1..b572e0e4e 100644 --- a/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp +++ b/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp @@ -25,6 +25,7 @@ #include <ftl/rgbd/capabilities.hpp> #include <ftl/codecs/shapes.hpp> #include <ftl/calibration/structures.hpp> +#include <ftl/calibration/parameters.hpp> #include "stereovideo.hpp" #include "ftl/threads.hpp" @@ -231,13 +232,15 @@ void StereoVideoSource::updateParameters(ftl::rgbd::Frame &frame) { calibration_ = ftl::calibration::CalibrationData(change); rectification_->setCalibration(calibration_); rectification_->setEnabled(change.enabled); + + do_update_params_ = true; return true; }); if (lsrc_->isStereo()) { Eigen::Matrix4d pose; - cv::cv2eigen(rectification_->getPose(Channel::Left), pose); - + // NOTE: pose update (new origin/rotation) + cv::cv2eigen(calibration_.origin * rectification_->getPose(Channel::Left), pose); frame.setPose() = pose; cv::Mat K = rectification_->cameraMatrix(color_size_); @@ -270,14 +273,6 @@ void StereoVideoSource::updateParameters(ftl::rgbd::Frame &frame) { doff }; - CHECK(params.fx > 0) << "focal length must be positive"; - CHECK(params.fy > 0) << "focal length must be positive"; - CHECK(params.cx < 0) << "bad principal point coordinate (negative value)"; - CHECK(-params.cx < params.width) << "principal point must be inside image"; - CHECK(params.cy < 0) << "bad principal point coordinate (negative value)"; - CHECK(-params.cy < params.height) << "principal point must be inside image"; - CHECK(params.baseline >= 0.0) << "baseline must be positive"; - host_->getConfig()["focal"] = params.fx; host_->getConfig()["centre_x"] = params.cx; host_->getConfig()["centre_y"] = params.cy; @@ -313,6 +308,15 @@ void StereoVideoSource::updateParameters(ftl::rgbd::Frame &frame) { frame.setPose() = pose; } + + const auto& params = frame.getLeft(); + CHECK(params.fx > 0) << "focal length must be positive"; + CHECK(params.fy > 0) << "focal length must be positive"; + CHECK(params.cx < 0) << "bad principal point coordinate (negative value)"; + CHECK(-params.cx < params.width) << "principal point must be inside image"; + CHECK(params.cy < 0) << "bad principal point coordinate (negative value)"; + CHECK(-params.cy < params.height) << "principal point must be inside image"; + CHECK(params.baseline >= 0.0) << "baseline must be positive"; } bool StereoVideoSource::capture(int64_t ts) { diff --git a/components/rgbd-sources/test/CMakeLists.txt b/components/rgbd-sources/test/CMakeLists.txt index 4a4b0d2c5..16a83e404 100644 --- a/components/rgbd-sources/test/CMakeLists.txt +++ b/components/rgbd-sources/test/CMakeLists.txt @@ -6,7 +6,7 @@ $<TARGET_OBJECTS:CatchTest> ) target_include_directories(source_unit PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../include") target_link_libraries(source_unit - ftlcommon ftlcodecs ftlnet Eigen3::Eigen ftldata ${CUDA_LIBRARIES}) + ftlcommon ftlcalibration ftlcodecs ftlnet Eigen3::Eigen ftldata ${CUDA_LIBRARIES}) add_test(SourceUnitTest source_unit) diff --git a/components/streams/include/ftl/streams/feed.hpp b/components/streams/include/ftl/streams/feed.hpp index 62234701b..ac141a045 100644 --- a/components/streams/include/ftl/streams/feed.hpp +++ b/components/streams/include/ftl/streams/feed.hpp @@ -110,6 +110,9 @@ public: */ ftl::rgbd::Source *getRGBD(ftl::data::FrameID id); + std::list<ftl::Configurable*> getPipelines(ftl::data::FrameID id); + std::list<ftl::Configurable*> getOperators(ftl::data::FrameID id, const std::string &name); + void remove(const std::string &str); void remove(uint32_t id); diff --git a/components/streams/include/ftl/streams/renderer.hpp b/components/streams/include/ftl/streams/renderer.hpp index 441fd32ce..0ae3e7242 100644 --- a/components/streams/include/ftl/streams/renderer.hpp +++ b/components/streams/include/ftl/streams/renderer.hpp @@ -31,6 +31,8 @@ class Source : public ftl::Configurable, public ftl::data::DiscreteSource { ftl::audio::StereoMixerF<100> &mixer(); + ftl::stream::Feed::Filter *filter() const; + private: ftl::stream::Feed *feed_; ftl::render::BaseSourceImpl *impl_; diff --git a/components/streams/include/ftl/streams/sender.hpp b/components/streams/include/ftl/streams/sender.hpp index 8edbbf1d6..9e679167a 100644 --- a/components/streams/include/ftl/streams/sender.hpp +++ b/components/streams/include/ftl/streams/sender.hpp @@ -68,6 +68,7 @@ class Sender : public ftl::Configurable { int64_t injection_timestamp_=0; SHARED_MUTEX mutex_; std::atomic_flag do_inject_; + std::atomic_flag do_reinject_; //std::function<void(ftl::codecs::Channel, int, int)> state_cb_; ftl::stream::StreamCallback reqcb_; int add_iframes_; @@ -105,7 +106,7 @@ class Sender : public ftl::Configurable { float _selectFloatMax(ftl::codecs::Channel c); ftl::audio::Encoder *_getAudioEncoder(int fsid, int sid, ftl::codecs::Channel c, ftl::codecs::Packet &pkt); - void _sendPersistent(ftl::data::Frame &frame); + void _sendPersistent(ftl::data::FrameSet &fs); void _send(ftl::rgbd::FrameSet &fs, ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt); bool _checkNeedsIFrame(int64_t ts, bool injecting); diff --git a/components/streams/src/baserender.hpp b/components/streams/src/baserender.hpp index 51c6ce09c..55082964f 100644 --- a/components/streams/src/baserender.hpp +++ b/components/streams/src/baserender.hpp @@ -30,6 +30,8 @@ class BaseSourceImpl { inline ftl::audio::StereoMixerF<100> &mixer() { return mixer_; } + virtual ftl::stream::Feed::Filter *filter()=0; + protected: ftl::render::Source *host_; ftl::audio::StereoMixerF<100> mixer_; diff --git a/components/streams/src/renderer.cpp b/components/streams/src/renderer.cpp index 439ea2f2e..66d3add2b 100644 --- a/components/streams/src/renderer.cpp +++ b/components/streams/src/renderer.cpp @@ -32,6 +32,11 @@ ftl::audio::StereoMixerF<100> &Source::mixer() { return impl_->mixer(); } +ftl::stream::Feed::Filter *Source::filter() const { + if (!impl_) return nullptr; + return impl_->filter(); +} + bool Source::supports(const std::string &puri) { ftl::URI uri(puri); if (!uri.isValid() || uri.getScheme() != ftl::URI::SCHEME_DEVICE) return false; diff --git a/components/streams/src/renderers/openvr_render.hpp b/components/streams/src/renderers/openvr_render.hpp index 3907b9c3e..d07383998 100644 --- a/components/streams/src/renderers/openvr_render.hpp +++ b/components/streams/src/renderers/openvr_render.hpp @@ -32,6 +32,8 @@ class OpenVRRender : public ftl::render::BaseSourceImpl { static bool supported(); + ftl::stream::Feed::Filter *filter() override { return filter_; } + private: ftl::stream::Feed *feed_; ftl::stream::Feed::Filter *filter_; diff --git a/components/streams/src/renderers/screen_render.hpp b/components/streams/src/renderers/screen_render.hpp index 91a867cf0..fedc65ad4 100644 --- a/components/streams/src/renderers/screen_render.hpp +++ b/components/streams/src/renderers/screen_render.hpp @@ -26,6 +26,8 @@ class ScreenRender : public ftl::render::BaseSourceImpl { bool isReady() override; + ftl::stream::Feed::Filter *filter() override { return filter_; } + private: ftl::stream::Feed *feed_; ftl::stream::Feed::Filter *filter_; diff --git a/components/streams/src/sender.cpp b/components/streams/src/sender.cpp index fba9c922a..62e48bd0b 100644 --- a/components/streams/src/sender.cpp +++ b/components/streams/src/sender.cpp @@ -25,6 +25,7 @@ using ftl::stream::injectConfig; Sender::Sender(nlohmann::json &config) : ftl::Configurable(config), stream_(nullptr) { do_inject_.test_and_set(); + do_reinject_.test_and_set(); iframe_ = 1; add_iframes_ = value("iframes", 50); timestamp_ = -1; @@ -104,13 +105,20 @@ static void writeValue(std::vector<unsigned char> &data, T value) { data.insert(data.end(), pvalue_start, pvalue_start+sizeof(T)); } -void Sender::_sendPersistent(ftl::data::Frame &frame) { - auto *session = frame.parent(); - if (session) { - for (auto c : session->channels()) { - Sender::post(frame, c); +void Sender::_sendPersistent(ftl::rgbd::FrameSet &fs) { + std::unordered_set<ftl::codecs::Channel> persist_chan; + + for (auto &frame : fs.frames) { + auto *session = frame.parent(); + if (session) { + auto chans = session->channels(); + persist_chan.insert(chans.begin(), chans.end()); } } + + for (auto c : persist_chan) { + post(fs, c); + } } void Sender::fakePost(ftl::data::FrameSet &fs, ftl::codecs::Channel c) { @@ -174,6 +182,10 @@ void Sender::post(ftl::data::FrameSet &fs, ftl::codecs::Channel c, bool noencode pkt.packet_count = static_cast<uint8_t>(fs.frames[i].packet_tx+1); // FIXME: 255 limit currently _send(fs, spkt, pkt); } + + if (injection_timestamp_ >= spkt.timestamp) { + do_reinject_.clear(); + } return; } @@ -182,6 +194,9 @@ void Sender::post(ftl::data::FrameSet &fs, ftl::codecs::Channel c, bool noencode bool do_inject = !do_inject_.test_and_set(); bool do_iframe = _checkNeedsIFrame(fs.timestamp(), do_inject); + if (!do_reinject_.test_and_set()) { + do_inject = true; + } FTL_Profile("SenderPost", 0.02); @@ -192,6 +207,10 @@ void Sender::post(ftl::data::FrameSet &fs, ftl::codecs::Channel c, bool noencode //int ccount = 0; int forward_count = 0; + if (do_inject) { + _sendPersistent(fs); + } + for (size_t i=0; i<fs.frames.size(); ++i) { auto &frame = fs.frames[i]; if (!frame.has(c)) continue; @@ -199,11 +218,6 @@ void Sender::post(ftl::data::FrameSet &fs, ftl::codecs::Channel c, bool noencode ++valid_frames; //++fs.flush_count; - // TODO: Send entire persistent session on inject - if (do_inject) { - _sendPersistent(frame); - } - //ccount += frame.changed().size(); if (selected.find(c) != selected.end() || (int)c >= 32) { diff --git a/components/structures/include/ftl/data/new_frame.hpp b/components/structures/include/ftl/data/new_frame.hpp index 6bbd61bc2..94c539086 100644 --- a/components/structures/include/ftl/data/new_frame.hpp +++ b/components/structures/include/ftl/data/new_frame.hpp @@ -930,9 +930,25 @@ T &ftl::data::Frame::set(ftl::codecs::Channel c) { auto i = data_.find(c); if (i != data_.end()) { if (i->second.status != ftl::data::ChannelStatus::FLUSHED) { - i->second.encoded.clear(); + + auto &d = i->second; + if (d.status != ftl::data::ChannelStatus::INVALID && !d.data.has_value() && d.encoded.size() > 0) { + UNIQUE_LOCK(parent_->mutex(), lk); + if (!d.data.has_value()) { + // Do a decode now and change the status + //d.status = ftl::data::ChannelStatus::DISPATCHED; + + try { + decode_type<T>(d.data, d.encoded.front().data); + } catch (...) { + throw FTL_Error("Decode failure for channel " << int(c)); + } + } + } + + d.encoded.clear(); touch(c); - return *std::any_cast<T>(&i->second.data); + return *std::any_cast<T>(&d.data); } else { throw FTL_Error("Channel is flushed and read-only: " << static_cast<unsigned int>(c)); } @@ -952,6 +968,22 @@ ftl::data::Aggregator<T> ftl::data::Frame::set(ftl::codecs::Channel c) { auto i = data_.find(c); if (i != data_.end()) { if (i->second.status != ftl::data::ChannelStatus::FLUSHED) { + + auto &d = i->second; + if (d.status != ftl::data::ChannelStatus::INVALID && !d.data.has_value() && d.encoded.size() > 0) { + UNIQUE_LOCK(parent_->mutex(), lk); + if (!d.data.has_value()) { + // Do a decode now and change the status + //d.status = ftl::data::ChannelStatus::DISPATCHED; + + try { + decode_type<T>(d.data, d.encoded.front().data); + } catch (...) { + throw FTL_Error("Decode failure for channel " << int(c)); + } + } + } + i->second.encoded.clear(); touch(c); return ftl::data::Aggregator<T>{*std::any_cast<T>(&i->second.data), isAggregate(c)}; -- GitLab