From bb78a21b3c71a38f975a9147b1bb67d62ef1ba91 Mon Sep 17 00:00:00 2001 From: Nicolas Pope <nicolas.pope@utu.fi> Date: Sun, 30 Jun 2019 19:37:34 +0300 Subject: [PATCH] Implements #112 thumbnails --- applications/gui/CMakeLists.txt | 1 + applications/gui/src/gltexture.cpp | 13 +- applications/gui/src/gltexture.hpp | 1 + applications/gui/src/media_panel.cpp | 19 -- applications/gui/src/screen.cpp | 21 +- applications/gui/src/src_window.cpp | 69 +++++- applications/gui/src/src_window.hpp | 9 + applications/gui/src/thumbview.cpp | 32 +++ applications/gui/src/thumbview.hpp | 29 +++ .../include/ftl/virtual_source.hpp | 2 +- .../reconstruct/src/virtual_source.cpp | 2 +- .../include/ftl/rgbd/detail/source.hpp | 6 +- .../rgbd-sources/include/ftl/rgbd/source.hpp | 1 + .../include/ftl/rgbd/streamer.hpp | 8 +- .../rgbd-sources/src/bitrate_settings.hpp | 32 +++ components/rgbd-sources/src/image.hpp | 2 +- .../rgbd-sources/src/middlebury_source.cpp | 2 +- .../rgbd-sources/src/middlebury_source.hpp | 2 +- components/rgbd-sources/src/net.cpp | 52 ++-- components/rgbd-sources/src/net.hpp | 4 +- .../rgbd-sources/src/realsense_source.cpp | 2 +- .../rgbd-sources/src/realsense_source.hpp | 2 +- .../rgbd-sources/src/snapshot_source.hpp | 2 +- components/rgbd-sources/src/source.cpp | 21 +- components/rgbd-sources/src/stereovideo.cpp | 2 +- components/rgbd-sources/src/stereovideo.hpp | 2 +- components/rgbd-sources/src/streamer.cpp | 231 +++++++----------- components/rgbd-sources/test/source_unit.cpp | 12 +- 28 files changed, 358 insertions(+), 223 deletions(-) create mode 100644 applications/gui/src/thumbview.cpp create mode 100644 applications/gui/src/thumbview.hpp create mode 100644 components/rgbd-sources/src/bitrate_settings.hpp diff --git a/applications/gui/CMakeLists.txt b/applications/gui/CMakeLists.txt index cda2b89d2..baffb9bdd 100644 --- a/applications/gui/CMakeLists.txt +++ b/applications/gui/CMakeLists.txt @@ -12,6 +12,7 @@ set(GUISRC src/gltexture.cpp src/camera.cpp src/media_panel.cpp + src/thumbview.cpp ) add_executable(ftl-gui ${GUISRC}) diff --git a/applications/gui/src/gltexture.cpp b/applications/gui/src/gltexture.cpp index 752894a3c..8b36e848f 100644 --- a/applications/gui/src/gltexture.cpp +++ b/applications/gui/src/gltexture.cpp @@ -14,20 +14,21 @@ GLTexture::~GLTexture() { } void GLTexture::update(cv::Mat &m) { + if (m.rows == 0) return; if (glid_ == std::numeric_limits<unsigned int>::max()) { glGenTextures(1, &glid_); glBindTexture(GL_TEXTURE_2D, glid_); - cv::Mat m(cv::Size(100,100), CV_8UC3); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, m.cols, m.rows, 0, GL_RGB, GL_UNSIGNED_BYTE, m.data); + //cv::Mat m(cv::Size(100,100), CV_8UC3); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, m.cols, m.rows, 0, GL_BGR, GL_UNSIGNED_BYTE, m.data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } else { + glBindTexture(GL_TEXTURE_2D, glid_); + // TODO Allow for other formats + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, m.cols, m.rows, 0, GL_BGR, GL_UNSIGNED_BYTE, m.data); } - if (m.rows == 0) return; - glBindTexture(GL_TEXTURE_2D, glid_); - // TODO Allow for other formats - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, m.cols, m.rows, 0, GL_BGR, GL_UNSIGNED_BYTE, m.data); auto err = glGetError(); if (err != 0) LOG(ERROR) << "OpenGL Texture error: " << err; } diff --git a/applications/gui/src/gltexture.hpp b/applications/gui/src/gltexture.hpp index 4fb0bdaef..f83eabea2 100644 --- a/applications/gui/src/gltexture.hpp +++ b/applications/gui/src/gltexture.hpp @@ -13,6 +13,7 @@ class GLTexture { void update(cv::Mat &m); unsigned int texture() const { return glid_; } + bool isValid() const { return glid_ != std::numeric_limits<unsigned int>::max(); } private: unsigned int glid_; diff --git a/applications/gui/src/media_panel.cpp b/applications/gui/src/media_panel.cpp index 2e0f5fae2..84b45e9fc 100644 --- a/applications/gui/src/media_panel.cpp +++ b/applications/gui/src/media_panel.cpp @@ -26,25 +26,6 @@ MediaPanel::MediaPanel(ftl::gui::Screen *screen) : nanogui::Window(screen, ""), //setFixedSize(size); setPosition(Vector2i(screen->width() / 2 - size[0]/2, screen->height() - 30 - size[1])); - Theme *mediatheme = new Theme(*theme()); - mediatheme->mIconScale = 1.2f; - mediatheme->mWindowDropShadowSize = 0; - mediatheme->mWindowFillFocused = nanogui::Color(45, 150); - mediatheme->mWindowFillUnfocused = nanogui::Color(45, 80); - mediatheme->mButtonGradientTopUnfocused = nanogui::Color(0,0); - mediatheme->mButtonGradientBotUnfocused = nanogui::Color(0,0); - mediatheme->mButtonGradientTopFocused = nanogui::Color(80,230); - mediatheme->mButtonGradientBotFocused = nanogui::Color(80,230); - mediatheme->mIconColor = nanogui::Color(255,255); - mediatheme->mTextColor = nanogui::Color(1.0f,1.0f,1.0f,1.0f); - mediatheme->mBorderDark = nanogui::Color(0,0); - mediatheme->mBorderMedium = nanogui::Color(0,0); - mediatheme->mBorderLight = nanogui::Color(0,0); - mediatheme->mDropShadow = nanogui::Color(0,0); - mediatheme->mButtonFontSize = 30; - - setTheme(mediatheme); - auto button = new Button(this, "", ENTYPO_ICON_EDIT); button->setTooltip("Edit camera properties"); button->setCallback([this]() { diff --git a/applications/gui/src/screen.cpp b/applications/gui/src/screen.cpp index 00d46ee58..9f3d03bdf 100644 --- a/applications/gui/src/screen.cpp +++ b/applications/gui/src/screen.cpp @@ -72,6 +72,24 @@ ftl::gui::Screen::Screen(ftl::Configurable *proot, ftl::net::Universe *pnet, ftl toolbuttheme->mButtonGradientTopPushed = nanogui::Color(60,180); toolbuttheme->mButtonGradientBotPushed = nanogui::Color(60,180); + Theme *mediatheme = new Theme(*theme()); + mediatheme->mIconScale = 1.2f; + mediatheme->mWindowDropShadowSize = 0; + mediatheme->mWindowFillFocused = nanogui::Color(45, 150); + mediatheme->mWindowFillUnfocused = nanogui::Color(45, 80); + mediatheme->mButtonGradientTopUnfocused = nanogui::Color(0,0); + mediatheme->mButtonGradientBotUnfocused = nanogui::Color(0,0); + mediatheme->mButtonGradientTopFocused = nanogui::Color(80,230); + mediatheme->mButtonGradientBotFocused = nanogui::Color(80,230); + mediatheme->mIconColor = nanogui::Color(255,255); + mediatheme->mTextColor = nanogui::Color(1.0f,1.0f,1.0f,1.0f); + mediatheme->mBorderDark = nanogui::Color(0,0); + mediatheme->mBorderMedium = nanogui::Color(0,0); + mediatheme->mBorderLight = nanogui::Color(0,0); + mediatheme->mDropShadow = nanogui::Color(0,0); + mediatheme->mButtonFontSize = 30; + mediatheme->mStandardFontSize = 20; + windowtheme = new Theme(*theme()); windowtheme->mWindowFillFocused = nanogui::Color(220, 200); windowtheme->mWindowFillUnfocused = nanogui::Color(220, 200); @@ -196,6 +214,7 @@ ftl::gui::Screen::Screen(ftl::Configurable *proot, ftl::net::Universe *pnet, ftl swindow_ = new ftl::gui::SourceWindow(this); mwindow_ = new ftl::gui::MediaPanel(this); mwindow_->setVisible(false); + mwindow_->setTheme(mediatheme); cwindow_->setPosition(Eigen::Vector2i(80, 20)); //swindow_->setPosition(Eigen::Vector2i(80, 400)); @@ -203,7 +222,7 @@ ftl::gui::Screen::Screen(ftl::Configurable *proot, ftl::net::Universe *pnet, ftl swindow_->setVisible(true); swindow_->center(); cwindow_->setTheme(windowtheme); - swindow_->setTheme(windowtheme); + swindow_->setTheme(mediatheme); mShader.init("RGBDShader", defaultImageViewVertexShader, defaultImageViewFragmentShader); diff --git a/applications/gui/src/src_window.cpp b/applications/gui/src/src_window.cpp index 124285573..c654a9877 100644 --- a/applications/gui/src/src_window.cpp +++ b/applications/gui/src/src_window.cpp @@ -12,11 +12,14 @@ #include <nanogui/glutil.h> #include <nanogui/screen.h> #include <nanogui/layout.h> +#include <nanogui/vscrollpanel.h> #ifdef HAVE_LIBARCHIVE #include "ftl/rgbd/snapshot.hpp" #endif +#include "thumbview.hpp" + using ftl::gui::SourceWindow; using ftl::gui::Screen; using ftl::rgbd::Source; @@ -25,7 +28,7 @@ using ftl::config::json_t; SourceWindow::SourceWindow(ftl::gui::Screen *screen) : nanogui::Window(screen, ""), screen_(screen) { - setLayout(new nanogui::GroupLayout()); + setLayout(new nanogui::BoxLayout(nanogui::Orientation::Vertical, nanogui::Alignment::Fill, 20, 5)); using namespace nanogui; @@ -39,24 +42,33 @@ SourceWindow::SourceWindow(ftl::gui::Screen *screen) // tools->setLayout(new BoxLayout(Orientation::Horizontal, // Alignment::Middle, 0, 6)); - new Label(this, "Select source","sans-bold"); + auto label = new Label(this, "Select Camera","sans-bold",20); + available_ = screen_->control()->getNet()->findAll<string>("list_streams"); - auto select = new ComboBox(this, available_); - select->setCallback([this,select](int ix) { + //auto select = new ComboBox(this, available_); + //select->setCallback([this,select](int ix) { //src_->set("uri", available_[ix]); // TODO(Nick) Check camera exists first - screen_->setActiveCamera(cameras_[available_[ix]]); - LOG(INFO) << "Change source: " << ix; - }); + // screen_->setActiveCamera(cameras_[available_[ix]]); + // LOG(INFO) << "Change source: " << ix; + //}); - _updateCameras(); + auto vscroll = new VScrollPanel(this); + ipanel_ = new Widget(vscroll); + ipanel_->setLayout(new GridLayout(nanogui::Orientation::Horizontal, 2, + nanogui::Alignment::Middle, 0, 5)); + //ipanel_ = new ImageView(vscroll, 0); - screen->net()->onConnect([this,select](ftl::net::Peer *p) { + screen->net()->onConnect([this](ftl::net::Peer *p) { + UNIQUE_LOCK(mutex_, lk); available_ = screen_->net()->findAll<string>("list_streams"); - select->setItems(available_); + //select->setItems(available_); _updateCameras(); }); + UNIQUE_LOCK(mutex_, lk); + _updateCameras(); + /*new Label(this, "Source Options","sans-bold"); auto tools = new Widget(this); @@ -118,6 +130,11 @@ SourceWindow::SourceWindow(ftl::gui::Screen *screen) } void SourceWindow::_updateCameras() { + refresh_thumbs_ = true; + if (thumbs_.size() != available_.size()) { + thumbs_.resize(available_.size()); + } + for (auto s : available_) { if (cameras_.find(s) == cameras_.end()) { json_t srcjson; @@ -126,6 +143,8 @@ void SourceWindow::_updateCameras() { std::vector<ftl::rgbd::Source*> srcs = ftl::createArray<ftl::rgbd::Source>(screen_->root(), "sources", screen_->net()); auto *src = srcs[srcs.size()-1]; + LOG(INFO) << "Making camera: " << src->getURI(); + auto *cam = new ftl::gui::Camera(screen_, src); cameras_[s] = cam; } else { @@ -137,3 +156,33 @@ void SourceWindow::_updateCameras() { SourceWindow::~SourceWindow() { } + +void SourceWindow::draw(NVGcontext *ctx) { + if (refresh_thumbs_) { + UNIQUE_LOCK(mutex_, lk); + //refresh_thumbs_ = false; + + for (size_t i=0; i<thumbs_.size(); ++i) { + cv::Mat t; + auto *cam = cameras_[available_[i]]; + if (cam) { + if (cam->source()->thumbnail(t)) { + thumbs_[i].update(t); + } else { + refresh_thumbs_ = true; + } + } + + if (ipanel_->childCount() < i+1) { + new ftl::gui::ThumbView(ipanel_, screen_, cam); + } + if (thumbs_[i].isValid()) dynamic_cast<nanogui::ImageView*>(ipanel_->childAt(i))->bindImage(thumbs_[i].texture()); + } + + // TODO(Nick) remove excess image views + + center(); + } + + nanogui::Window::draw(ctx); +} diff --git a/applications/gui/src/src_window.hpp b/applications/gui/src/src_window.hpp index 77f529c8c..de14d0d05 100644 --- a/applications/gui/src/src_window.hpp +++ b/applications/gui/src/src_window.hpp @@ -2,12 +2,15 @@ #define _FTL_GUI_SRCWINDOW_HPP_ #include <nanogui/window.h> +#include <nanogui/imageview.h> #include <ftl/master.hpp> #include <ftl/uuid.hpp> #include <ftl/rgbd/source.hpp> +#include <ftl/threads.hpp> #include <vector> #include <map> #include <string> +#include "gltexture.hpp" class VirtualCameraView; @@ -24,10 +27,16 @@ class SourceWindow : public nanogui::Window { const std::vector<ftl::gui::Camera*> &getCameras(); + virtual void draw(NVGcontext *ctx); + private: ftl::gui::Screen *screen_; std::map<std::string, ftl::gui::Camera*> cameras_; std::vector<std::string> available_; + std::vector<GLTexture> thumbs_; + bool refresh_thumbs_; + nanogui::Widget *ipanel_; + std::mutex mutex_; void _updateCameras(); diff --git a/applications/gui/src/thumbview.cpp b/applications/gui/src/thumbview.cpp new file mode 100644 index 000000000..4b2d03a75 --- /dev/null +++ b/applications/gui/src/thumbview.cpp @@ -0,0 +1,32 @@ +#include "thumbview.hpp" +#include "screen.hpp" +#include "camera.hpp" + +using ftl::gui::ThumbView; +using ftl::gui::Screen; +using ftl::gui::Camera; + +ThumbView::ThumbView(nanogui::Widget *parent, ftl::gui::Screen *screen, ftl::gui::Camera *cam) + : ImageView(parent, 0), screen_(screen), cam_(cam) { + setCursor(nanogui::Cursor::Hand); +} + +ThumbView::~ThumbView() { + +} + +bool ThumbView::mouseButtonEvent(const nanogui::Vector2i &p, int button, bool down, int modifiers) { + if (button == 0 && !down) { + screen_->setActiveCamera(cam_); + } +} + +void ThumbView::draw(NVGcontext *ctx) { + ImageView::draw(ctx); + + nvgScissor(ctx, mPos.x(), mPos.y(), mSize.x(), mSize.y()); + nvgFontSize(ctx, 14); + nvgFontFace(ctx, "sans-bold"); + nvgText(ctx, mPos.x() + 10, mPos.y()+mSize.y() - 10, cam_->source()->getURI().c_str(), NULL); + nvgResetScissor(ctx); +} diff --git a/applications/gui/src/thumbview.hpp b/applications/gui/src/thumbview.hpp new file mode 100644 index 000000000..9bbac8097 --- /dev/null +++ b/applications/gui/src/thumbview.hpp @@ -0,0 +1,29 @@ +#ifndef _FTL_GUI_THUMBVIEW_HPP_ +#define _FTL_GUI_THUMBVIEW_HPP_ + +#include <nanogui/imageview.h> + +namespace ftl { +namespace gui { + +class Screen; +class Camera; + +class ThumbView : public nanogui::ImageView { + public: + ThumbView(nanogui::Widget *parent, ftl::gui::Screen *screen, ftl::gui::Camera *cam); + ~ThumbView(); + + bool mouseButtonEvent(const nanogui::Vector2i &p, int button, bool down, int modifiers); + + void draw(NVGcontext *ctx); + + private: + Screen *screen_; + Camera *cam_; +}; + +} +} + +#endif // _FTL_GUI_THUMBVIEW_HPP_ diff --git a/applications/reconstruct/include/ftl/virtual_source.hpp b/applications/reconstruct/include/ftl/virtual_source.hpp index ff1a89d78..931bdd5eb 100644 --- a/applications/reconstruct/include/ftl/virtual_source.hpp +++ b/applications/reconstruct/include/ftl/virtual_source.hpp @@ -26,7 +26,7 @@ class VirtualSource : public ftl::rgbd::detail::Source { void setScene(ftl::voxhash::SceneRep *); - bool grab(); + bool grab(int n, int b); //void getRGBD(cv::Mat &rgb, cv::Mat &depth); bool isReady(); diff --git a/applications/reconstruct/src/virtual_source.cpp b/applications/reconstruct/src/virtual_source.cpp index 46eca475e..cf6d268c1 100644 --- a/applications/reconstruct/src/virtual_source.cpp +++ b/applications/reconstruct/src/virtual_source.cpp @@ -53,7 +53,7 @@ void VirtualSource::setScene(ftl::voxhash::SceneRep *scene) { scene_ = scene; } -bool VirtualSource::grab() { +bool VirtualSource::grab(int n, int b) { if (scene_) { // Ensure this host thread is using correct GPU. diff --git a/components/rgbd-sources/include/ftl/rgbd/detail/source.hpp b/components/rgbd-sources/include/ftl/rgbd/detail/source.hpp index a047174de..8ebdab720 100644 --- a/components/rgbd-sources/include/ftl/rgbd/detail/source.hpp +++ b/components/rgbd-sources/include/ftl/rgbd/detail/source.hpp @@ -27,7 +27,11 @@ class Source { explicit Source(ftl::rgbd::Source *host) : capabilities_(0), host_(host), params_({0}) { } virtual ~Source() {} - virtual bool grab()=0; + /** + * @param n Number of frames to request in batch. Default -1 means automatic (10) + * @param b Bit rate setting. -1 = automatic, 0 = best quality, 9 = lowest quality + */ + virtual bool grab(int n, int b)=0; virtual bool isReady() { return false; }; virtual void setPose(const Eigen::Matrix4d &pose) { }; diff --git a/components/rgbd-sources/include/ftl/rgbd/source.hpp b/components/rgbd-sources/include/ftl/rgbd/source.hpp index 3b5e53f18..3cf1a2b52 100644 --- a/components/rgbd-sources/include/ftl/rgbd/source.hpp +++ b/components/rgbd-sources/include/ftl/rgbd/source.hpp @@ -173,6 +173,7 @@ class Source : public ftl::Configurable { detail::Source *impl_; cv::Mat rgb_; cv::Mat depth_; + cv::Mat thumb_; Camera params_; // TODO Find better solution Eigen::Matrix4d pose_; ftl::net::Universe *net_; diff --git a/components/rgbd-sources/include/ftl/rgbd/streamer.hpp b/components/rgbd-sources/include/ftl/rgbd/streamer.hpp index 665f9af9d..5dac8fd13 100644 --- a/components/rgbd-sources/include/ftl/rgbd/streamer.hpp +++ b/components/rgbd-sources/include/ftl/rgbd/streamer.hpp @@ -17,14 +17,15 @@ namespace ftl { namespace rgbd { static const int kChunkDim = 4; +static constexpr int kChunkCount = kChunkDim * kChunkDim; namespace detail { struct StreamClient { std::string uri; ftl::UUID peerid; - int txcount; // Frames sent since last request - int txmax; // Frames to send in request + std::atomic<int> txcount; // Frames sent since last request + int txmax; // Frames to send in request }; static const unsigned int kGrabbed = 0x1; @@ -34,11 +35,12 @@ static const unsigned int kDepth = 0x4; struct StreamSource { ftl::rgbd::Source *src; std::atomic<unsigned int> jobs; // Busy or ready to swap? + std::atomic<unsigned int> clientCount; cv::Mat rgb; // Tx buffer cv::Mat depth; // Tx buffer cv::Mat prev_rgb; cv::Mat prev_depth; - std::vector<detail::StreamClient> clients[10]; // One list per bitrate + std::list<detail::StreamClient> clients[10]; // One list per bitrate std::shared_mutex mutex; unsigned long long frame; }; diff --git a/components/rgbd-sources/src/bitrate_settings.hpp b/components/rgbd-sources/src/bitrate_settings.hpp new file mode 100644 index 000000000..6b6e1508e --- /dev/null +++ b/components/rgbd-sources/src/bitrate_settings.hpp @@ -0,0 +1,32 @@ +#ifndef _FTL_RGBD_BITRATESETTINGS_HPP_ +#define _FTL_RGBD_BITRATESETTINGS_HPP_ + +namespace ftl { +namespace rgbd { +namespace detail { + +struct BitrateSetting { + int width; + int height; + int jpg_quality; + int png_compression; +}; + +static const BitrateSetting bitrate_settings[] = { + 1280, 720, 95, 1, + 1280, 720, 95, 1, + 1280, 720, 95, 1, + 1280, 720, 75, 1, + 640, 360, 95, 1, + 640, 360, 75, 5, + 640, 360, 50, 5, + 320, 160, 95, 5, + 320, 160, 75, 5, + 320, 160, 50, 9 +}; + +} +} +} + +#endif // _FTL_RGBD_BITRATESETTINGS_HPP_ diff --git a/components/rgbd-sources/src/image.hpp b/components/rgbd-sources/src/image.hpp index 296a1c876..b389fd176 100644 --- a/components/rgbd-sources/src/image.hpp +++ b/components/rgbd-sources/src/image.hpp @@ -14,7 +14,7 @@ class ImageSource : public ftl::rgbd::detail::Source { } - bool grab() { return false; }; + bool grab(int n, int b) { return false; }; bool isReady() { return false; }; }; diff --git a/components/rgbd-sources/src/middlebury_source.cpp b/components/rgbd-sources/src/middlebury_source.cpp index a1f490e1d..d335f8dbf 100644 --- a/components/rgbd-sources/src/middlebury_source.cpp +++ b/components/rgbd-sources/src/middlebury_source.cpp @@ -150,7 +150,7 @@ void MiddleburySource::_performDisparity() { stream_.waitForCompletion(); } -bool MiddleburySource::grab() { +bool MiddleburySource::grab(int n, int b) { //_performDisparity(); return true; } diff --git a/components/rgbd-sources/src/middlebury_source.hpp b/components/rgbd-sources/src/middlebury_source.hpp index 10cd47d44..5f0e2be53 100644 --- a/components/rgbd-sources/src/middlebury_source.hpp +++ b/components/rgbd-sources/src/middlebury_source.hpp @@ -19,7 +19,7 @@ class MiddleburySource : public detail::Source { MiddleburySource(ftl::rgbd::Source *, const std::string &dir); ~MiddleburySource() {}; - bool grab(); + bool grab(int n, int b); bool isReady() { return ready_; } private: diff --git a/components/rgbd-sources/src/net.cpp b/components/rgbd-sources/src/net.cpp index 62682d712..7752f8d1f 100644 --- a/components/rgbd-sources/src/net.cpp +++ b/components/rgbd-sources/src/net.cpp @@ -56,7 +56,7 @@ bool NetSource::_getCalibration(Universe &net, const UUID &peer, const string &s } NetSource::NetSource(ftl::rgbd::Source *host) - : ftl::rgbd::detail::Source(host), active_(false) { + : ftl::rgbd::detail::Source(host), active_(false), minB_(9), maxN_(1) { gamma_ = host->value("gamma", 1.0f); temperature_ = host->value("temperature", 6500); @@ -120,9 +120,20 @@ void NetSource::_recvChunk(int frame, int chunk, bool delta, const vector<unsign cv::Mat chunkRGB = rgb_(roi); cv::Mat chunkDepth = depth_(roi); - tmp_rgb.copyTo(chunkRGB); - tmp_depth.convertTo(chunkDepth, CV_32FC1, 1.0f/1000.0f); //(16.0f*10.0f)); - if (chunk == 0) N_--; + // Original size so just copy + if (tmp_rgb.cols == chunkRGB.cols) { + tmp_rgb.copyTo(chunkRGB); + tmp_depth.convertTo(chunkDepth, CV_32FC1, 1.0f/1000.0f); //(16.0f*10.0f)); + // Downsized so needs a scale up + } else { + cv::resize(tmp_rgb, chunkRGB, chunkRGB.size()); + tmp_depth.convertTo(tmp_depth, CV_32FC1, 1.0f/1000.0f); + cv::resize(tmp_depth, chunkDepth, chunkDepth.size()); + } + + if (chunk == 0) { + N_--; + } } void NetSource::setPose(const Eigen::Matrix4d &pose) { @@ -162,14 +173,14 @@ void NetSource::_updateURI() { _recvChunk(frame, chunk, delta, jpg, d); }); - N_ = 1; + N_ = 0; // Initiate stream with request for first 10 frames - try { - host_->getNet()->send(peer_, "get_stream", *uri, N_, 0, host_->getNet()->id(), *uri); - } catch(...) { - LOG(ERROR) << "Could not connect to stream " << *uri; - } + //try { + // host_->getNet()->send(peer_, "get_stream", *uri, N_, 0, host_->getNet()->id(), *uri); + //} catch(...) { + // LOG(ERROR) << "Could not connect to stream " << *uri; + //} // Update chunk details chunks_dim_ = ftl::rgbd::kChunkDim; @@ -188,13 +199,24 @@ void NetSource::_updateURI() { } } -bool NetSource::grab() { - // Send one frame before end to prevent unwanted pause - if (N_ <= 2) { - N_ = 10; - if (!host_->getNet()->send(peer_, "get_stream", *host_->get<string>("uri"), N_, 0, host_->getNet()->id(), *host_->get<string>("uri"))) { +bool NetSource::grab(int n, int b) { + // Choose highest requested number of frames + maxN_ = std::max(maxN_,(n == -1) ? 10 : n); + + // Choose best requested quality + minB_ = std::min(minB_,(b == -1) ? 0 : b); + + // Send k frames before end to prevent unwanted pause + // Unless only a single frame is requested + if ((N_ <= 2 && maxN_ > 1) || N_ == 0) { + N_ = maxN_; + + if (!host_->getNet()->send(peer_, "get_stream", *host_->get<string>("uri"), N_, minB_, host_->getNet()->id(), *host_->get<string>("uri"))) { active_ = false; } + + maxN_ = 1; // Reset to single frame + minB_ = 9; // Reset to worst quality } return true; } diff --git a/components/rgbd-sources/src/net.hpp b/components/rgbd-sources/src/net.hpp index 5a5639273..61bbbe7d7 100644 --- a/components/rgbd-sources/src/net.hpp +++ b/components/rgbd-sources/src/net.hpp @@ -22,7 +22,7 @@ class NetSource : public detail::Source { explicit NetSource(ftl::rgbd::Source *); ~NetSource(); - bool grab(); + bool grab(int n, int b); bool isReady(); void setPose(const Eigen::Matrix4d &pose); @@ -43,6 +43,8 @@ class NetSource : public detail::Source { cv::Mat idepth_; float gamma_; int temperature_; + int minB_; + int maxN_; bool _getCalibration(ftl::net::Universe &net, const ftl::UUID &peer, const std::string &src, ftl::rgbd::Camera &p); void _recv(const std::vector<unsigned char> &jpg, const std::vector<unsigned char> &d); diff --git a/components/rgbd-sources/src/realsense_source.cpp b/components/rgbd-sources/src/realsense_source.cpp index e1b66afb1..a4f34c236 100644 --- a/components/rgbd-sources/src/realsense_source.cpp +++ b/components/rgbd-sources/src/realsense_source.cpp @@ -38,7 +38,7 @@ RealsenseSource::~RealsenseSource() { } -bool RealsenseSource::grab() { +bool RealsenseSource::grab(int n, int b) { rs2::frameset frames = pipe_.wait_for_frames(); //rs2::align align(RS2_STREAM_DEPTH); frames = align_to_depth_.process(frames); //align_to_depth_.process(frames); diff --git a/components/rgbd-sources/src/realsense_source.hpp b/components/rgbd-sources/src/realsense_source.hpp index 2a48ac2db..2af26bbaf 100644 --- a/components/rgbd-sources/src/realsense_source.hpp +++ b/components/rgbd-sources/src/realsense_source.hpp @@ -17,7 +17,7 @@ class RealsenseSource : public ftl::rgbd::detail::Source { RealsenseSource(ftl::rgbd::Source *host); ~RealsenseSource(); - bool grab(); + bool grab(int n=-1, int b=-1); bool isReady(); private: diff --git a/components/rgbd-sources/src/snapshot_source.hpp b/components/rgbd-sources/src/snapshot_source.hpp index 7b5b491fc..38b9d875a 100644 --- a/components/rgbd-sources/src/snapshot_source.hpp +++ b/components/rgbd-sources/src/snapshot_source.hpp @@ -17,7 +17,7 @@ class SnapshotSource : public detail::Source { SnapshotSource(ftl::rgbd::Source *, ftl::rgbd::SnapshotReader &reader, const std::string &id); ~SnapshotSource() {}; - bool grab() override {}; + bool grab(int n, int b) override { return true; }; bool isReady() { return true; } //void reset(); diff --git a/components/rgbd-sources/src/source.cpp b/components/rgbd-sources/src/source.cpp index b5741520c..1f6c521cc 100644 --- a/components/rgbd-sources/src/source.cpp +++ b/components/rgbd-sources/src/source.cpp @@ -196,7 +196,7 @@ const Eigen::Matrix4d &Source::getPose() const { } bool Source::hasCapabilities(capability_t c) { - return getCapabilities() & c == c; + return (getCapabilities() & c) == c; } capability_t Source::getCapabilities() const { @@ -212,10 +212,27 @@ void Source::reset() { bool Source::grab() { UNIQUE_LOCK(mutex_,lk); - if (impl_ && impl_->grab()) { + if (impl_ && impl_->grab(-1,-1)) { impl_->rgb_.copyTo(rgb_); impl_->depth_.copyTo(depth_); return true; } return false; } + +bool Source::thumbnail(cv::Mat &t) { + // TODO(Nick) periodic refresh + if (impl_) { + UNIQUE_LOCK(mutex_,lk); + impl_->grab(1, 9); + impl_->rgb_.copyTo(rgb_); + impl_->depth_.copyTo(depth_); + } + if (!rgb_.empty()) { + SHARED_LOCK(mutex_,lk); + // Downsize and square the rgb_ image + cv::resize(rgb_, thumb_, cv::Size(320,180)); + } + t = thumb_; + return !thumb_.empty(); +} diff --git a/components/rgbd-sources/src/stereovideo.cpp b/components/rgbd-sources/src/stereovideo.cpp index c0471643a..9442eefde 100644 --- a/components/rgbd-sources/src/stereovideo.cpp +++ b/components/rgbd-sources/src/stereovideo.cpp @@ -123,7 +123,7 @@ static void disparityToDepth(const cv::cuda::GpuMat &disparity, cv::cuda::GpuMat cv::cuda::divide(val, disparity, depth, 1.0f / 1000.0f, -1, stream); } -bool StereoVideoSource::grab() { +bool StereoVideoSource::grab(int n, int b) { lsrc_->get(left_, right_, stream_); if (depth_tmp_.empty()) depth_tmp_ = cv::cuda::GpuMat(left_.size(), CV_32FC1); if (disp_tmp_.empty()) disp_tmp_ = cv::cuda::GpuMat(left_.size(), CV_32FC1); diff --git a/components/rgbd-sources/src/stereovideo.hpp b/components/rgbd-sources/src/stereovideo.hpp index a5db20fb9..28fe6b90d 100644 --- a/components/rgbd-sources/src/stereovideo.hpp +++ b/components/rgbd-sources/src/stereovideo.hpp @@ -26,7 +26,7 @@ class StereoVideoSource : public detail::Source { StereoVideoSource(ftl::rgbd::Source*, const std::string &); ~StereoVideoSource(); - bool grab(); + bool grab(int n, int b); bool isReady(); //const cv::Mat &getRight() const { return right_; } diff --git a/components/rgbd-sources/src/streamer.cpp b/components/rgbd-sources/src/streamer.cpp index e2daa0f0f..3dc57428b 100644 --- a/components/rgbd-sources/src/streamer.cpp +++ b/components/rgbd-sources/src/streamer.cpp @@ -5,10 +5,13 @@ #include <chrono> #include <tuple> +#include "bitrate_settings.hpp" + using ftl::rgbd::Streamer; using ftl::rgbd::Source; using ftl::rgbd::detail::StreamSource; using ftl::rgbd::detail::StreamClient; +using ftl::rgbd::detail::bitrate_settings; using ftl::net::Universe; using std::string; using std::list; @@ -123,6 +126,7 @@ void Streamer::add(Source *src) { //s->prev_depth = cv::Mat(cv::Size(src->parameters().width, src->parameters().height), CV_16SC1, 0); s->jobs = 0; s->frame = 0; + s->clientCount = 0; sources_[src->getID()] = s; } @@ -133,37 +137,38 @@ void Streamer::add(Source *src) { void Streamer::_addClient(const string &source, int N, int rate, const ftl::UUID &peer, const string &dest) { StreamSource *s = nullptr; - //{ + { UNIQUE_LOCK(mutex_,slk); if (sources_.find(source) == sources_.end()) return; if (rate < 0 || rate >= 10) return; if (N < 0 || N > ftl::rgbd::kMaxFrames) return; - DLOG(INFO) << "Adding Stream Peer: " << peer.to_string(); + LOG(INFO) << "Adding Stream Peer: " << peer.to_string() << " rate=" << rate << " N=" << N; s = sources_[source]; - //} + } - if (!s) return; + if (!s) return; // No matching stream + SHARED_LOCK(mutex_, slk); UNIQUE_LOCK(s->mutex, lk2); - for (int i=0; i<s->clients[rate].size(); i++) { - if (s->clients[rate][i].peerid == peer) { - StreamClient &c = s->clients[rate][i]; - c.txmax = N; - c.txcount = 0; + for (auto &client : s->clients[rate]) { + // If already listening, just update chunk counters + if (client.peerid == peer) { + client.txmax = N * kChunkCount; + client.txcount = 0; return; } } - StreamClient c; + // Not an existing client so add one + StreamClient &c = s->clients[rate].emplace_back(); c.peerid = peer; c.uri = dest; c.txcount = 0; - c.txmax = N; - - s->clients[rate].push_back(c); + c.txmax = N * kChunkCount; + ++s->clientCount; } void Streamer::remove(Source *) { @@ -176,7 +181,7 @@ void Streamer::remove(const std::string &) { void Streamer::stop() { active_ = false; - //pool_.stop(); + wait(); } void Streamer::poll() { @@ -222,14 +227,17 @@ void Streamer::_swap(StreamSource *src) { if (src->jobs == 0) { UNIQUE_LOCK(src->mutex,lk); - auto i = src->clients[0].begin(); - while (i != src->clients[0].end()) { - (*i).txcount++; - if ((*i).txcount >= (*i).txmax) { - LOG(INFO) << "Remove client: " << (*i).uri; - i = src->clients[0].erase(i); - } else { - i++; + for (unsigned int b=0; b<10; ++b) { + auto i = src->clients[b].begin(); + while (i != src->clients[b].end()) { + // Client request completed so remove from list + if ((*i).txcount >= (*i).txmax) { + LOG(INFO) << "Remove client: " << (*i).uri; + i = src->clients[b].erase(i); + --src->clientCount; + } else { + i++; + } } } @@ -268,7 +276,7 @@ void Streamer::_schedule() { string uri = s.first; // No point in doing work if no clients - if (s.second->clients[0].size() == 0) { + if (s.second->clientCount == 0) { continue; } @@ -283,8 +291,7 @@ void Streamer::_schedule() { // Grab job ftl::pool.push([this,src](int id) { - //StreamSource *src = sources_[uri]; - auto start = std::chrono::high_resolution_clock::now(); + //auto start = std::chrono::high_resolution_clock::now(); try { src->src->grab(); } catch (std::exception &ex) { @@ -295,14 +302,11 @@ void Streamer::_schedule() { LOG(ERROR) << "Unknown exception when grabbing frame"; } - std::chrono::duration<double> elapsed = - std::chrono::high_resolution_clock::now() - start; - LOG(INFO) << "Grab in " << elapsed.count() << "s"; + //std::chrono::duration<double> elapsed = + // std::chrono::high_resolution_clock::now() - start; + //LOG(INFO) << "Grab in " << elapsed.count() << "s"; - // CHECK (Nick) Can state be an atomic instead? - //UNIQUE_LOCK(src->mutex, lk); src->jobs--; - //src->state |= ftl::rgbd::detail::kGrabbed; _swap(src); // Mark job as finished @@ -311,154 +315,83 @@ void Streamer::_schedule() { }); // Create jobs for each chunk - for (int i=0; i<(kChunkDim*kChunkDim); i++) { + for (int i=0; i<kChunkCount; ++i) { + // Add chunk job to thread pool ftl::pool.push([this,src](int id, int chunk) { if (!src->rgb.empty() && !src->depth.empty()) { - bool delta = (chunk+src->frame) % 8 > 0; + bool delta = (chunk+src->frame) % 8 > 0; // Do XOR or not int chunk_width = src->rgb.cols / kChunkDim; int chunk_height = src->rgb.rows / kChunkDim; - // Build chunk head + // Build chunk heads int cx = (chunk % kChunkDim) * chunk_width; int cy = (chunk / kChunkDim) * chunk_height; - cv::Rect roi(cx,cy,chunk_width,chunk_height); vector<unsigned char> rgb_buf; cv::Mat chunkRGB = src->rgb(roi); cv::Mat chunkDepth = src->depth(roi); //cv::Mat chunkDepthPrev = src->prev_depth(roi); - cv::imencode(".jpg", chunkRGB, rgb_buf); - cv::Mat d2, d3; vector<unsigned char> d_buf; chunkDepth.convertTo(d2, CV_16UC1, 1000); // 16*10); //if (delta) d3 = (d2 * 2) - chunkDepthPrev; //else d3 = d2; //d2.copyTo(chunkDepthPrev); - vector<int> pngparams = {cv::IMWRITE_PNG_COMPRESSION, compress_level_}; // Default is 1 for fast, 9 = small but slow. - cv::imencode(".png", d2, d_buf, pngparams); - - //LOG(INFO) << "Sending chunk " << chunk << " : size = " << (d_buf.size()+rgb_buf.size()) / 1024 << "kb"; - - SHARED_LOCK(src->mutex,lk); - auto i = src->clients[0].begin(); - while (i != src->clients[0].end()) { - try { - // TODO(Nick) Send pose and timestamp - if (!net_->send((*i).peerid, (*i).uri, 0, chunk, delta, rgb_buf, d_buf)) { - (*i).txcount = (*i).txmax; + + // For each allowed bitrate setting (0 = max quality) + for (unsigned int b=0; b<10; ++b) { + { + //SHARED_LOCK(src->mutex,lk); + if (src->clients[b].size() == 0) continue; + } + + // Max bitrate means no changes + if (b == 0) { + cv::imencode(".jpg", chunkRGB, rgb_buf); + vector<int> pngparams = {cv::IMWRITE_PNG_COMPRESSION, compress_level_}; // Default is 1 for fast, 9 = small but slow. + cv::imencode(".png", d2, d_buf, pngparams); + + // Otherwise must downscale and change compression params + // TODO(Nick) could reuse downscales + } else { + cv::Mat downrgb, downdepth; + cv::resize(chunkRGB, downrgb, cv::Size(bitrate_settings[b].width / kChunkDim, bitrate_settings[b].height / kChunkDim)); + cv::resize(d2, downdepth, cv::Size(bitrate_settings[b].width / kChunkDim, bitrate_settings[b].height / kChunkDim)); + vector<int> jpgparams = {cv::IMWRITE_JPEG_QUALITY, bitrate_settings[b].jpg_quality}; + cv::imencode(".jpg", downrgb, rgb_buf, jpgparams); + vector<int> pngparams = {cv::IMWRITE_PNG_COMPRESSION, bitrate_settings[b].png_compression}; // Default is 1 for fast, 9 = small but slow. + cv::imencode(".png", downdepth, d_buf, pngparams); + } + + //if (chunk == 0) LOG(INFO) << "Sending chunk " << chunk << " : size = " << (d_buf.size()+rgb_buf.size()) << "bytes"; + + // Lock to prevent clients being added / removed + SHARED_LOCK(src->mutex,lk); + auto c = src->clients[b].begin(); + while (c != src->clients[b].end()) { + try { + // TODO(Nick) Send pose and timestamp + if (!net_->send((*c).peerid, (*c).uri, 0, chunk, delta, rgb_buf, d_buf)) { + // Send failed so mark as client stream completed + (*c).txcount = (*c).txmax; + } else { + ++(*c).txcount; + } + } catch(...) { + (*c).txcount = (*c).txmax; } - } catch(...) { - (*i).txcount = (*i).txmax; + ++c; } - i++; } } - //src->state |= ftl::rgbd::detail::kRGB; src->jobs--; _swap(src); --jobs_; job_cv_.notify_one(); }, i); } - - // Compress colour job - /*pool_.push([this,src](int id) { - if (!src->rgb.empty()) { - auto start = std::chrono::high_resolution_clock::now(); - - //vector<unsigned char> src->rgb_buf; - cv::imencode(".jpg", src->rgb, src->rgb_buf); - } - - src->state |= ftl::rgbd::detail::kRGB; - _swap(src); - --jobs_; - job_cv_.notify_one(); - }); - - // Compress depth job - ftl::pool.push([this,src](int id) { - auto start = std::chrono::high_resolution_clock::now(); - - if (!src->depth.empty()) { - cv::Mat d2; - src->depth.convertTo(d2, CV_16UC1, 16*100); - //vector<unsigned char> d_buf; - - // Setting 1 = fast but large - // Setting 9 = small but slow - // Anything up to 8 causes minimal if any impact on frame rate - // on my (Nicks) laptop, but 9 halves the frame rate. - vector<int> pngparams = {cv::IMWRITE_PNG_COMPRESSION, 1}; // Default is 1 for fast, 9 = small but slow. - cv::imencode(".png", d2, src->d_buf, pngparams); - } - - std::chrono::duration<double> elapsed = - std::chrono::high_resolution_clock::now() - start; - LOG(INFO) << "Depth Compress in " << elapsed.count() << "s"; - - src->state |= ftl::rgbd::detail::kDepth; - _swap(src); - --jobs_; - job_cv_.notify_one(); - });*/ - - // Transmit job - // For any single source and bitrate there is only one thread - // meaning that no lock is required here since outer shared_lock - // prevents addition of new clients. - // TODO, could do one for each bitrate... - /* pool_.push([this,src](int id) { - //StreamSource *src = sources_[uri]; - - try { - if (src && src->rgb.rows > 0 && src->depth.rows > 0 && src->clients[0].size() > 0) { - auto start = std::chrono::high_resolution_clock::now(); - - vector<unsigned char> rgb_buf; - cv::imencode(".jpg", src->rgb, rgb_buf); - - std::chrono::duration<double> elapsed = - std::chrono::high_resolution_clock::now() - start; - LOG(INFO) << "JPG in " << elapsed.count() << "s"; - - cv::Mat d2; - src->depth.convertTo(d2, CV_16UC1, 16*100); - vector<unsigned char> d_buf; - - // Setting 1 = fast but large - // Setting 9 = small but slow - // Anything up to 8 causes minimal if any impact on frame rate - // on my (Nicks) laptop, but 9 halves the frame rate. - vector<int> pngparams = {cv::IMWRITE_PNG_COMPRESSION, 1}; // Default is 1 for fast, 9 = small but slow. - cv::imencode(".png", d2, d_buf, pngparams); - - //LOG(INFO) << "Data size: " << ((rgb_buf.size() + d_buf.size()) / 1024) << "kb"; - - - } - } catch(...) { - LOG(ERROR) << "Error in transmission loop"; - } - - // CHECK (Nick) Could state be an atomic? - //UNIQUE_LOCK(src->mutex,lk); - //LOG(INFO) << "Tx Frame: " << uri; - src->state |= ftl::rgbd::detail::kTransmitted; - _swap(*src); - //lk.unlock(); - - // Mark job as finished - //UNIQUE_LOCK(job_mtx_,ulk); - //jobs_--; - //ulk.unlock(); - - --jobs_; - job_cv_.notify_one(); - });*/ } } diff --git a/components/rgbd-sources/test/source_unit.cpp b/components/rgbd-sources/test/source_unit.cpp index 8454c011b..b27b72e07 100644 --- a/components/rgbd-sources/test/source_unit.cpp +++ b/components/rgbd-sources/test/source_unit.cpp @@ -26,7 +26,7 @@ class ImageSource : public ftl::rgbd::detail::Source { last_type = "image"; } - bool grab() { return true; }; + bool grab(int n, int b) { return true; }; bool isReady() { return true; }; }; @@ -39,7 +39,7 @@ class StereoVideoSource : public ftl::rgbd::detail::Source { last_type = "video"; } - bool grab() { return true; }; + bool grab(int n, int b) { return true; }; bool isReady() { return true; }; }; @@ -49,7 +49,7 @@ class NetSource : public ftl::rgbd::detail::Source { last_type = "net"; } - bool grab() { return true; }; + bool grab(int n, int b) { return true; }; bool isReady() { return true; }; }; @@ -59,7 +59,7 @@ class SnapshotSource : public ftl::rgbd::detail::Source { last_type = "snapshot"; } - bool grab() { return true; }; + bool grab(int n, int b) { return true; }; bool isReady() { return true; }; }; @@ -69,7 +69,7 @@ class RealsenseSource : public ftl::rgbd::detail::Source { last_type = "realsense"; } - bool grab() { return true; }; + bool grab(int n, int b) { return true; }; bool isReady() { return true; }; }; @@ -79,7 +79,7 @@ class MiddleburySource : public ftl::rgbd::detail::Source { last_type = "middlebury"; } - bool grab() { return true; }; + bool grab(int n, int b) { return true; }; bool isReady() { return true; }; }; -- GitLab