diff --git a/applications/gui2/src/modules/camera.cpp b/applications/gui2/src/modules/camera.cpp index 670695023f9cbcb04cacd6326cf21e115e6d7300..36e0e37793c1a66dc356e121166820af3ad7706c 100644 --- a/applications/gui2/src/modules/camera.cpp +++ b/applications/gui2/src/modules/camera.cpp @@ -5,6 +5,10 @@ #include <ftl/rgbd/capabilities.hpp> #include <chrono> +#include <opencv2/imgproc.hpp> +#include <opencv2/imgcodecs.hpp> +#include <opencv2/cudaarithm.hpp> + #include <loguru.hpp> using ftl::gui2::Camera; @@ -341,6 +345,19 @@ void Camera::startStreaming(const std::unordered_set<ftl::codecs::Channel> &chan io->feed()->startStreaming(filter); } +void Camera::snapshot(const std::string &filename) { + auto ptr = std::atomic_load(&latest_); + if (ptr) { + auto &frame = ptr->frames[frame_idx]; + if (frame.hasChannel(channel)) { + const auto &snap = frame.get<cv::Mat>(channel); + cv::Mat output; + cv::cvtColor(snap, output, cv::COLOR_BGRA2BGR); + cv::imwrite(filename, output); + } + } +} + ftl::cuda::TextureObject<uchar4>& Camera::getFrame() { if (std::atomic_load(¤t_fs_)) { auto& frame = current_fs_->frames[frame_idx].cast<ftl::rgbd::Frame>(); diff --git a/applications/gui2/src/modules/camera.hpp b/applications/gui2/src/modules/camera.hpp index 16bf9f1b44751382d911ecac0aba66f8524122dd..7405c8c5091dda95b43fdfd44dd6d594b4ff8193 100644 --- a/applications/gui2/src/modules/camera.hpp +++ b/applications/gui2/src/modules/camera.hpp @@ -61,6 +61,8 @@ public: void startRecording(const std::string &filename, const std::unordered_set<ftl::codecs::Channel> &channels); void startStreaming(const std::unordered_set<ftl::codecs::Channel> &channels); + void snapshot(const std::string &filename); + private: int frame_idx = -1; ftl::data::FrameID frame_id_; diff --git a/applications/gui2/src/views/camera.cpp b/applications/gui2/src/views/camera.cpp index 04a14afde483d5e0801884bda4f1a34773fa7aa6..565381ebc11f6512d6f12c8b0e9a1e92db4eec5c 100644 --- a/applications/gui2/src/views/camera.cpp +++ b/applications/gui2/src/views/camera.cpp @@ -377,6 +377,29 @@ CameraView::CameraView(ftl::gui2::Screen* parent, ftl::gui2::Camera* ctrl) : imview_->setCursor(nanogui::Cursor::Crosshair); mod->setCursor(nanogui::Cursor::Crosshair); } + + auto theme = dynamic_cast<ftl::gui2::Screen*>(screen())->getTheme("toolbutton"); + //this->setTheme(theme); + + context_menu_ = new nanogui::Window(parent, ""); + context_menu_->setVisible(false); + context_menu_->setLayout(new nanogui::BoxLayout(nanogui::Orientation::Vertical)); + context_menu_->setTheme(theme); + + auto *button = new nanogui::Button(context_menu_, "Capture Image"); + button->setCallback([this]() { + char timestamp[18]; + std::time_t t=std::time(NULL); + std::strftime(timestamp, sizeof(timestamp), "%F-%H%M%S", std::localtime(&t)); + context_menu_->setVisible(false); + ctrl_->snapshot(std::string(timestamp)+std::string(".png")); + }); + + button = new nanogui::Button(context_menu_, "Settings"); + button->setCallback([this, button]() { + context_menu_->setVisible(false); + ctrl_->screen->getModule<ftl::gui2::ConfigCtrl>()->show(ctrl_->getID()); + }); } CameraView::~CameraView() { @@ -385,6 +408,11 @@ CameraView::~CameraView() { // should be fixed in nanogui panel_->dispose(); } + + if (context_menu_->parent()->getRefCount() > 0) { + context_menu_->setVisible(false); + context_menu_->dispose(); + } } void CameraView::refresh() { @@ -434,7 +462,16 @@ bool CameraView::mouseButtonEvent(const Eigen::Vector2i &p, int button, bool dow 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); } + context_menu_->setVisible(false); return true; + } else if (button == 1) { + if (!down) { + context_menu_->setPosition(p - mPos); + context_menu_->setVisible(true); + return true; + } + } else { + context_menu_->setVisible(false); } return false; } diff --git a/applications/gui2/src/views/camera.hpp b/applications/gui2/src/views/camera.hpp index 6965a2903d07ecd916acc69e8e2a00023493412e..efeb4221b177b0a4bc15066ce5b5ad8160a7a480 100644 --- a/applications/gui2/src/views/camera.hpp +++ b/applications/gui2/src/views/camera.hpp @@ -34,6 +34,7 @@ protected: Camera* ctrl_; MediaPanel* panel_; FTLImageView* imview_; + nanogui::Window *context_menu_; public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW diff --git a/applications/gui2/src/views/camera3d.cpp b/applications/gui2/src/views/camera3d.cpp index 06e6e2400433ae81d1ad6a70e4ecc1c824868f17..9d1c0fea298ba1e51380cf2691455d5a83b6be52 100644 --- a/applications/gui2/src/views/camera3d.cpp +++ b/applications/gui2/src/views/camera3d.cpp @@ -65,8 +65,7 @@ bool CameraView3D::keyboardEvent(int key, int scancode, int action, int modifier } bool CameraView3D::mouseButtonEvent(const Eigen::Vector2i &p, int button, bool down, int modifiers) { - LOG(INFO) << "mouseButtonEvent: " << p; - return true; + return CameraView::mouseButtonEvent(p, button, down, modifiers); } bool CameraView3D::mouseMotionEvent(const Eigen::Vector2i &p, const Eigen::Vector2i &rel, int button, int modifiers) { diff --git a/components/streams/src/feed.cpp b/components/streams/src/feed.cpp index 0eeadf3d309dc3ee01cc47c1ea28704046a89074..8a2eb4a2840a95f726b0b6db77d2534da943cb90 100644 --- a/components/streams/src/feed.cpp +++ b/components/streams/src/feed.cpp @@ -65,13 +65,13 @@ std::list<ftl::data::FrameSetPtr> Feed::Filter::getLatestFrameSets() { SHARED_LOCK(feed_->mtx_, lk); if (sources_.empty()) { for (auto &i : feed_->latest_) { - results.emplace_back(std::atomic_load(&(i.second))); + if (i.second) results.emplace_back(std::atomic_load(&(i.second))); } } else { for (auto &s : sources_) { auto i = feed_->latest_.find(s); if (i != feed_->latest_.end()) { - results.emplace_back(std::atomic_load(&(i->second))); + if (i->second) results.emplace_back(std::atomic_load(&(i->second))); } } } @@ -181,10 +181,10 @@ Feed::Feed(nlohmann::json &config, ftl::net::Universe*net) : pre_pipelines_[fs->frameset()]->apply(*fs, *fs, 0); } - // FIXME: Adding new to latest_ will modify data structure in non-threadsafe way! - std::atomic_store(&latest_[fs->frameset()], fs); - SHARED_LOCK(mtx_, lk); + + std::atomic_store(&latest_.at(fs->frameset()), fs); + for (auto* filter : filters_) { // TODO: smarter update (update only when changed) instead of // filter->channels_available_ = fs->channels(); @@ -603,6 +603,7 @@ std::string Feed::getName(const std::string &puri) { void Feed::add(uint32_t fsid, const std::string &uri, ftl::stream::Stream* stream) { fsid_lookup_[uri] = fsid; + latest_[fsid] = nullptr; streams_[fsid].push_back(stream); _createPipeline(fsid); @@ -677,6 +678,7 @@ uint32_t Feed::add(const std::string &path) { // Make the source object ftl::data::DiscreteSource *source; + latest_[fsid] = nullptr; lk.unlock(); if (uri.getBaseURI() == "device:render" || uri.getBaseURI() == "device:openvr") { @@ -768,7 +770,8 @@ uint32_t Feed::getID(const std::string &source) { const std::unordered_set<Channel> Feed::availableChannels(ftl::data::FrameID id) { ftl::data::FrameSetPtr fs; - std::atomic_store(&fs, latest_[id.frameset()]); + // FIXME: Should this be locked? + std::atomic_store(&fs, latest_.at(id.frameset())); if (fs && fs->hasFrame(id.source())) { return (*fs.get())[id.source()].allChannels(); } @@ -780,8 +783,10 @@ std::vector<ftl::data::FrameID> Feed::listFrames() { SHARED_LOCK(mtx_, lk); result.reserve(fsid_lookup_.size()); for (const auto [k, fs] : latest_) { - for (unsigned i = 0; i < fs->frames.size(); i++) { - result.push_back(ftl::data::FrameID(k, i)); + if (fs) { + for (unsigned i = 0; i < fs->frames.size(); i++) { + result.push_back(ftl::data::FrameID(k, i)); + } } } return result; @@ -817,8 +822,9 @@ std::vector<unsigned int> Feed::listFrameSets() { std::vector<unsigned int> result; result.reserve(fsid_lookup_.size()); for (const auto [k, fs] : latest_) { - std::ignore = fs; - result.push_back(k); + if (fs) { + result.push_back(k); + } } return result; }