From 538ca135060e5712ad8089327e098d5702645f54 Mon Sep 17 00:00:00 2001 From: Nicolas Pope <nicolas.pope@utu.fi> Date: Tue, 7 Jan 2020 10:06:38 +0200 Subject: [PATCH] Implements #272 Disparity pipeline in reconstruct --- applications/gui/src/camera.cpp | 29 +- applications/gui/src/gltexture.cpp | 4 +- applications/gui/src/screen.cpp | 1 + applications/reconstruct/src/main.cpp | 27 +- .../reconstruct/src/reconstruction.cpp | 95 ++--- .../reconstruct/src/reconstruction.hpp | 5 +- applications/vision/src/main.cpp | 7 +- .../codecs/include/ftl/codecs/channels.hpp | 7 +- .../include/ftl/codecs/opencv_decoder.hpp | 2 +- components/codecs/src/channels.cpp | 4 +- components/codecs/src/nvpipe_decoder.cpp | 4 +- components/codecs/src/opencv_decoder.cpp | 13 +- components/codecs/test/opencv_codec_unit.cpp | 4 +- components/operators/CMakeLists.txt | 3 + .../include/ftl/operators/colours.hpp | 1 + .../include/ftl/operators/disparity.hpp | 22 ++ components/operators/src/colours.cpp | 36 +- components/operators/src/depth.cpp | 76 ++++ .../src/disparity/bilateral_filter.cpp | 28 +- components/operators/src/disparity/cuda.hpp | 3 + .../operators/src/disparity/disp2depth.cu | 29 ++ .../src/disparity/disparity_to_depth.cpp | 5 +- .../operators/src/disparity/fixstars_sgm.cpp | 9 +- .../opencv/disparity_bilateral_filter.cpp | 200 +++++++++++ .../opencv/disparity_bilateral_filter.cu | 220 ++++++++++++ .../opencv/disparity_bilateral_filter.hpp | 8 + .../src/disparity/opencv/joint_bilateral.hpp | 12 + components/operators/src/mask.cpp | 4 + components/operators/src/normals.cpp | 6 +- components/operators/src/operator.cpp | 17 +- components/operators/src/segmentation.cpp | 6 +- .../cpp/include/ftl/render/tri_render.hpp | 3 + components/renderers/cpp/src/tri_render.cpp | 10 + .../include/ftl/rgbd/detail/source.hpp | 3 +- .../rgbd-sources/include/ftl/rgbd/frame.hpp | 20 ++ .../include/ftl/rgbd/frameset.hpp | 2 + .../rgbd-sources/include/ftl/rgbd/group.hpp | 33 +- .../rgbd-sources/include/ftl/rgbd/source.hpp | 12 +- .../include/ftl/rgbd/streamer.hpp | 2 + .../rgbd-sources/include/ftl/rgbd/virtual.hpp | 2 +- components/rgbd-sources/src/frame.cpp | 89 ++++- components/rgbd-sources/src/frameset.cpp | 8 + components/rgbd-sources/src/group.cpp | 326 ++++++++++++------ components/rgbd-sources/src/source.cpp | 14 +- .../src/sources/ftlfile/file_source.cpp | 89 +++-- .../src/sources/ftlfile/file_source.hpp | 5 +- .../src/sources/ftlfile/player.cpp | 7 +- .../rgbd-sources/src/sources/net/net.cpp | 45 ++- .../rgbd-sources/src/sources/net/net.hpp | 7 +- .../sources/realsense/realsense_source.cpp | 9 +- .../src/sources/snapshot/snapshot_source.cpp | 8 +- .../src/sources/stereovideo/stereovideo.cpp | 32 +- .../src/sources/virtual/virtual.cpp | 17 +- components/rgbd-sources/src/streamer.cpp | 117 +++++-- 54 files changed, 1394 insertions(+), 353 deletions(-) create mode 100644 components/operators/src/depth.cpp create mode 100644 components/operators/src/disparity/opencv/disparity_bilateral_filter.cpp create mode 100644 components/operators/src/disparity/opencv/disparity_bilateral_filter.cu create mode 100644 components/operators/src/disparity/opencv/disparity_bilateral_filter.hpp create mode 100644 components/operators/src/disparity/opencv/joint_bilateral.hpp diff --git a/applications/gui/src/camera.cpp b/applications/gui/src/camera.cpp index fb5777740..3259bfa33 100644 --- a/applications/gui/src/camera.cpp +++ b/applications/gui/src/camera.cpp @@ -14,6 +14,7 @@ using ftl::gui::GLTexture; using ftl::gui::PoseWindow; using ftl::codecs::Channel; using ftl::codecs::Channels; +using cv::cuda::GpuMat; // TODO(Nick) MOVE class StatisticsImage { @@ -156,20 +157,22 @@ ftl::gui::Camera::Camera(ftl::gui::Screen *screen, ftl::rgbd::Source *src) : scr posewin_->setTheme(screen->windowtheme); posewin_->setVisible(false); - src->setCallback([this](int64_t ts, cv::cuda::GpuMat &channel1, cv::cuda::GpuMat &channel2) { + src->setCallback([this](int64_t ts, ftl::rgbd::Frame &frame) { UNIQUE_LOCK(mutex_, lk); - im1_.create(channel1.size(), channel1.type()); - im2_.create(channel2.size(), channel2.type()); - - //cv::swap(channel1, im1_); - //cv::swap(channel2, im2_); + auto &channel1 = frame.get<GpuMat>(Channel::Colour); + im1_.create(channel1.size(), channel1.type()); channel1.download(im1_); - channel2.download(im2_); - + // OpenGL (0,0) bottom left cv::flip(im1_, im1_, 0); - cv::flip(im2_, im2_, 0); + + if (channel_ != Channel::Colour && channel_ != Channel::None && frame.hasChannel(channel_)) { + auto &channel2 = frame.get<GpuMat>(channel_); + im2_.create(channel2.size(), channel2.type()); + channel2.download(im2_); + cv::flip(im2_, im2_, 0); + } }); } @@ -330,6 +333,7 @@ static void visualizeDepthMap( const cv::Mat &depth, cv::Mat &out, applyColorMap(out, out, cv::COLORMAP_JET); out.setTo(cv::Scalar(255, 255, 255), mask); + cv::cvtColor(out,out, cv::COLOR_BGR2BGRA); } static void visualizeEnergy( const cv::Mat &depth, cv::Mat &out, @@ -343,6 +347,7 @@ static void visualizeEnergy( const cv::Mat &depth, cv::Mat &out, applyColorMap(out, out, cv::COLORMAP_JET); //out.setTo(cv::Scalar(255, 255, 255), mask); + cv::cvtColor(out,out, cv::COLOR_BGR2BGRA); } static void drawEdges( const cv::Mat &in, cv::Mat &out, @@ -353,8 +358,8 @@ static void drawEdges( const cv::Mat &in, cv::Mat &out, cv::Laplacian(in, edges, 8, ksize); cv::threshold(edges, edges, threshold, 255, threshold_type); - cv::Mat edges_color(in.size(), CV_8UC3); - cv::addWeighted(edges, weight, out, 1.0, 0.0, out, CV_8UC3); + cv::Mat edges_color(in.size(), CV_8UC4); + cv::addWeighted(edges, weight, out, 1.0, 0.0, out, CV_8UC4); } cv::Mat ftl::gui::Camera::visualizeActiveChannel() { @@ -497,7 +502,7 @@ const GLTexture &ftl::gui::Camera::captureFrame() { //case Channel::Flow: case Channel::ColourNormals: case Channel::Right: - if (im2_.rows == 0 || im2_.type() != CV_8UC3) { break; } + if (im2_.rows == 0 || im2_.type() != CV_8UC4) { break; } texture2_.update(im2_); break; diff --git a/applications/gui/src/gltexture.cpp b/applications/gui/src/gltexture.cpp index 8b36e848f..d12c19379 100644 --- a/applications/gui/src/gltexture.cpp +++ b/applications/gui/src/gltexture.cpp @@ -19,7 +19,7 @@ void GLTexture::update(cv::Mat &m) { 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_BGR, GL_UNSIGNED_BYTE, m.data); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m.cols, m.rows, 0, GL_BGRA, 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); @@ -27,7 +27,7 @@ void GLTexture::update(cv::Mat &m) { } 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); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m.cols, m.rows, 0, GL_BGRA, GL_UNSIGNED_BYTE, m.data); } auto err = glGetError(); if (err != 0) LOG(ERROR) << "OpenGL Texture error: " << err; diff --git a/applications/gui/src/screen.cpp b/applications/gui/src/screen.cpp index 77c4e4318..aa2d5e36e 100644 --- a/applications/gui/src/screen.cpp +++ b/applications/gui/src/screen.cpp @@ -54,6 +54,7 @@ namespace { in vec2 uv; void main() { color = blendAmount * texture(image1, uv) + (1.0 - blendAmount) * texture(image2, uv); + color.w = 1.0f; })"; } diff --git a/applications/reconstruct/src/main.cpp b/applications/reconstruct/src/main.cpp index 9cb5c0819..c3dc37e32 100644 --- a/applications/reconstruct/src/main.cpp +++ b/applications/reconstruct/src/main.cpp @@ -101,6 +101,8 @@ static void run(ftl::Configurable *root) { sourcecounts.push_back(configuration_size); } + ftl::codecs::Channels channels; + // Check paths for FTL files to load. auto paths = (*root->get<nlohmann::json>("paths")); for (auto &x : paths.items()) { @@ -117,8 +119,9 @@ static void run(ftl::Configurable *root) { reader.begin(); int max_stream = 0; - reader.read(reader.getStartTime()+100, [&max_stream](const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt) { + reader.read(reader.getStartTime()+100, [&max_stream,&channels](const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt) { max_stream = max(max_stream, spkt.streamID); + if ((int)spkt.channel < 32) channels |= spkt.channel; }); reader.end(); @@ -135,6 +138,8 @@ static void run(ftl::Configurable *root) { } } + if (channels.empty()) channels |= Channel::Depth; + // Create a vector of all input RGB-Depth sources auto sources = ftl::createArray<Source>(root, "sources", net); @@ -191,6 +196,11 @@ static void run(ftl::Configurable *root) { std::string id = std::to_string(cumulative); auto reconstr = ftl::create<ftl::Reconstruction>(root, id, id); for (size_t i=cumulative; i<cumulative+c; i++) { + if (channels.has(Channel::Depth)) { + sources[i]->setChannel(Channel::Depth); + } else if (channels.has(Channel::Right)) { + sources[i]->setChannel(Channel::Right); + } reconstr->addSource(sources[i]); } groups.push_back(reconstr); @@ -202,10 +212,18 @@ static void run(ftl::Configurable *root) { renderpipe->append<ftl::operators::FXAA>("antialiasing"); vs->onRender([vs, &groups, &renderpipe](ftl::rgbd::Frame &out) { + bool hasFrame = false; for (auto &reconstr : groups) { - reconstr->render(vs, out); + hasFrame = reconstr->render(vs, out) || hasFrame; + } + + if (hasFrame) { + renderpipe->apply(out, out, vs, 0); + return true; + } else { + LOG(INFO) << "NO FRAME"; + return false; } - renderpipe->apply(out, out, vs, 0); }); stream->add(vs); @@ -313,8 +331,7 @@ static void run(ftl::Configurable *root) { }); // ------------------------------------------------------------------------- - - stream->setLatency(6); // FIXME: This depends on source!? + //stream->add(group); stream->run(); diff --git a/applications/reconstruct/src/reconstruction.cpp b/applications/reconstruct/src/reconstruction.cpp index 8a868e56e..621693f0e 100644 --- a/applications/reconstruct/src/reconstruction.cpp +++ b/applications/reconstruct/src/reconstruction.cpp @@ -9,6 +9,7 @@ #include "ftl/operators/antialiasing.hpp" #include "ftl/operators/mvmls.hpp" #include "ftl/operators/clipping.hpp" +#include <ftl/operators/disparity.hpp> using ftl::Reconstruction; using ftl::codecs::Channel; @@ -24,14 +25,17 @@ static Eigen::Affine3d create_rotation_matrix(float ax, float ay, float az) { } Reconstruction::Reconstruction(nlohmann::json &config, const std::string name) : - ftl::Configurable(config), busy_(false), fs_render_(), fs_align_() { + ftl::Configurable(config), busy_(false), rbusy_(false), new_frame_(false), fs_render_(), fs_align_() { group_ = new ftl::rgbd::Group; - group_->setName("ReconGroup-" + name); + group_->setName("Reconstruction-" + name); group_->setLatency(4); renderer_ = ftl::create<ftl::render::Triangular>(this, "renderer", &fs_render_); pipeline_ = ftl::config::create<ftl::operators::Graph>(this, "pre_filters"); + pipeline_->append<ftl::operators::DepthChannel>("depth"); // Ensure there is a depth channel + pipeline_->append<ftl::operators::DisparityBilateralFilter>("bilateral_filter")->set("enabled", false); + pipeline_->append<ftl::operators::DisparityToDepth>("calculate_depth")->set("enabled", false); pipeline_->append<ftl::operators::ClipScene>("clipping")->set("enabled", false); pipeline_->append<ftl::operators::ColourChannels>("colour"); // Convert BGR to BGRA //pipeline_->append<ftl::operators::HFSmoother>("hfnoise"); // Remove high-frequency noise @@ -49,52 +53,46 @@ Reconstruction::Reconstruction(nlohmann::json &config, const std::string name) : group_->sync([this](ftl::rgbd::FrameSet &fs) -> bool { // TODO: pause - if (busy_) { - LOG(INFO) << "Group frameset dropped: " << fs.timestamp; + /*if (busy_) { + LOG(WARNING) << "Group frameset dropped: " << fs.timestamp; return true; } - busy_ = true; + busy_ = true;*/ // Swap the entire frameset to allow rapid return - fs.swapTo(fs_align_); + // FIXME: This gets stuck on a mutex + //fs.swapTo(fs_align_); - ftl::pool.push([this](int id) { - UNIQUE_LOCK(fs_align_.mtx, lk); - - /*rgb_.resize(fs_align_.frames.size()); - for (size_t i = 0; i < rgb_.size(); i++) { - auto &depth = fs_align_.frames[i].get<cv::cuda::GpuMat>(ftl::codecs::Channel::Depth); - auto &color = fs_align_.frames[i].get<cv::cuda::GpuMat>(ftl::codecs::Channel::Colour); - - if (depth.size() != color.size()) { - std::swap(rgb_[i], color); - cv::cuda::resize(rgb_[i], color, depth.size(), 0.0, 0.0, cv::INTER_LINEAR); - } - }*/ - - pipeline_->apply(fs_align_, fs_align_, 0); + //ftl::pool.push([this](int id) { + //UNIQUE_LOCK(fs.mtx, lk); + + pipeline_->apply(fs, fs, 0); // TODO: To use second GPU, could do a download, swap, device change, // then upload to other device. Or some direct device-2-device copy. - /* - for (size_t i = 0; i < rgb_.size(); i++) { - auto &depth = fs_align_.frames[i].get<cv::cuda::GpuMat>(ftl::codecs::Channel::Depth); - auto &color = fs_align_.frames[i].get<cv::cuda::GpuMat>(ftl::codecs::Channel::Colour); - auto &tmp = rgb_[i]; - - // TODO doesn't always work correctly if resolution changes - if (!tmp.empty() && (depth.size() != tmp.size())) { - std::swap(tmp, color); - fs_align_.frames[i].resetTexture(ftl::codecs::Channel::Colour); - fs_align_.frames[i].createTexture<uchar4>(ftl::codecs::Channel::Colour, true); - } - }*/ - fs_align_.swapTo(fs_render_); + // FIXME: Need a buffer of framesets + /*if (rbusy_) { + LOG(WARNING) << "Render frameset dropped: " << fs_align_.timestamp; + busy_ = false; + //return; + } + rbusy_ = true;*/ + + //LOG(INFO) << "Align complete... " << fs.timestamp; + + // TODO: Reduce mutex locking problem here... + { + UNIQUE_LOCK(exchange_mtx_, lk); + if (new_frame_ == true) LOG(WARNING) << "Frame lost"; + fs.swapTo(fs_align_); + new_frame_ = true; + } - LOG(INFO) << "Align complete... " << fs_align_.timestamp; - busy_ = false; - }); + //fs.resetFull(); + + //busy_ = false; + //}); return true; }); } @@ -104,7 +102,7 @@ Reconstruction::~Reconstruction() { } void Reconstruction::addSource(ftl::rgbd::Source *src) { - src->setChannel(Channel::Depth); + //src->setChannel(Channel::Depth); group_->addSource(src); // TODO: check if source is already in group? } @@ -112,7 +110,20 @@ void Reconstruction::addRawCallback(const std::function<void(ftl::rgbd::Source * group_->addRawCallback(cb); } -void Reconstruction::render(ftl::rgbd::VirtualSource *vs, ftl::rgbd::Frame &out) { +bool Reconstruction::render(ftl::rgbd::VirtualSource *vs, ftl::rgbd::Frame &out) { + { + UNIQUE_LOCK(exchange_mtx_, lk); + if (new_frame_) { + new_frame_ = false; + fs_align_.swapTo(fs_render_); + } + } + /*if (fs_render_.stale || fs_render_.timestamp <= 0) { + LOG(ERROR) << "STALE FRAME TO RENDER"; + return false; + } + fs_render_.stale = true;*/ + // Create scene transform, intended for axis aligning the walls and floor Eigen::Matrix4d transform; //if (getConfig()["transform"].is_object()) { @@ -134,5 +145,7 @@ void Reconstruction::render(ftl::rgbd::VirtualSource *vs, ftl::rgbd::Frame &out) //} Eigen::Affine3d sm = Eigen::Affine3d(Eigen::Scaling(double(value("scale", 1.0f)))); - renderer_->render(vs, out, sm.matrix() * transform); + bool res = renderer_->render(vs, out, sm.matrix() * transform); + //fs_render_.resetFull(); + return res; } \ No newline at end of file diff --git a/applications/reconstruct/src/reconstruction.hpp b/applications/reconstruct/src/reconstruction.hpp index 50441bedc..cb3bb8ebd 100644 --- a/applications/reconstruct/src/reconstruction.hpp +++ b/applications/reconstruct/src/reconstruction.hpp @@ -23,10 +23,13 @@ class Reconstruction : public ftl::Configurable { /** * Do the render for a specified virtual camera. */ - void render(ftl::rgbd::VirtualSource *vs, ftl::rgbd::Frame &out); + bool render(ftl::rgbd::VirtualSource *vs, ftl::rgbd::Frame &out); private: bool busy_; + bool rbusy_; + bool new_frame_; + MUTEX exchange_mtx_; ftl::rgbd::FrameSet fs_render_; ftl::rgbd::FrameSet fs_align_; diff --git a/applications/vision/src/main.cpp b/applications/vision/src/main.cpp index 4f245a077..058d3f989 100644 --- a/applications/vision/src/main.cpp +++ b/applications/vision/src/main.cpp @@ -24,6 +24,7 @@ #include <ftl/net/universe.hpp> #include <ftl/master.hpp> #include <nlohmann/json.hpp> +#include <ftl/operators/disparity.hpp> #include "opencv2/imgproc.hpp" #include "opencv2/imgcodecs.hpp" @@ -54,7 +55,7 @@ static void run(ftl::Configurable *root) { auto paths = root->get<vector<string>>("paths"); string file = ""; - if (paths && (*paths).size() > 0) file = (*paths)[0]; + if (paths && (*paths).size() > 0) file = (*paths)[(*paths).size()-1]; Source *source = nullptr; source = ftl::create<Source>(root, "source"); @@ -65,6 +66,10 @@ static void run(ftl::Configurable *root) { Streamer *stream = ftl::create<Streamer>(root, "stream", net); stream->add(source); + + auto pipeline = ftl::config::create<ftl::operators::Graph>(root, "pipeline"); + pipeline->append<ftl::operators::DepthChannel>("depth"); // Ensure there is a depth channel + stream->group()->addPipeline(pipeline); net->start(); diff --git a/components/codecs/include/ftl/codecs/channels.hpp b/components/codecs/include/ftl/codecs/channels.hpp index 9aa214302..dca2eef76 100644 --- a/components/codecs/include/ftl/codecs/channels.hpp +++ b/components/codecs/include/ftl/codecs/channels.hpp @@ -14,7 +14,6 @@ enum struct Channel : int { Depth = 1, // 32S or 32F Right = 2, // 8UC3 or 8UC4 Colour2 = 2, - Disparity = 3, Depth2 = 3, Deviation = 4, Screen = 4, @@ -24,7 +23,6 @@ enum struct Channel : int { Contribution = 7, // 32F EnergyVector = 8, // 32FC4 Flow = 9, // 32F - Smoothing = 9, // 32F Energy = 10, // 32F Mask = 11, // 32U Density = 12, // 32F @@ -32,7 +30,9 @@ enum struct Channel : int { Support2 = 14, // 8UC4 (currently) Segmentation = 15, // 32S? ColourNormals = 16, // 8UC4 - ColourHighRes = 20, // 8UC3 or 8UC4 + ColourHighRes = 17, // 8UC3 or 8UC4 + Disparity = 18, + Smoothing = 19, // 32F AudioLeft = 32, AudioRight = 33, @@ -102,6 +102,7 @@ class Channels { } inline size_t count() { return std::bitset<32>(mask).count(); } + inline bool empty() { return mask == 0; } inline void clear() { mask = 0; } static const size_t kMax = 32; diff --git a/components/codecs/include/ftl/codecs/opencv_decoder.hpp b/components/codecs/include/ftl/codecs/opencv_decoder.hpp index 53b61f683..0f96cc7ee 100644 --- a/components/codecs/include/ftl/codecs/opencv_decoder.hpp +++ b/components/codecs/include/ftl/codecs/opencv_decoder.hpp @@ -16,7 +16,7 @@ class OpenCVDecoder : public ftl::codecs::Decoder { bool accepts(const ftl::codecs::Packet &pkt); private: - cv::Mat tmp_; + //cv::Mat tmp_; }; } diff --git a/components/codecs/src/channels.cpp b/components/codecs/src/channels.cpp index effaf9eb2..b33427b70 100644 --- a/components/codecs/src/channels.cpp +++ b/components/codecs/src/channels.cpp @@ -8,9 +8,9 @@ struct ChannelInfo { }; static ChannelInfo info[] = { - "Colour", CV_8UC3, + "Colour", CV_8UC4, "Depth", CV_32F, - "Right", CV_8UC3, + "Right", CV_8UC4, "DepthRight", CV_32F, "Deviation", CV_32F, "Normals", CV_32FC4, diff --git a/components/codecs/src/nvpipe_decoder.cpp b/components/codecs/src/nvpipe_decoder.cpp index d6652549c..844029495 100644 --- a/components/codecs/src/nvpipe_decoder.cpp +++ b/components/codecs/src/nvpipe_decoder.cpp @@ -94,9 +94,9 @@ bool NvPipeDecoder::decode(const ftl::codecs::Packet &pkt, cv::cuda::GpuMat &out //if (out.rows == ftl::codecs::getHeight(pkt.definition)) { // Flag 0x1 means frame is in RGB so needs conversion to BGR if (pkt.flags & 0x1) { - cv::cuda::cvtColor(tmp_, out, cv::COLOR_RGBA2BGR, 0, stream_); + cv::cuda::cvtColor(tmp_, out, cv::COLOR_RGBA2BGRA, 0, stream_); } else { - cv::cuda::cvtColor(tmp_, out, cv::COLOR_BGRA2BGR, 0, stream_); + //cv::cuda::cvtColor(tmp_, out, cv::COLOR_BGRA2BGR, 0, stream_); } /*} else { LOG(WARNING) << "Resizing decoded frame from " << tmp_.size() << " to " << out.size(); diff --git a/components/codecs/src/opencv_decoder.cpp b/components/codecs/src/opencv_decoder.cpp index c3c5e9567..f721b462a 100644 --- a/components/codecs/src/opencv_decoder.cpp +++ b/components/codecs/src/opencv_decoder.cpp @@ -31,8 +31,15 @@ bool OpenCVDecoder::decode(const ftl::codecs::Packet &pkt, cv::cuda::GpuMat &out //LOG(INFO) << "DECODE JPEG " << (int)pkt.block_number << "/" << chunk_dim; + cv::Mat tmp2_, tmp_; // Decode in temporary buffers to prevent long locks - cv::imdecode(pkt.data, cv::IMREAD_UNCHANGED, &tmp_); + cv::imdecode(pkt.data, cv::IMREAD_UNCHANGED, &tmp2_); + + if (tmp2_.type() == CV_8UC3) { + cv::cvtColor(tmp2_, tmp_, cv::COLOR_BGR2BGRA); + } else { + tmp_ = tmp2_; + } // Apply colour correction to chunk //ftl::rgbd::colourCorrection(tmp_rgb, gamma_, temperature_); @@ -45,7 +52,7 @@ bool OpenCVDecoder::decode(const ftl::codecs::Packet &pkt, cv::cuda::GpuMat &out if (!tmp_.empty() && tmp_.type() == CV_16U && chunkHead.type() == CV_32F) { tmp_.convertTo(tmp_, CV_32FC1, 1.0f/1000.0f); chunkHead.upload(tmp_); - } else if (!tmp_.empty() && tmp_.type() == CV_8UC3 && chunkHead.type() == CV_8UC3) { + } else if (!tmp_.empty() && tmp_.type() == CV_8UC4 && chunkHead.type() == CV_8UC4) { //tmp_.copyTo(chunkHead); chunkHead.upload(tmp_); } else { @@ -57,7 +64,7 @@ bool OpenCVDecoder::decode(const ftl::codecs::Packet &pkt, cv::cuda::GpuMat &out tmp_.convertTo(tmp_, CV_32FC1, 1.0f/1000.0f); //(16.0f*10.0f)); cv::resize(tmp_, tmp_, chunkHead.size(), 0, 0, cv::INTER_NEAREST); chunkHead.upload(tmp_); - } else if (!tmp_.empty() && tmp_.type() == CV_8UC3 && chunkHead.type() == CV_8UC3) { + } else if (!tmp_.empty() && tmp_.type() == CV_8UC4 && chunkHead.type() == CV_8UC4) { cv::resize(tmp_, tmp_, chunkHead.size()); chunkHead.upload(tmp_); } else { diff --git a/components/codecs/test/opencv_codec_unit.cpp b/components/codecs/test/opencv_codec_unit.cpp index 961658db5..0982551bd 100644 --- a/components/codecs/test/opencv_codec_unit.cpp +++ b/components/codecs/test/opencv_codec_unit.cpp @@ -84,8 +84,8 @@ TEST_CASE( "OpenCVEncoder::encode() - A depth test image at preset 0" ) { TEST_CASE( "OpenCVDecoder::decode() - A colour test image no resolution change" ) { ftl::codecs::OpenCVEncoder encoder(definition_t::HD1080, definition_t::SD480); ftl::codecs::OpenCVDecoder decoder; - cv::cuda::GpuMat in(cv::Size(1024,576), CV_8UC3, cv::Scalar(255,0,0)); - cv::cuda::GpuMat out(cv::Size(1024,576), CV_8UC3, cv::Scalar(0,0,0)); + cv::cuda::GpuMat in(cv::Size(1024,576), CV_8UC4, cv::Scalar(255,0,0,0)); + cv::cuda::GpuMat out(cv::Size(1024,576), CV_8UC4, cv::Scalar(0,0,0,0)); std::mutex mtx; diff --git a/components/operators/CMakeLists.txt b/components/operators/CMakeLists.txt index b025a39b3..2a55cce0d 100644 --- a/components/operators/CMakeLists.txt +++ b/components/operators/CMakeLists.txt @@ -11,6 +11,8 @@ set(OPERSRC src/disparity/disp2depth.cu src/disparity/disparity_to_depth.cpp src/disparity/bilateral_filter.cpp + src/disparity/opencv/disparity_bilateral_filter.cpp + src/disparity/opencv/disparity_bilateral_filter.cu src/segmentation.cu src/segmentation.cpp src/mask.cu @@ -20,6 +22,7 @@ set(OPERSRC src/mvmls.cpp src/correspondence.cu src/clipping.cpp + src/depth.cpp ) diff --git a/components/operators/include/ftl/operators/colours.hpp b/components/operators/include/ftl/operators/colours.hpp index afad434d4..5f8ba6cd1 100644 --- a/components/operators/include/ftl/operators/colours.hpp +++ b/components/operators/include/ftl/operators/colours.hpp @@ -17,6 +17,7 @@ class ColourChannels : public ftl::operators::Operator { private: cv::cuda::GpuMat temp_; + cv::cuda::GpuMat rbuf_; }; } diff --git a/components/operators/include/ftl/operators/disparity.hpp b/components/operators/include/ftl/operators/disparity.hpp index 0c26c904f..e4ff99d78 100644 --- a/components/operators/include/ftl/operators/disparity.hpp +++ b/components/operators/include/ftl/operators/disparity.hpp @@ -77,6 +77,28 @@ class DisparityToDepth : public ftl::operators::Operator { bool apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd::Source *src, cudaStream_t stream) override; }; +/** + * Create a missing depth channel using Left and Right colour with a complete + * disparity pipeline. This is a wrapper operator that combines all of the + * disparity to depth steps. + */ +class DepthChannel : public ftl::operators::Operator { + public: + explicit DepthChannel(ftl::Configurable *cfg); + ~DepthChannel(); + + inline Operator::Type type() const override { return Operator::Type::ManyToMany; } + + bool apply(ftl::rgbd::FrameSet &in, ftl::rgbd::FrameSet &out, cudaStream_t stream) override; + + private: + ftl::operators::Graph *pipe_; + std::vector<cv::cuda::GpuMat> rbuf_; + cv::Size depth_size_; + + void _createPipeline(); +}; + /* * Optical flow smoothing for depth */ diff --git a/components/operators/src/colours.cpp b/components/operators/src/colours.cpp index 6a49f6ede..955bfed58 100644 --- a/components/operators/src/colours.cpp +++ b/components/operators/src/colours.cpp @@ -25,15 +25,39 @@ bool ColourChannels::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgb cv::cuda::cvtColor(temp_,col, cv::COLOR_BGR2BGRA, 0, cvstream); } + if (in.hasChannel(Channel::Right)) { + auto &col = in.get<cv::cuda::GpuMat>(Channel::Right); + + // Convert colour from BGR to BGRA if needed + if (col.type() == CV_8UC3) { + //cv::cuda::Stream cvstream = cv::cuda::StreamAccessor::wrapStream(stream); + // Convert to 4 channel colour + temp_.create(col.size(), CV_8UC4); + cv::cuda::swap(col, temp_); + cv::cuda::cvtColor(temp_,col, cv::COLOR_BGR2BGRA, 0, cvstream); + } + } + //in.resetTexture(Channel::Colour); in.createTexture<uchar4>(Channel::Colour, true); - auto &depth = in.get<cv::cuda::GpuMat>(Channel::Depth); - if (depth.size() != col.size()) { - auto &col2 = in.create<cv::cuda::GpuMat>(Channel::ColourHighRes); - cv::cuda::resize(col, col2, depth.size(), 0.0, 0.0, cv::INTER_LINEAR, cvstream); - in.createTexture<uchar4>(Channel::ColourHighRes, true); - in.swapChannels(Channel::Colour, Channel::ColourHighRes); + if (in.hasChannel(Channel::Depth)) { + auto &depth = in.get<cv::cuda::GpuMat>(Channel::Depth); + if (depth.size() != col.size()) { + auto &col2 = in.create<cv::cuda::GpuMat>(Channel::ColourHighRes); + cv::cuda::resize(col, col2, depth.size(), 0.0, 0.0, cv::INTER_LINEAR, cvstream); + in.createTexture<uchar4>(Channel::ColourHighRes, true); + in.swapChannels(Channel::Colour, Channel::ColourHighRes); + } + + // Ensure right channel is also downsized + if (in.hasChannel(Channel::Right)) { + auto &right = in.get<cv::cuda::GpuMat>(Channel::Right); + if (depth.size() != right.size()) { + cv::cuda::resize(right, rbuf_, depth.size(), 0.0, 0.0, cv::INTER_CUBIC, cvstream); + cv::cuda::swap(right, rbuf_); + } + } } return true; diff --git a/components/operators/src/depth.cpp b/components/operators/src/depth.cpp new file mode 100644 index 000000000..3d332d6ae --- /dev/null +++ b/components/operators/src/depth.cpp @@ -0,0 +1,76 @@ +#include <ftl/operators/disparity.hpp> + +#include "ftl/operators/smoothing.hpp" +#include "ftl/operators/colours.hpp" +#include "ftl/operators/normals.hpp" +#include "ftl/operators/filling.hpp" +#include "ftl/operators/segmentation.hpp" +#include "ftl/operators/disparity.hpp" +#include "ftl/operators/mask.hpp" + +using ftl::operators::DepthChannel; +using ftl::codecs::Channel; + +DepthChannel::DepthChannel(ftl::Configurable *cfg) : ftl::operators::Operator(cfg) { + pipe_ = nullptr; +} + +DepthChannel::~DepthChannel() { + +} + +void DepthChannel::_createPipeline() { + if (pipe_ != nullptr) return; + + pipe_ = ftl::config::create<ftl::operators::Graph>(config(), "depth"); + depth_size_ = cv::Size( pipe_->value("width", 1280), + pipe_->value("height", 720)); + + pipe_->append<ftl::operators::ColourChannels>("colour"); // Convert BGR to BGRA + pipe_->append<ftl::operators::FixstarsSGM>("algorithm"); + #ifdef HAVE_OPTFLOW + pipe_->append<ftl::operators::OpticalFlowTemporalSmoothing>("optflow_filter"); + #endif + pipe_->append<ftl::operators::DisparityBilateralFilter>("bilateral_filter"); + pipe_->append<ftl::operators::DisparityToDepth>("calculate_depth"); + pipe_->append<ftl::operators::Normals>("normals"); // Estimate surface normals + pipe_->append<ftl::operators::CrossSupport>("cross"); + pipe_->append<ftl::operators::DiscontinuityMask>("discontinuity_mask"); + pipe_->append<ftl::operators::AggreMLS>("mls"); // Perform MLS (using smoothing channel) +} + +bool DepthChannel::apply(ftl::rgbd::FrameSet &in, ftl::rgbd::FrameSet &out, cudaStream_t stream) { + auto cvstream = cv::cuda::StreamAccessor::wrapStream(stream); + + rbuf_.resize(in.frames.size()); + + for (int i=0; i<in.frames.size(); ++i) { + auto &f = in.frames[i]; + if (!f.hasChannel(Channel::Depth) && f.hasChannel(Channel::Right)) { + _createPipeline(); + + cv::cuda::GpuMat& left = f.get<cv::cuda::GpuMat>(Channel::Left); + cv::cuda::GpuMat& right = f.get<cv::cuda::GpuMat>(Channel::Right); + cv::cuda::GpuMat& depth = f.create<cv::cuda::GpuMat>(Channel::Depth); + depth.create(left.size(), CV_32FC1); + + if (left.empty() || right.empty()) continue; + + /*if (depth_size_ != left.size()) { + auto &col2 = f.create<cv::cuda::GpuMat>(Channel::ColourHighRes); + cv::cuda::resize(left, col2, depth_size_, 0.0, 0.0, cv::INTER_CUBIC, cvstream); + f.createTexture<uchar4>(Channel::ColourHighRes, true); + f.swapChannels(Channel::Colour, Channel::ColourHighRes); + } + + if (depth_size_ != right.size()) { + cv::cuda::resize(right, rbuf_[i], depth_size_, 0.0, 0.0, cv::INTER_CUBIC, cvstream); + cv::cuda::swap(right, rbuf_[i]); + }*/ + + pipe_->apply(f, f, in.sources[i], stream); + } + } + + return true; +} diff --git a/components/operators/src/disparity/bilateral_filter.cpp b/components/operators/src/disparity/bilateral_filter.cpp index 8c91fac6f..13f2ed1ea 100644 --- a/components/operators/src/disparity/bilateral_filter.cpp +++ b/components/operators/src/disparity/bilateral_filter.cpp @@ -1,5 +1,8 @@ #include <ftl/operators/disparity.hpp> +#include "opencv/joint_bilateral.hpp" +#include "cuda.hpp" + using cv::cuda::GpuMat; using cv::Size; @@ -13,15 +16,36 @@ DisparityBilateralFilter::DisparityBilateralFilter(ftl::Configurable* cfg) : n_disp_ = cfg->value("n_disp", 256); radius_ = cfg->value("radius", 7); iter_ = cfg->value("iter", 13); - filter_ = cv::cuda::createDisparityBilateralFilter(n_disp_ * scale_, radius_, iter_); + filter_ = nullptr; } bool DisparityBilateralFilter::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd::Source *src, cudaStream_t stream) { - if (!in.hasChannel(Channel::Disparity) || !in.hasChannel(Channel::Colour)) { + if (!in.hasChannel(Channel::Colour)) { + LOG(ERROR) << "Joint Bilateral Filter is missing Colour"; return false; + } else if (!in.hasChannel(Channel::Disparity)) { + // Have depth, so calculate disparity... + if (in.hasChannel(Channel::Depth)) { + // No disparity, so create it. + const auto params = src->parameters(); + const GpuMat &depth = in.get<GpuMat>(Channel::Depth); + + GpuMat &disp = out.create<GpuMat>(Channel::Disparity); + disp.create(depth.size(), CV_32FC1); + + LOG(ERROR) << "Calculated disparity from depth"; + + ftl::cuda::depth_to_disparity(disp, depth, params, stream); + } else { + LOG(ERROR) << "Joint Bilateral Filter is missing Disparity and Depth"; + return false; + } } + + if (!filter_) filter_ = ftl::cuda::createDisparityBilateralFilter(n_disp_ * scale_, radius_, iter_); + auto cvstream = cv::cuda::StreamAccessor::wrapStream(stream); const GpuMat &rgb = in.get<GpuMat>(Channel::Colour); GpuMat &disp_in = in.get<GpuMat>(Channel::Disparity); diff --git a/components/operators/src/disparity/cuda.hpp b/components/operators/src/disparity/cuda.hpp index d4cf06aae..48c8ffd1b 100644 --- a/components/operators/src/disparity/cuda.hpp +++ b/components/operators/src/disparity/cuda.hpp @@ -6,6 +6,9 @@ namespace cuda { void disparity_to_depth(const cv::cuda::GpuMat &disparity, cv::cuda::GpuMat &depth, const ftl::rgbd::Camera &c, cudaStream_t &stream); +void depth_to_disparity(cv::cuda::GpuMat &disparity, const cv::cuda::GpuMat &depth, + const ftl::rgbd::Camera &c, cudaStream_t &stream); + void optflow_filter(cv::cuda::GpuMat &disp, const cv::cuda::GpuMat &optflow, cv::cuda::GpuMat &history, int n_max, float threshold, diff --git a/components/operators/src/disparity/disp2depth.cu b/components/operators/src/disparity/disp2depth.cu index 86a3fc290..a2c60def9 100644 --- a/components/operators/src/disparity/disp2depth.cu +++ b/components/operators/src/disparity/disp2depth.cu @@ -27,3 +27,32 @@ namespace cuda { } } } + +//============================================================================== + +__global__ void d2drev_kernel(cv::cuda::PtrStepSz<float> disp, cv::cuda::PtrStepSz<float> depth, + ftl::rgbd::Camera cam) { + +for (STRIDE_Y(v,disp.rows)) { +for (STRIDE_X(u,disp.cols)) { + float d = depth(v,u); + float disparity = (d > cam.maxDepth || d < cam.minDepth) ? 0.0f : ((cam.baseline*cam.fx) / d) + cam.doffs; + disp(v,u) = disparity; +} +} +} + +namespace ftl { +namespace cuda { +void depth_to_disparity(cv::cuda::GpuMat &disparity, const cv::cuda::GpuMat &depth, + const ftl::rgbd::Camera &c, cudaStream_t &stream) { + dim3 grid(1,1,1); + dim3 threads(128, 1, 1); + grid.x = cv::cuda::device::divUp(disparity.cols, 128); + grid.y = cv::cuda::device::divUp(disparity.rows, 1); + d2drev_kernel<<<grid, threads, 0, stream>>>( + disparity, depth, c); + cudaSafeCall( cudaGetLastError() ); +} +} +} diff --git a/components/operators/src/disparity/disparity_to_depth.cpp b/components/operators/src/disparity/disparity_to_depth.cpp index 5951802f0..d3cc33772 100644 --- a/components/operators/src/disparity/disparity_to_depth.cpp +++ b/components/operators/src/disparity/disparity_to_depth.cpp @@ -9,7 +9,10 @@ using cv::cuda::GpuMat; bool DisparityToDepth::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd::Source *src, cudaStream_t stream) { - if (!in.hasChannel(Channel::Disparity)) { return false; } + if (!in.hasChannel(Channel::Disparity)) { + LOG(ERROR) << "Missing disparity before convert to depth"; + return false; + } const auto params = src->parameters(); const GpuMat &disp = in.get<GpuMat>(Channel::Disparity); diff --git a/components/operators/src/disparity/fixstars_sgm.cpp b/components/operators/src/disparity/fixstars_sgm.cpp index 79a18685e..ae15033a2 100644 --- a/components/operators/src/disparity/fixstars_sgm.cpp +++ b/components/operators/src/disparity/fixstars_sgm.cpp @@ -89,6 +89,8 @@ bool FixstarsSGM::init() { rbw_.create(size_, CV_8UC1); disp_int_.create(size_, CV_16SC1); + LOG(INFO) << "INIT FIXSTARS"; + ssgm_ = new sgm::StereoSGM(size_.width, size_.height, max_disp_, 8, 16, lbw_.step, disp_int_.step / sizeof(short), sgm::EXECUTE_INOUT_CUDA2CUDA, @@ -106,12 +108,13 @@ bool FixstarsSGM::updateParameters() { bool FixstarsSGM::apply(Frame &in, Frame &out, Source *src, cudaStream_t stream) { if (!in.hasChannel(Channel::Left) || !in.hasChannel(Channel::Right)) { + LOG(ERROR) << "Fixstars is missing Left or Right channel"; return false; } const auto &l = in.get<GpuMat>(Channel::Left); const auto &r = in.get<GpuMat>(Channel::Right); - + if (l.size() != size_) { size_ = l.size(); if (!init()) { return false; } @@ -120,8 +123,8 @@ bool FixstarsSGM::apply(Frame &in, Frame &out, Source *src, cudaStream_t stream) auto &disp = out.create<GpuMat>(Channel::Disparity, Format<float>(l.size())); auto cvstream = cv::cuda::StreamAccessor::wrapStream(stream); - cv::cuda::cvtColor(l, lbw_, cv::COLOR_BGR2GRAY, 0, cvstream); - cv::cuda::cvtColor(r, rbw_, cv::COLOR_BGR2GRAY, 0, cvstream); + cv::cuda::cvtColor(l, lbw_, cv::COLOR_BGRA2GRAY, 0, cvstream); + cv::cuda::cvtColor(r, rbw_, cv::COLOR_BGRA2GRAY, 0, cvstream); cvstream.waitForCompletion(); ssgm_->execute(lbw_.data, rbw_.data, disp_int_.data); diff --git a/components/operators/src/disparity/opencv/disparity_bilateral_filter.cpp b/components/operators/src/disparity/opencv/disparity_bilateral_filter.cpp new file mode 100644 index 000000000..447982808 --- /dev/null +++ b/components/operators/src/disparity/opencv/disparity_bilateral_filter.cpp @@ -0,0 +1,200 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#include <ftl/cuda_common.hpp> +#include <opencv2/cudastereo.hpp> +#include <opencv2/core/cuda_stream_accessor.hpp> +#include "joint_bilateral.hpp" + +using namespace cv; +using namespace cv::cuda; + +#if !defined (HAVE_CUDA) || defined (CUDA_DISABLER) + +Ptr<cuda::DisparityBilateralFilter> ftl::cuda::createDisparityBilateralFilter(int, int, int) { throw_no_cuda(); return Ptr<cuda::DisparityBilateralFilter>(); } + +#else /* !defined (HAVE_CUDA) */ + +#include "disparity_bilateral_filter.hpp" + +namespace +{ + class DispBilateralFilterImpl : public cv::cuda::DisparityBilateralFilter + { + public: + DispBilateralFilterImpl(int ndisp, int radius, int iters); + + void apply(InputArray disparity, InputArray image, OutputArray dst, Stream& stream); + + int getNumDisparities() const { return ndisp_; } + void setNumDisparities(int numDisparities) { ndisp_ = numDisparities; } + + int getRadius() const { return radius_; } + void setRadius(int radius); + + int getNumIters() const { return iters_; } + void setNumIters(int iters) { iters_ = iters; } + + double getEdgeThreshold() const { return edge_threshold_; } + void setEdgeThreshold(double edge_threshold) { edge_threshold_ = (float) edge_threshold; } + + double getMaxDiscThreshold() const { return max_disc_threshold_; } + void setMaxDiscThreshold(double max_disc_threshold) { max_disc_threshold_ = (float) max_disc_threshold; } + + double getSigmaRange() const { return sigma_range_; } + void setSigmaRange(double sigma_range); + + private: + int ndisp_; + int radius_; + int iters_; + float edge_threshold_; + float max_disc_threshold_; + float sigma_range_; + + GpuMat table_color_; + GpuMat table_space_; + }; + + void calc_color_weighted_table(GpuMat& table_color, float sigma_range, int len) + { + Mat cpu_table_color(1, len, CV_32F); + + float* line = cpu_table_color.ptr<float>(); + + for(int i = 0; i < len; i++) + line[i] = static_cast<float>(std::exp(-double(i * i) / (2 * sigma_range * sigma_range))); + + table_color.upload(cpu_table_color); + } + + void calc_space_weighted_filter(GpuMat& table_space, int win_size, float dist_space) + { + int half = (win_size >> 1); + + Mat cpu_table_space(half + 1, half + 1, CV_32F); + + for (int y = 0; y <= half; ++y) + { + float* row = cpu_table_space.ptr<float>(y); + for (int x = 0; x <= half; ++x) + row[x] = exp(-sqrt(float(y * y) + float(x * x)) / dist_space); + } + + table_space.upload(cpu_table_space); + } + + const float DEFAULT_EDGE_THRESHOLD = 0.1f; + const float DEFAULT_MAX_DISC_THRESHOLD = 0.2f; + const float DEFAULT_SIGMA_RANGE = 10.0f; + + DispBilateralFilterImpl::DispBilateralFilterImpl(int ndisp, int radius, int iters) : + ndisp_(ndisp), radius_(radius), iters_(iters), + edge_threshold_(DEFAULT_EDGE_THRESHOLD), max_disc_threshold_(DEFAULT_MAX_DISC_THRESHOLD), + sigma_range_(DEFAULT_SIGMA_RANGE) + { + calc_color_weighted_table(table_color_, sigma_range_, 255); + calc_space_weighted_filter(table_space_, radius_ * 2 + 1, radius_ + 1.0f); + } + + void DispBilateralFilterImpl::setRadius(int radius) + { + radius_ = radius; + calc_space_weighted_filter(table_space_, radius_ * 2 + 1, radius_ + 1.0f); + } + + void DispBilateralFilterImpl::setSigmaRange(double sigma_range) + { + sigma_range_ = (float) sigma_range; + calc_color_weighted_table(table_color_, sigma_range_, 255); + } + + template <typename T> + void disp_bilateral_filter_operator(int ndisp, int radius, int iters, float edge_threshold, float max_disc_threshold, + GpuMat& table_color, GpuMat& table_space, + const GpuMat& disp, const GpuMat& img, + OutputArray _dst, Stream& stream) + { + using namespace ftl::cuda::device::disp_bilateral_filter; + + const short edge_disc = std::max<short>(short(1), short(ndisp * edge_threshold + 0.5)); + const short max_disc = short(ndisp * max_disc_threshold + 0.5); + + size_t table_space_step = table_space.step / sizeof(float); + + _dst.create(disp.size(), disp.type()); + GpuMat dst = _dst.getGpuMat(); + + if (dst.data != disp.data) + disp.copyTo(dst, stream); + + disp_bilateral_filter<T>(dst, img, img.channels(), iters, table_color.ptr<float>(), (float *)table_space.data, table_space_step, radius, edge_disc, max_disc, StreamAccessor::getStream(stream)); + } + + void DispBilateralFilterImpl::apply(InputArray _disp, InputArray _image, OutputArray dst, Stream& stream) + { + typedef void (*bilateral_filter_operator_t)(int ndisp, int radius, int iters, float edge_threshold, float max_disc_threshold, + GpuMat& table_color, GpuMat& table_space, + const GpuMat& disp, const GpuMat& img, OutputArray dst, Stream& stream); + const bilateral_filter_operator_t operators[] = + {disp_bilateral_filter_operator<unsigned char>, 0, 0, disp_bilateral_filter_operator<short>, 0, 0, 0, 0}; + + CV_Assert( 0 < ndisp_ && 0 < radius_ && 0 < iters_ ); + + GpuMat disp = _disp.getGpuMat(); + GpuMat img = _image.getGpuMat(); + + CV_Assert( disp.type() == CV_8U || disp.type() == CV_16S ); + CV_Assert( img.type() == CV_8UC1 || img.type() == CV_8UC3 || img.type() == CV_8UC4 ); + CV_Assert( disp.size() == img.size() ); + + operators[disp.type()](ndisp_, radius_, iters_, edge_threshold_, max_disc_threshold_, + table_color_, table_space_, disp, img, dst, stream); + } +} + +Ptr<cuda::DisparityBilateralFilter> ftl::cuda::createDisparityBilateralFilter(int ndisp, int radius, int iters) +{ + return makePtr<DispBilateralFilterImpl>(ndisp, radius, iters); +} + +#endif /* !defined (HAVE_CUDA) */ diff --git a/components/operators/src/disparity/opencv/disparity_bilateral_filter.cu b/components/operators/src/disparity/opencv/disparity_bilateral_filter.cu new file mode 100644 index 000000000..134095101 --- /dev/null +++ b/components/operators/src/disparity/opencv/disparity_bilateral_filter.cu @@ -0,0 +1,220 @@ +/*M/////////////////////////////////////////////////////////////////////////////////////// +// +// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING. +// +// By downloading, copying, installing or using the software you agree to this license. +// If you do not agree to this license, do not download, install, +// copy or use the software. +// +// +// License Agreement +// For Open Source Computer Vision Library +// +// Copyright (C) 2000-2008, Intel Corporation, all rights reserved. +// Copyright (C) 2009, Willow Garage Inc., all rights reserved. +// Third party copyrights are property of their respective owners. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistribution's of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistribution's in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * The name of the copyright holders may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// This software is provided by the copyright holders and contributors "as is" and +// any express or implied warranties, including, but not limited to, the implied +// warranties of merchantability and fitness for a particular purpose are disclaimed. +// In no event shall the Intel Corporation or contributors be liable for any direct, +// indirect, incidental, special, exemplary, or consequential damages +// (including, but not limited to, procurement of substitute goods or services; +// loss of use, data, or profits; or business interruption) however caused +// and on any theory of liability, whether in contract, strict liability, +// or tort (including negligence or otherwise) arising in any way out of +// the use of this software, even if advised of the possibility of such damage. +// +//M*/ + +#if !defined CUDA_DISABLER + +#include "opencv2/core/cuda/common.hpp" +#include "opencv2/core/cuda/limits.hpp" + +#include "disparity_bilateral_filter.hpp" + +using namespace cv::cuda::device; +using namespace cv::cuda; +using namespace cv; + +namespace ftl { namespace cuda { namespace device +{ + namespace disp_bilateral_filter + { + template <int channels> + struct DistRgbMax + { + static __device__ __forceinline__ uchar calc(const uchar* a, const uchar* b) + { + // TODO: (Nick) Is this the best way to read for performance? + uchar x = ::abs(a[0] - b[0]); + uchar y = ::abs(a[1] - b[1]); + uchar z = ::abs(a[2] - b[2]); + return (::max(::max(x, y), z)); + } + }; + + template <> + struct DistRgbMax<1> + { + static __device__ __forceinline__ uchar calc(const uchar* a, const uchar* b) + { + return ::abs(a[0] - b[0]); + } + }; + + template <int channels, typename T> + __global__ void disp_bilateral_filter(int t, T* disp, size_t disp_step, + const uchar* img, size_t img_step, int h, int w, + const float* ctable_color, const float * ctable_space, size_t ctable_space_step, + int cradius, + short cedge_disc, short cmax_disc) + { + const int y = blockIdx.y * blockDim.y + threadIdx.y; + const int x = ((blockIdx.x * blockDim.x + threadIdx.x) << 1) + ((y + t) & 1); + + T dp[5]; + + if (y > 0 && y < h - 1 && x > 0 && x < w - 1) + { + dp[0] = *(disp + (y ) * disp_step + x + 0); + dp[1] = *(disp + (y-1) * disp_step + x + 0); + dp[2] = *(disp + (y ) * disp_step + x - 1); + dp[3] = *(disp + (y+1) * disp_step + x + 0); + dp[4] = *(disp + (y ) * disp_step + x + 1); + + if(::abs(dp[1] - dp[0]) >= cedge_disc || ::abs(dp[2] - dp[0]) >= cedge_disc || ::abs(dp[3] - dp[0]) >= cedge_disc || ::abs(dp[4] - dp[0]) >= cedge_disc) + { + const int ymin = ::max(0, y - cradius); + const int xmin = ::max(0, x - cradius); + const int ymax = ::min(h - 1, y + cradius); + const int xmax = ::min(w - 1, x + cradius); + + float cost[] = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f}; + + const uchar* ic = img + y * img_step + channels * x; + + for(int yi = ymin; yi <= ymax; yi++) + { + const T* disp_y = disp + yi * disp_step; + + for(int xi = xmin; xi <= xmax; xi++) + { + const uchar* in = img + yi * img_step + channels * xi; + + uchar dist_rgb = DistRgbMax<channels>::calc(in, ic); + + const float weight = ctable_color[dist_rgb] * (ctable_space + ::abs(y-yi)* ctable_space_step)[::abs(x-xi)]; + + const T disp_reg = disp_y[xi]; + + cost[0] += ::min(cmax_disc, ::abs(disp_reg - dp[0])) * weight; + cost[1] += ::min(cmax_disc, ::abs(disp_reg - dp[1])) * weight; + cost[2] += ::min(cmax_disc, ::abs(disp_reg - dp[2])) * weight; + cost[3] += ::min(cmax_disc, ::abs(disp_reg - dp[3])) * weight; + cost[4] += ::min(cmax_disc, ::abs(disp_reg - dp[4])) * weight; + } + } + + float minimum = numeric_limits<float>::max(); + int id = 0; + + if (cost[0] < minimum) + { + minimum = cost[0]; + id = 0; + } + if (cost[1] < minimum) + { + minimum = cost[1]; + id = 1; + } + if (cost[2] < minimum) + { + minimum = cost[2]; + id = 2; + } + if (cost[3] < minimum) + { + minimum = cost[3]; + id = 3; + } + if (cost[4] < minimum) + { + minimum = cost[4]; + id = 4; + } + + *(disp + y * disp_step + x) = dp[id]; + } + } + } + + template <typename T> + void disp_bilateral_filter(cv::cuda::PtrStepSz<T> disp, cv::cuda::PtrStepSzb img, int channels, int iters, const float *table_color, const float* table_space, size_t table_step, int radius, short edge_disc, short max_disc, cudaStream_t stream) + { + dim3 threads(32, 8, 1); + dim3 grid(1, 1, 1); + grid.x = divUp(disp.cols, threads.x << 1); + grid.y = divUp(disp.rows, threads.y); + + switch (channels) + { + case 1: + for (int i = 0; i < iters; ++i) + { + disp_bilateral_filter<1><<<grid, threads, 0, stream>>>(0, disp.data, disp.step/sizeof(T), img.data, img.step, disp.rows, disp.cols, table_color, table_space, table_step, radius, edge_disc, max_disc); + cudaSafeCall( cudaGetLastError() ); + + disp_bilateral_filter<1><<<grid, threads, 0, stream>>>(1, disp.data, disp.step/sizeof(T), img.data, img.step, disp.rows, disp.cols, table_color, table_space, table_step, radius, edge_disc, max_disc); + cudaSafeCall( cudaGetLastError() ); + } + break; + case 3: + for (int i = 0; i < iters; ++i) + { + disp_bilateral_filter<3><<<grid, threads, 0, stream>>>(0, disp.data, disp.step/sizeof(T), img.data, img.step, disp.rows, disp.cols, table_color, table_space, table_step, radius, edge_disc, max_disc); + cudaSafeCall( cudaGetLastError() ); + + disp_bilateral_filter<3><<<grid, threads, 0, stream>>>(1, disp.data, disp.step/sizeof(T), img.data, img.step, disp.rows, disp.cols, table_color, table_space, table_step, radius, edge_disc, max_disc); + cudaSafeCall( cudaGetLastError() ); + } + break; + case 4: // Nick: Support 4 channel + for (int i = 0; i < iters; ++i) + { + disp_bilateral_filter<4><<<grid, threads, 0, stream>>>(0, disp.data, disp.step/sizeof(T), img.data, img.step, disp.rows, disp.cols, table_color, table_space, table_step, radius, edge_disc, max_disc); + cudaSafeCall( cudaGetLastError() ); + + disp_bilateral_filter<4><<<grid, threads, 0, stream>>>(1, disp.data, disp.step/sizeof(T), img.data, img.step, disp.rows, disp.cols, table_color, table_space, table_step, radius, edge_disc, max_disc); + cudaSafeCall( cudaGetLastError() ); + } + break; + default: + CV_Error(cv::Error::BadNumChannels, "Unsupported channels count"); + } + + if (stream == 0) + cudaSafeCall( cudaDeviceSynchronize() ); + } + + template void disp_bilateral_filter<uchar>(cv::cuda::PtrStepSz<uchar> disp, cv::cuda::PtrStepSzb img, int channels, int iters, const float *table_color, const float *table_space, size_t table_step, int radius, short, short, cudaStream_t stream); + template void disp_bilateral_filter<short>(cv::cuda::PtrStepSz<short> disp, cv::cuda::PtrStepSzb img, int channels, int iters, const float *table_color, const float *table_space, size_t table_step, int radius, short, short, cudaStream_t stream); + } // namespace bilateral_filter +}}} // namespace ftl { namespace cuda { namespace cudev + +#endif /* CUDA_DISABLER */ diff --git a/components/operators/src/disparity/opencv/disparity_bilateral_filter.hpp b/components/operators/src/disparity/opencv/disparity_bilateral_filter.hpp new file mode 100644 index 000000000..1f39d07f0 --- /dev/null +++ b/components/operators/src/disparity/opencv/disparity_bilateral_filter.hpp @@ -0,0 +1,8 @@ +namespace ftl { namespace cuda { namespace device +{ + namespace disp_bilateral_filter + { + template<typename T> + void disp_bilateral_filter(cv::cuda::PtrStepSz<T> disp, cv::cuda::PtrStepSzb img, int channels, int iters, const float *, const float *, size_t, int radius, short edge_disc, short max_disc, cudaStream_t stream); + } +}}} diff --git a/components/operators/src/disparity/opencv/joint_bilateral.hpp b/components/operators/src/disparity/opencv/joint_bilateral.hpp new file mode 100644 index 000000000..aeb4d9e07 --- /dev/null +++ b/components/operators/src/disparity/opencv/joint_bilateral.hpp @@ -0,0 +1,12 @@ +#ifndef _FTL_CUDA_OPENCV_BILATERAL_HPP_ +#define _FTL_CUDA_OPENCV_BILATERAL_HPP_ + +#include <ftl/cuda_common.hpp> + +namespace ftl { +namespace cuda { + cv::Ptr<cv::cuda::DisparityBilateralFilter> createDisparityBilateralFilter(int ndisp, int radius, int iters); +} +} + +#endif // _FTL_CUDA_OPENCV_BILATERAL_HPP_ diff --git a/components/operators/src/mask.cpp b/components/operators/src/mask.cpp index c7dcbb2ac..e914a5ad8 100644 --- a/components/operators/src/mask.cpp +++ b/components/operators/src/mask.cpp @@ -15,9 +15,13 @@ DiscontinuityMask::~DiscontinuityMask() { } bool DiscontinuityMask::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd::Source *s, cudaStream_t stream) { + if (in.hasChannel(Channel::Mask)) return true; + int radius = config()->value("radius", 2); float threshold = config()->value("threshold", 0.1f); + if (!in.hasChannel(Channel::Depth) || !in.hasChannel(Channel::Support1)) return false; + ftl::cuda::discontinuity( out.createTexture<int>(Channel::Mask, ftl::rgbd::Format<int>(in.get<cv::cuda::GpuMat>(Channel::Depth).size())), in.createTexture<uchar4>(Channel::Support1), diff --git a/components/operators/src/normals.cpp b/components/operators/src/normals.cpp index 57903aa1d..65fcef37b 100644 --- a/components/operators/src/normals.cpp +++ b/components/operators/src/normals.cpp @@ -22,7 +22,8 @@ bool Normals::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd::Sour } if (out.hasChannel(Channel::Normals)) { - LOG(WARNING) << "Output already has normals"; + //LOG(WARNING) << "Output already has normals"; + return true; } ftl::cuda::normals( @@ -53,7 +54,8 @@ bool SmoothNormals::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd } if (out.hasChannel(Channel::Normals)) { - LOG(WARNING) << "Output already has normals"; + //LOG(WARNING) << "Output already has normals"; + return true; } auto &depth = in.get<cv::cuda::GpuMat>(Channel::Depth); diff --git a/components/operators/src/operator.cpp b/components/operators/src/operator.cpp index dd821ee6f..6b796267d 100644 --- a/components/operators/src/operator.cpp +++ b/components/operators/src/operator.cpp @@ -52,15 +52,22 @@ bool Graph::apply(FrameSet &in, FrameSet &out, cudaStream_t stream) { if (i.instances[0]->type() == Operator::Type::OneToOne) { // Make sure there are enough instances - while (i.instances.size() < in.frames.size()) { + //while (i.instances.size() < in.frames.size()) { + //i.instances.push_back(i.maker->make()); + //} + if (in.frames.size() > 1) { i.instances.push_back(i.maker->make()); } for (int j=0; j<in.frames.size(); ++j) { - auto *instance = i.instances[j]; + auto *instance = i.instances[j&0x1]; if (instance->enabled()) { - instance->apply(in.frames[j], out.frames[j], in.sources[j], stream_actual); + try { + if (!instance->apply(in.frames[j], out.frames[j], in.sources[j], stream_actual)) return false; + } catch (const std::exception &e) { + LOG(ERROR) << "Operator exception: " << e.what(); + } } } } else if (i.instances[0]->type() == Operator::Type::ManyToMany) { @@ -68,7 +75,7 @@ bool Graph::apply(FrameSet &in, FrameSet &out, cudaStream_t stream) { if (instance->enabled()) { try { - instance->apply(in, out, stream_actual); + if (!instance->apply(in, out, stream_actual)) return false; } catch (const std::exception &e) { LOG(ERROR) << "Operator exception: " << e.what(); } @@ -96,7 +103,7 @@ bool Graph::apply(Frame &in, Frame &out, Source *s, cudaStream_t stream) { auto *instance = i.instances[0]; if (instance->enabled()) { - instance->apply(in, out, s, stream); + if (!instance->apply(in, out, s, stream)) return false; } } diff --git a/components/operators/src/segmentation.cpp b/components/operators/src/segmentation.cpp index 9bf7605e1..005c436d2 100644 --- a/components/operators/src/segmentation.cpp +++ b/components/operators/src/segmentation.cpp @@ -16,7 +16,9 @@ CrossSupport::~CrossSupport() { bool CrossSupport::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd::Source *s, cudaStream_t stream) { bool use_mask = config()->value("discon_support", false); - if (use_mask) { + + if (use_mask && !in.hasChannel(Channel::Support2)) { + if (!in.hasChannel(Channel::Mask)) return false; ftl::cuda::support_region( in.createTexture<int>(Channel::Mask), out.createTexture<uchar4>(Channel::Support2, ftl::rgbd::Format<uchar4>(in.get<cv::cuda::GpuMat>(Channel::Colour).size())), @@ -24,7 +26,7 @@ bool CrossSupport::apply(ftl::rgbd::Frame &in, ftl::rgbd::Frame &out, ftl::rgbd: config()->value("h_max", 5), config()->value("symmetric", false), stream ); - } else { + } else if (!in.hasChannel(Channel::Support1)) { ftl::cuda::support_region( in.createTexture<uchar4>(Channel::Colour), out.createTexture<uchar4>(Channel::Support1, ftl::rgbd::Format<uchar4>(in.get<cv::cuda::GpuMat>(Channel::Colour).size())), diff --git a/components/renderers/cpp/include/ftl/render/tri_render.hpp b/components/renderers/cpp/include/ftl/render/tri_render.hpp index 27b6db318..d15ba9c73 100644 --- a/components/renderers/cpp/include/ftl/render/tri_render.hpp +++ b/components/renderers/cpp/include/ftl/render/tri_render.hpp @@ -44,6 +44,7 @@ class Triangular : public ftl::render::Renderer { float3 light_pos_; Eigen::Matrix4d transform_; float scale_; + int64_t last_frame_; cv::cuda::GpuMat env_image_; ftl::cuda::TextureObject<uchar4> env_tex_; @@ -55,6 +56,8 @@ class Triangular : public ftl::render::Renderer { void _reprojectChannel(ftl::rgbd::Frame &, ftl::codecs::Channel in, ftl::codecs::Channel out, const Eigen::Matrix4d &t, cudaStream_t); void _dibr(ftl::rgbd::Frame &, const Eigen::Matrix4d &t, cudaStream_t); void _mesh(ftl::rgbd::Frame &, ftl::rgbd::Source *, const Eigen::Matrix4d &t, cudaStream_t); + + bool _alreadySeen() const { return last_frame_ == scene_->timestamp; } }; } diff --git a/components/renderers/cpp/src/tri_render.cpp b/components/renderers/cpp/src/tri_render.cpp index d1d0894f4..6df15bbd1 100644 --- a/components/renderers/cpp/src/tri_render.cpp +++ b/components/renderers/cpp/src/tri_render.cpp @@ -147,6 +147,7 @@ Triangular::Triangular(nlohmann::json &config, ftl::rgbd::FrameSet *fs) : ftl::r //filters_ = ftl::create<ftl::Filters>(this, "filters"); //filters_->create<ftl::filters::DepthSmoother>("hfnoise"); + last_frame_ = -1; } Triangular::~Triangular() { @@ -579,6 +580,13 @@ bool Triangular::render(ftl::rgbd::VirtualSource *src, ftl::rgbd::Frame &out, co int aligned_source = value("aligned_source",-1); if (aligned_source >= 0 && aligned_source < scene_->frames.size()) { + // Can only send at originally received frame rate due to reuse of + // encodings that can't be sent twice. + if (_alreadySeen()) { + out.reset(); + return false; + } + // FIXME: Output may not be same resolution as source! cudaSafeCall(cudaStreamSynchronize(stream_)); scene_->frames[aligned_source].copyTo(Channel::Depth + Channel::Colour + Channel::Smoothing + Channel::Confidence, out); @@ -596,6 +604,7 @@ bool Triangular::render(ftl::rgbd::VirtualSource *src, ftl::rgbd::Frame &out, co //out.resetTexture(Channel::Normals); } + last_frame_ = scene_->timestamp; return true; } @@ -704,5 +713,6 @@ bool Triangular::render(ftl::rgbd::VirtualSource *src, ftl::rgbd::Frame &out, co } cudaSafeCall(cudaStreamSynchronize(stream_)); + last_frame_ = scene_->timestamp; return true; } diff --git a/components/rgbd-sources/include/ftl/rgbd/detail/source.hpp b/components/rgbd-sources/include/ftl/rgbd/detail/source.hpp index 712d29170..cc5aeec50 100644 --- a/components/rgbd-sources/include/ftl/rgbd/detail/source.hpp +++ b/components/rgbd-sources/include/ftl/rgbd/detail/source.hpp @@ -62,8 +62,7 @@ class Source { capability_t capabilities_; ftl::rgbd::Source *host_; ftl::rgbd::Camera params_; - cv::cuda::GpuMat rgb_; - cv::cuda::GpuMat depth_; + ftl::rgbd::Frame frame_; int64_t timestamp_; //Eigen::Matrix4f pose_; }; diff --git a/components/rgbd-sources/include/ftl/rgbd/frame.hpp b/components/rgbd-sources/include/ftl/rgbd/frame.hpp index 8411c71a6..109eb008a 100644 --- a/components/rgbd-sources/include/ftl/rgbd/frame.hpp +++ b/components/rgbd-sources/include/ftl/rgbd/frame.hpp @@ -11,11 +11,13 @@ #include <ftl/codecs/channels.hpp> #include <ftl/rgbd/format.hpp> #include <ftl/codecs/bitrates.hpp> +#include <ftl/codecs/packet.hpp> #include <ftl/cuda_common.hpp> #include <type_traits> #include <array> +#include <list> namespace ftl { namespace rgbd { @@ -90,6 +92,18 @@ public: template <typename T> ftl::cuda::TextureObject<T> &createTexture(ftl::codecs::Channel c, bool interpolated=false); + /** + * Append encoded data for a channel. This will move the data, invalidating + * the original packet structure. It is to be used to allow data that is + * already encoded to be transmitted or saved again without re-encoding. + * A called to `create` will clear all encoded data for that channel. + */ + void pushPacket(ftl::codecs::Channel c, ftl::codecs::Packet &pkt); + + const std::list<ftl::codecs::Packet> &getPackets(ftl::codecs::Channel c) const; + + void mergeEncoding(ftl::rgbd::Frame &f); + void resetTexture(ftl::codecs::Channel c); /** @@ -97,6 +111,11 @@ public: */ void reset(); + /** + * Reset all channels and release memory. + */ + void resetFull(); + bool empty(ftl::codecs::Channels c); inline bool empty(ftl::codecs::Channel c) { @@ -159,6 +178,7 @@ private: ftl::cuda::TextureObjectBase tex; cv::Mat host; cv::cuda::GpuMat gpu; + std::list<ftl::codecs::Packet> encoded; }; std::array<ChannelData, ftl::codecs::Channels::kMax> data_; diff --git a/components/rgbd-sources/include/ftl/rgbd/frameset.hpp b/components/rgbd-sources/include/ftl/rgbd/frameset.hpp index f18b52635..ee1322b73 100644 --- a/components/rgbd-sources/include/ftl/rgbd/frameset.hpp +++ b/components/rgbd-sources/include/ftl/rgbd/frameset.hpp @@ -29,6 +29,8 @@ struct FrameSet { void upload(ftl::codecs::Channels, cudaStream_t stream=0); void download(ftl::codecs::Channels, cudaStream_t stream=0); void swapTo(ftl::rgbd::FrameSet &); + + void resetFull(); }; } diff --git a/components/rgbd-sources/include/ftl/rgbd/group.hpp b/components/rgbd-sources/include/ftl/rgbd/group.hpp index fdd7ba741..8df9ab1f3 100644 --- a/components/rgbd-sources/include/ftl/rgbd/group.hpp +++ b/components/rgbd-sources/include/ftl/rgbd/group.hpp @@ -12,12 +12,16 @@ #include <vector> namespace ftl { +namespace operators { + class Graph; +} + namespace rgbd { class Source; // Allows a latency of 20 frames maximum -static const size_t kFrameBufferSize = 20; +static const size_t kMaxFramesets = 15; /** * Manage a group of RGB-D sources to obtain synchronised sets of frames from @@ -55,6 +59,12 @@ class Group { */ void addGroup(ftl::rgbd::Group *); + /** + * Add a pipeline to be run after each frame is received from source but + * before it as added to a synchronised frameset. + */ + void addPipeline(ftl::operators::Graph *g) { pipeline_ = g; }; + /** * Provide a function to be called once per frame with a valid frameset * at the specified latency. The function may not be called under certain @@ -95,20 +105,25 @@ class Group { * the reference point, this may already be several frames old. Latency * does not correspond to actual current time. */ - void setLatency(int frames) { latency_ = frames; } + void setLatency(int frames) { } void stop() {} int streamID(const ftl::rgbd::Source *s) const; private: - std::vector<FrameSet> framesets_; + std::list<FrameSet*> framesets_; // Active framesets + std::list<FrameSet*> allocated_; // Keep memory allocations + std::vector<Source*> sources_; + ftl::operators::Graph *pipeline_; size_t head_; std::function<bool(FrameSet &)> callback_; MUTEX mutex_; int mspf_; - int latency_; + float latency_; + float fps_; + int stats_count_; int64_t last_ts_; std::atomic<int> jobs_; volatile bool skip_; @@ -120,13 +135,19 @@ class Group { /* Insert a new frameset into the buffer, along with all intermediate * framesets between the last in buffer and the new one. */ - void _addFrameset(int64_t timestamp); + ftl::rgbd::FrameSet *_addFrameset(int64_t timestamp); void _retrieveJob(ftl::rgbd::Source *); void _computeJob(ftl::rgbd::Source *); /* Find a frameset with given latency in frames. */ - ftl::rgbd::FrameSet *_getFrameset(int f); + ftl::rgbd::FrameSet *_getFrameset(); + + /* Search for a matching frameset. */ + ftl::rgbd::FrameSet *_findFrameset(int64_t ts); + void _freeFrameset(ftl::rgbd::FrameSet *); + + void _recordStats(float fps, float latency); }; } diff --git a/components/rgbd-sources/include/ftl/rgbd/source.hpp b/components/rgbd-sources/include/ftl/rgbd/source.hpp index 895817359..dc41f10a1 100644 --- a/components/rgbd-sources/include/ftl/rgbd/source.hpp +++ b/components/rgbd-sources/include/ftl/rgbd/source.hpp @@ -32,6 +32,7 @@ class VirtualSource; class Player; typedef std::function<void(ftl::rgbd::Source*, const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt)> RawCallback; +typedef std::function<void(int64_t,ftl::rgbd::Frame&)> FrameCallback; /** * RGBD Generic data source configurable entity. This class hides the @@ -176,14 +177,14 @@ class Source : public ftl::Configurable { SHARED_MUTEX &mutex() { return mutex_; } - std::function<void(int64_t, cv::cuda::GpuMat &, cv::cuda::GpuMat &)> &callback() { return callback_; } + const FrameCallback &callback() { return callback_; } /** * Set the callback that receives decoded frames as they are generated. * There can be only a single such callback as the buffers can be swapped * by the callback. */ - void setCallback(std::function<void(int64_t, cv::cuda::GpuMat &, cv::cuda::GpuMat &)> cb); + void setCallback(const FrameCallback &cb); void removeCallback() { callback_ = nullptr; } /** @@ -207,7 +208,8 @@ class Source : public ftl::Configurable { * Notify of a decoded or available pair of frames. This calls the source * callback after having verified the correct resolution of the frames. */ - void notify(int64_t ts, cv::cuda::GpuMat &c1, cv::cuda::GpuMat &c2); + //void notify(int64_t ts, cv::cuda::GpuMat &c1, cv::cuda::GpuMat &c2); + void notify(int64_t ts, ftl::rgbd::Frame &f); // ==== Inject Data into stream ============================================ @@ -227,8 +229,8 @@ class Source : public ftl::Configurable { SHARED_MUTEX mutex_; ftl::codecs::Channel channel_; cudaStream_t stream_; - std::function<void(int64_t, cv::cuda::GpuMat &, cv::cuda::GpuMat &)> callback_; - std::list<std::function<void(ftl::rgbd::Source*, const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt)>> rawcallbacks_; + FrameCallback callback_; + std::list<RawCallback> rawcallbacks_; detail::Source *_createImplementation(); detail::Source *_createFileImpl(const ftl::URI &uri); diff --git a/components/rgbd-sources/include/ftl/rgbd/streamer.hpp b/components/rgbd-sources/include/ftl/rgbd/streamer.hpp index 1fa1cb486..8c3277556 100644 --- a/components/rgbd-sources/include/ftl/rgbd/streamer.hpp +++ b/components/rgbd-sources/include/ftl/rgbd/streamer.hpp @@ -107,6 +107,8 @@ class Streamer : public ftl::Configurable { */ void add(ftl::rgbd::Group *grp); + ftl::rgbd::Group *group() { return &group_; } + void remove(Source *); void remove(const std::string &); diff --git a/components/rgbd-sources/include/ftl/rgbd/virtual.hpp b/components/rgbd-sources/include/ftl/rgbd/virtual.hpp index f0ab3a93b..f9bf5e7f5 100644 --- a/components/rgbd-sources/include/ftl/rgbd/virtual.hpp +++ b/components/rgbd-sources/include/ftl/rgbd/virtual.hpp @@ -11,7 +11,7 @@ class VirtualSource : public ftl::rgbd::Source { explicit VirtualSource(ftl::config::json_t &cfg); ~VirtualSource(); - void onRender(const std::function<void(ftl::rgbd::Frame &)> &); + void onRender(const std::function<bool(ftl::rgbd::Frame &)> &); /** * Write frames into source buffers from an external renderer. Virtual diff --git a/components/rgbd-sources/src/frame.cpp b/components/rgbd-sources/src/frame.cpp index 3c9fe54d8..c1b76e117 100644 --- a/components/rgbd-sources/src/frame.cpp +++ b/components/rgbd-sources/src/frame.cpp @@ -11,6 +11,19 @@ static cv::cuda::GpuMat noneGPU; void Frame::reset() { channels_.clear(); gpu_.clear(); + for (size_t i=0u; i<Channels::kMax; ++i) { + data_[i].encoded.clear(); + } +} + +void Frame::resetFull() { + channels_.clear(); + gpu_.clear(); + for (size_t i=0u; i<Channels::kMax; ++i) { + data_[i].gpu = cv::cuda::GpuMat(); + data_[i].host = cv::Mat(); + data_[i].encoded.clear(); + } } void Frame::download(Channel c, cv::cuda::Stream stream) { @@ -39,6 +52,37 @@ void Frame::upload(Channels c, cv::cuda::Stream stream) { } } +void Frame::pushPacket(ftl::codecs::Channel c, ftl::codecs::Packet &pkt) { + if (hasChannel(c)) { + auto &m1 = _get(c); + m1.encoded.emplace_back() = std::move(pkt); + } else { + LOG(ERROR) << "Channel " << (int)c << " doesn't exist for packet push"; + } +} + +const std::list<ftl::codecs::Packet> &Frame::getPackets(ftl::codecs::Channel c) const { + if (!hasChannel(c)) { + throw ftl::exception(ftl::Formatter() << "Frame channel does not exist: " << (int)c); + } + + auto &m1 = _get(c); + return m1.encoded; +} + +void Frame::mergeEncoding(ftl::rgbd::Frame &f) { + //LOG(INFO) << "MERGE " << (unsigned int)f.channels_; + for (auto c : channels_) { + //if (!f.hasChannel(c)) f.create<cv::cuda::GpuMat>(c); + if (f.hasChannel(c)) { + auto &m1 = _get(c); + auto &m2 = f._get(c); + m1.encoded.splice(m1.encoded.begin(), m2.encoded); + //LOG(INFO) << "SPLICED: " << m1.encoded.size(); + } + } +} + bool Frame::empty(ftl::codecs::Channels channels) { for (auto c : channels) { if (empty(c)) return true; @@ -70,6 +114,12 @@ void Frame::swapTo(ftl::codecs::Channels channels, Frame &f) { auto temptex = std::move(m2.tex); m2.tex = std::move(m1.tex); m1.tex = std::move(temptex); + + if (m2.encoded.size() > 0 || m1.encoded.size() > 0) { + auto tempenc = std::move(m2.encoded); + m2.encoded = std::move(m1.encoded); + m1.encoded = std::move(tempenc); + } } } } @@ -83,6 +133,12 @@ void Frame::swapChannels(ftl::codecs::Channel a, ftl::codecs::Channel b) { auto temptex = std::move(m2.tex); m2.tex = std::move(m1.tex); m1.tex = std::move(temptex); + + if (m2.encoded.size() > 0 || m1.encoded.size() > 0) { + auto tempenc = std::move(m2.encoded); + m2.encoded = std::move(m1.encoded); + m1.encoded = std::move(tempenc); + } } void Frame::copyTo(ftl::codecs::Channels channels, Frame &f) { @@ -94,6 +150,9 @@ void Frame::copyTo(ftl::codecs::Channels channels, Frame &f) { if (channels.has(c)) { if (isCPU(c)) get<cv::Mat>(c).copyTo(f.create<cv::Mat>(c)); else get<cv::cuda::GpuMat>(c).copyTo(f.create<cv::cuda::GpuMat>(c)); + auto &m1 = _get(c); + auto &m2 = f._get(c); + m2.encoded = m1.encoded; //std::move(m1.encoded); // TODO: Copy? } } } @@ -176,13 +235,15 @@ template <> cv::Mat &Frame::create(ftl::codecs::Channel c, const ftl::rgbd::Form channels_ += c; gpu_ -= c; - auto &m = _get(c).host; + auto &m = _get(c); + + m.encoded.clear(); // Remove all old encoded data if (!f.empty()) { - m.create(f.size(), f.cvType); + m.host.create(f.size(), f.cvType); } - return m; + return m.host; } template <> cv::cuda::GpuMat &Frame::create(ftl::codecs::Channel c, const ftl::rgbd::FormatBase &f) { @@ -192,13 +253,15 @@ template <> cv::cuda::GpuMat &Frame::create(ftl::codecs::Channel c, const ftl::r channels_ += c; gpu_ += c; - auto &m = _get(c).gpu; + auto &m = _get(c); + + m.encoded.clear(); // Remove all old encoded data if (!f.empty()) { - m.create(f.size(), f.cvType); + m.gpu.create(f.size(), f.cvType); } - return m; + return m.gpu; } template <> cv::Mat &Frame::create(ftl::codecs::Channel c) { @@ -208,8 +271,11 @@ template <> cv::Mat &Frame::create(ftl::codecs::Channel c) { channels_ += c; gpu_ -= c; - auto &m = _get(c).host; - return m; + auto &m = _get(c); + + m.encoded.clear(); // Remove all old encoded data + + return m.host; } template <> cv::cuda::GpuMat &Frame::create(ftl::codecs::Channel c) { @@ -219,8 +285,11 @@ template <> cv::cuda::GpuMat &Frame::create(ftl::codecs::Channel c) { channels_ += c; gpu_ += c; - auto &m = _get(c).gpu; - return m; + auto &m = _get(c); + + m.encoded.clear(); // Remove all old encoded data + + return m.gpu; } void Frame::resetTexture(ftl::codecs::Channel c) { diff --git a/components/rgbd-sources/src/frameset.cpp b/components/rgbd-sources/src/frameset.cpp index 38232e3f6..44be010a3 100644 --- a/components/rgbd-sources/src/frameset.cpp +++ b/components/rgbd-sources/src/frameset.cpp @@ -37,3 +37,11 @@ void FrameSet::swapTo(ftl::rgbd::FrameSet &fs) { stale = true; } + +void FrameSet::resetFull() { + //count = 0; + //stale = false; + for (auto &f : frames) { + f.resetFull(); + } +} diff --git a/components/rgbd-sources/src/group.cpp b/components/rgbd-sources/src/group.cpp index aad850d2c..ca64d4764 100644 --- a/components/rgbd-sources/src/group.cpp +++ b/components/rgbd-sources/src/group.cpp @@ -1,19 +1,31 @@ #include <ftl/rgbd/group.hpp> #include <ftl/rgbd/source.hpp> #include <ftl/timer.hpp> +#include <ftl/operators/operator.hpp> #include <chrono> using ftl::rgbd::Group; using ftl::rgbd::Source; -using ftl::rgbd::kFrameBufferSize; +using ftl::rgbd::kMaxFramesets; using std::vector; using std::chrono::milliseconds; using std::this_thread::sleep_for; using ftl::codecs::Channel; -Group::Group() : framesets_(kFrameBufferSize), head_(0) { - framesets_[0].timestamp = -1; +Group::Group() : pipeline_(nullptr), head_(0) { + //framesets_[0].timestamp = -1; + //framesets_[0].stale = true; + + /*for (auto &i : framesets_) { + i.stale = true; + }*/ + + // Allocate some initial framesets + //for (int i=0; i<10; ++i) { + // allocated_.push_back(new ftl::rgbd::FrameSet); + //} + jobs_ = 0; skip_ = false; //setFPS(20); @@ -21,7 +33,9 @@ Group::Group() : framesets_(kFrameBufferSize), head_(0) { mspf_ = ftl::timer::getInterval(); name_ = "NoName"; - setLatency(5); + latency_ = 0.0f;; + stats_count_ = 0; + fps_ = 0.0f; } Group::~Group() { @@ -50,54 +64,54 @@ void Group::addSource(ftl::rgbd::Source *src) { size_t ix = sources_.size(); sources_.push_back(src); - src->setCallback([this,ix,src](int64_t timestamp, cv::cuda::GpuMat &rgb, cv::cuda::GpuMat &depth) { + src->setCallback([this,ix,src](int64_t timestamp, ftl::rgbd::Frame &frame) { if (timestamp == 0) return; auto chan = src->getChannel(); - //LOG(INFO) << "SRC CB: " << timestamp << " (" << framesets_[head_].timestamp << ")"; + //LOG(INFO) << "SRC CB (" << name_ << "): " << timestamp << " (" << ")"; UNIQUE_LOCK(mutex_, lk); - if (timestamp > framesets_[head_].timestamp) { + auto *fs = _findFrameset(timestamp); + + if (!fs) { // Add new frameset - _addFrameset(timestamp); - } else if (framesets_[(head_+1)%kFrameBufferSize].timestamp > timestamp) { + fs = _addFrameset(timestamp); + + if (!fs) return; + } /*else if (framesets_[(head_+1)%kFrameBufferSize].timestamp > timestamp) { // Too old, just ditch it LOG(WARNING) << "Received frame too old for buffer"; return; - } + }*/ // Search backwards to find match - for (size_t i=0; i<kFrameBufferSize; ++i) { - FrameSet &fs = framesets_[(head_+kFrameBufferSize-i) % kFrameBufferSize]; - if (fs.timestamp == timestamp) { + //for (size_t i=0; i<kFrameBufferSize; ++i) { + // FrameSet &fs = framesets_[(head_+kFrameBufferSize-i) % kFrameBufferSize]; + //if (fs.timestamp == timestamp) { lk.unlock(); - SHARED_LOCK(fs.mtx, lk2); - - //LOG(INFO) << "Adding frame: " << ix << " for " << timestamp; - // Ensure channels match source mat format - //fs.channel1[ix].create(rgb.size(), rgb.type()); - //fs.channel2[ix].create(depth.size(), depth.type()); - fs.frames[ix].create<cv::cuda::GpuMat>(Channel::Colour, Format<uchar3>(rgb.size())); //.create(rgb.size(), rgb.type()); - if (chan != Channel::None) fs.frames[ix].create<cv::cuda::GpuMat>(chan, ftl::rgbd::FormatBase(depth.cols, depth.rows, depth.type())); //.create(depth.size(), depth.type()); - - //cv::swap(rgb, fs.channel1[ix]); - //cv::swap(depth, fs.channel2[ix]); - cv::cuda::swap(rgb, fs.frames[ix].get<cv::cuda::GpuMat>(Channel::Colour)); - if (chan != Channel::None) cv::cuda::swap(depth, fs.frames[ix].get<cv::cuda::GpuMat>(chan)); - - ++fs.count; - fs.mask |= (1 << ix); - - if (fs.count == sources_.size()) { - //LOG(INFO) << "COMPLETE SET: " << fs.timestamp; - } else if (fs.count > sources_.size()) { - LOG(ERROR) << "Too many frames for frame set: " << fs.timestamp << " sources=" << sources_.size(); + SHARED_LOCK(fs->mtx, lk2); + + frame.swapTo(ftl::codecs::kAllChannels, fs->frames[ix]); + + if (fs->count+1 == sources_.size()) { + if (pipeline_) { + pipeline_->apply(*fs, *fs, 0); + } + } + + ++fs->count; + fs->mask |= (1 << ix); + + if (fs->count == sources_.size()) { + //LOG(INFO) << "COMPLETE SET (" << name_ << "): " << fs->timestamp; + } else if (fs->count > sources_.size()) { + LOG(ERROR) << "Too many frames for frame set: " << fs->timestamp << " sources=" << sources_.size(); } else { //LOG(INFO) << "INCOMPLETE SET (" << ix << "): " << fs.timestamp; } - if (callback_ && fs.count == sources_.size()) { + /*if (callback_ && fs->count == sources_.size()) { try { if (callback_(fs)) { // TODO: Remove callback if returns false? @@ -108,12 +122,12 @@ void Group::addSource(ftl::rgbd::Source *src) { // Reset count to prevent multiple reads of these frames //fs.count = 0; - } + }*/ return; - } - } - DLOG(WARNING) << "Frame timestamp not found in buffer"; + //} + //} + //LOG(WARNING) << "Frame timestamp not found in buffer"; }); } @@ -153,9 +167,9 @@ int Group::streamID(const ftl::rgbd::Source *s) const { } void Group::sync(std::function<bool(ftl::rgbd::FrameSet &)> cb) { - if (latency_ == 0) { - callback_ = cb; - } + //if (latency_ == 0) { + // callback_ = cb; + //} // 1. Capture camera frames with high precision cap_id_ = ftl::timer::add(ftl::timer::kTimerHighPrecision, [this](int64_t ts) { @@ -163,7 +177,6 @@ void Group::sync(std::function<bool(ftl::rgbd::FrameSet &)> cb) { if (skip_) return true; - last_ts_ = ts; for (auto s : sources_) { s->capture(ts); } @@ -183,6 +196,7 @@ void Group::sync(std::function<bool(ftl::rgbd::FrameSet &)> cb) { // 3. Issue IO retrieve ad compute jobs before finding a valid // frame at required latency to pass to callback. main_id_ = ftl::timer::add(ftl::timer::kTimerMain, [this,cb](int64_t ts) { + //if (skip_) LOG(ERROR) << "SKIPPING TIMER JOB " << ts; if (skip_) return true; jobs_++; @@ -191,41 +205,55 @@ void Group::sync(std::function<bool(ftl::rgbd::FrameSet &)> cb) { ftl::pool.push([this,s](int id) { _retrieveJob(s); + //if (jobs_ == 0) LOG(INFO) << "LAST JOB = Retrieve"; --jobs_; }); ftl::pool.push([this,s](int id) { _computeJob(s); + //if (jobs_ == 0) LOG(INFO) << "LAST JOB = Compute"; --jobs_; }); } // Find a previous frameset and specified latency and do the sync // callback with that frameset. - if (latency_ > 0) { + //if (latency_ > 0) { ftl::rgbd::FrameSet *fs = nullptr; UNIQUE_LOCK(mutex_, lk); - fs = _getFrameset(latency_); + fs = _getFrameset(); + + //LOG(INFO) << "Latency for " << name_ << " = " << (latency_*ftl::timer::getInterval()) << "ms"; if (fs) { UNIQUE_LOCK(fs->mtx, lk2); lk.unlock(); - - try { - cb(*fs); - //LOG(INFO) << "Frameset processed (" << name_ << "): " << fs->timestamp; - } catch(std::exception &e) { - LOG(ERROR) << "Exception in group sync callback: " << e.what(); - } - // The buffers are invalid after callback so mark stale fs->stale = true; + + //ftl::pool.push([this,fs,cb](int) { + try { + cb(*fs); + //LOG(INFO) << "Frameset processed (" << name_ << "): " << fs->timestamp; + } catch(std::exception &e) { + LOG(ERROR) << "Exception in group sync callback: " << e.what(); + } + + //fs->resetFull(); + + lk.lock(); + _freeFrameset(fs); + + jobs_--; + //}); } else { //LOG(INFO) << "NO FRAME FOUND: " << last_ts_ - latency_*mspf_; + //latency_++; + jobs_--; } - } + //} - jobs_--; + //if (jobs_ == 0) LOG(INFO) << "LAST JOB = Main"; return true; }); } @@ -242,47 +270,130 @@ void Group::removeRawCallback(const std::function<void(ftl::rgbd::Source*, const } } -//ftl::rgbd::FrameSet &Group::_getRelativeFrameset(int rel) { -// int idx = (rel < 0) ? (head_+kFrameBufferSize+rel)%kFrameBufferSize : (head_+rel)%kFrameBufferSize; -// return framesets_[idx]; -//} - -ftl::rgbd::FrameSet *Group::_getFrameset(int f) { - const int64_t lookfor = last_ts_-f*mspf_; +static void mergeFrameset(ftl::rgbd::FrameSet &f1, ftl::rgbd::FrameSet &f2) { + // Prepend all frame encodings in f2 into corresponding frame in f1. + for (int i=0; i<f1.frames.size(); ++i) { + f1.frames[i].mergeEncoding(f2.frames[i]); + } +} - for (size_t i=1; i<kFrameBufferSize; ++i) { - int idx = (head_+kFrameBufferSize-i)%kFrameBufferSize; +void Group::_recordStats(float fps, float latency) { + latency_ += latency; + fps_ += fps; + ++stats_count_; + + if (fps_/float(stats_count_) <= float(stats_count_)) { + fps_ /= float(stats_count_); + latency_ /= float(stats_count_); + LOG(INFO) << name_ << ": fps = " << fps_ << ", latency = " << latency_; + fps_ = 0.0f; + latency_ = 0.0f; + stats_count_ = 0; + } +} - if (framesets_[idx].timestamp == lookfor && framesets_[idx].count != sources_.size()) { - LOG(WARNING) << "Required frame not complete in '" << name_ << "' (timestamp=" << (framesets_[idx].timestamp) << " buffer=" << i << ")"; - //framesets_[idx].stale = true; - //return &framesets_[idx]; - continue; +ftl::rgbd::FrameSet *Group::_findFrameset(int64_t ts) { + // Search backwards to find match + for (auto f : framesets_) { + if (f->timestamp == ts) { + return f; + } else if (f->timestamp < ts) { + return nullptr; } + } + + return nullptr; +} - if (framesets_[idx].stale) return nullptr; +/* + * Get the most recent completed frameset that isn't stale. + * Note: Must occur inside a mutex lock. + */ +ftl::rgbd::FrameSet *Group::_getFrameset() { + for (auto i=framesets_.begin(); i!=framesets_.end(); i++) { + auto *f = *i; + if (!f->stale && f->count == sources_.size()) { + //LOG(INFO) << "GET FRAMESET and remove: " << f->timestamp; + auto j = framesets_.erase(i); + + int count = 0; + // Merge all previous frames + for (; j!=framesets_.end(); j++) { + ++count; + + auto *f2 = *j; + j = framesets_.erase(j); + mergeFrameset(*f,*f2); + _freeFrameset(f2); + } - if (framesets_[idx].timestamp == lookfor && framesets_[idx].count == sources_.size()) { - //framesets_[idx].stale = false; - return &framesets_[idx]; - } else if (framesets_[idx].timestamp < lookfor && framesets_[idx].count == sources_.size()) { - //framesets_[idx].stale = true; - return &framesets_[idx]; - } + //if (count > 0) LOG(INFO) << "COUNT = " << count; + int64_t now = ftl::timer::get_time(); + float framerate = 1000.0f / float(now - last_ts_); + _recordStats(framerate, now - f->timestamp); + last_ts_ = now; + return f; + } } + return nullptr; } -void Group::_addFrameset(int64_t timestamp) { - int count = (framesets_[head_].timestamp == -1) ? 200 : (timestamp - framesets_[head_].timestamp) / mspf_; +void Group::_freeFrameset(ftl::rgbd::FrameSet *fs) { + allocated_.push_back(fs); +} + +ftl::rgbd::FrameSet *Group::_addFrameset(int64_t timestamp) { + if (allocated_.size() == 0) { + if (framesets_.size() < kMaxFramesets) { + allocated_.push_back(new ftl::rgbd::FrameSet); + } else { + LOG(ERROR) << "Could not allocate frameset."; + return nullptr; + } + } + FrameSet *newf = allocated_.front(); + allocated_.pop_front(); + + newf->timestamp = timestamp; + newf->count = 0; + newf->mask = 0; + newf->stale = false; + newf->frames.resize(sources_.size()); + + for (auto &f : newf->frames) f.reset(); + + if (newf->sources.size() != sources_.size()) { + newf->sources.clear(); + for (auto s : sources_) newf->sources.push_back(s); + } + + // Insertion sort by timestamp + for (auto i=framesets_.begin(); i!=framesets_.end(); i++) { + auto *f = *i; + + if (timestamp > f->timestamp) { + framesets_.insert(i, newf); + return newf; + } + } + + framesets_.push_back(newf); + return newf; + + + //int count = (framesets_[head_].timestamp == -1) ? 1 : (timestamp - framesets_[head_].timestamp) / mspf_; //LOG(INFO) << "Massive timestamp difference: " << count; // Allow for massive timestamp changes (Windows clock adjust) // Only add a single frameset for large changes - if (count < -int(kFrameBufferSize) || count >= kFrameBufferSize-1) { + //if (count < -int(kFrameBufferSize) || count >= kFrameBufferSize-1) { + /*if (framesets_[head_].timestamp < timestamp) { head_ = (head_+1) % kFrameBufferSize; + //if (framesets_[head_].stale == false) LOG(FATAL) << "Buffer exceeded"; + #ifdef DEBUG_MUTEX std::unique_lock<std::shared_timed_mutex> lk(framesets_[head_].mtx, std::defer_lock); #else @@ -296,8 +407,6 @@ void Group::_addFrameset(int64_t timestamp) { framesets_[head_].count = 0; framesets_[head_].mask = 0; framesets_[head_].stale = false; - //framesets_[head_].channel1.resize(sources_.size()); - //framesets_[head_].channel2.resize(sources_.size()); framesets_[head_].frames.resize(sources_.size()); for (auto &f : framesets_[head_].frames) f.reset(); @@ -306,40 +415,31 @@ void Group::_addFrameset(int64_t timestamp) { framesets_[head_].sources.clear(); for (auto s : sources_) framesets_[head_].sources.push_back(s); } - return; - } - - if (count < 1) return; - - // Must make sure to also insert missing framesets - for (int i=0; i<count; ++i) { - int64_t lt = (framesets_[head_].timestamp == -1) ? timestamp-mspf_ : framesets_[head_].timestamp; - head_ = (head_+1) % kFrameBufferSize; - #ifdef DEBUG_MUTEX - std::unique_lock<std::shared_timed_mutex> lk(framesets_[head_].mtx, std::defer_lock); - #else - std::unique_lock<std::shared_mutex> lk(framesets_[head_].mtx, std::defer_lock); - #endif - if (!lk.try_lock()) { - LOG(ERROR) << "Frameset in use!! (" << name_ << ") " << framesets_[head_].timestamp << " stale=" << framesets_[head_].stale; - continue; - } - framesets_[head_].timestamp = lt+mspf_; - framesets_[head_].count = 0; - framesets_[head_].mask = 0; - framesets_[head_].stale = false; - //framesets_[head_].channel1.resize(sources_.size()); - //framesets_[head_].channel2.resize(sources_.size()); - framesets_[head_].frames.resize(sources_.size()); + // Find number of valid frames that will be skipped + int count = 0; + //for (int j=1; j<kFrameBufferSize; ++j) { + int idx2 = (head_+kFrameBufferSize-1)%kFrameBufferSize; + //if (framesets_[idx2].stale || framesets_[idx2].timestamp <= 0) break; + ++count; + + // Make sure encoded packets are moved from skipped frames + //if (framesets_[idx2].count >= framesets_[idx2].sources.size()) { + if (framesets_[idx2].stale == false) { + mergeFrameset(framesets_[head_], framesets_[idx2]); + framesets_[idx2].stale = true; + framesets_[idx2].timestamp = -1; + } + //} - for (auto &f : framesets_[head_].frames) f.reset(); + float framerate = 1000.0f / float((count+1)*ftl::timer::getInterval()); + _recordStats(framerate, ftl::timer::get_time() - timestamp); - if (framesets_[head_].sources.size() != sources_.size()) { - framesets_[head_].sources.clear(); - for (auto s : sources_) framesets_[head_].sources.push_back(s); - } - } + return; + } else { + LOG(ERROR) << "Old frame received: " << (framesets_[head_].timestamp - timestamp); + return; + }*/ } void Group::setName(const std::string &name) { diff --git a/components/rgbd-sources/src/source.cpp b/components/rgbd-sources/src/source.cpp index f4f217af1..384978e7f 100644 --- a/components/rgbd-sources/src/source.cpp +++ b/components/rgbd-sources/src/source.cpp @@ -32,6 +32,8 @@ using ftl::rgbd::capability_t; using ftl::codecs::Channel; using ftl::rgbd::detail::FileSource; using ftl::rgbd::Camera; +using ftl::rgbd::RawCallback; +using ftl::rgbd::FrameCallback; std::map<std::string, ftl::rgbd::Player*> Source::readers__; @@ -247,12 +249,12 @@ const ftl::rgbd::Camera Source::parameters(ftl::codecs::Channel chan) const { return (impl_) ? impl_->parameters(chan) : parameters(); } -void Source::setCallback(std::function<void(int64_t, cv::cuda::GpuMat &, cv::cuda::GpuMat &)> cb) { +void Source::setCallback(const FrameCallback &cb) { if (bool(callback_)) LOG(ERROR) << "Source already has a callback: " << getURI(); callback_ = cb; } -void Source::addRawCallback(const std::function<void(ftl::rgbd::Source*, const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt)> &f) { +void Source::addRawCallback(const RawCallback &f) { UNIQUE_LOCK(mutex_,lk); rawcallbacks_.push_back(f); } @@ -298,12 +300,8 @@ Camera Camera::scaled(int width, int height) const { return newcam; } -void Source::notify(int64_t ts, cv::cuda::GpuMat &c1, cv::cuda::GpuMat &c2) { - // Ensure correct scaling of images and parameters. - int max_width = max(impl_->params_.width, max(c1.cols, c2.cols)); - int max_height = max(impl_->params_.height, max(c1.rows, c2.rows)); - - if (callback_) callback_(ts, c1, c2); +void Source::notify(int64_t ts, ftl::rgbd::Frame &f) { + if (callback_) callback_(ts, f); } void Source::inject(const Eigen::Matrix4d &pose) { diff --git a/components/rgbd-sources/src/sources/ftlfile/file_source.cpp b/components/rgbd-sources/src/sources/ftlfile/file_source.cpp index 554a558b3..01b602c20 100644 --- a/components/rgbd-sources/src/sources/ftlfile/file_source.cpp +++ b/components/rgbd-sources/src/sources/ftlfile/file_source.cpp @@ -6,6 +6,7 @@ using ftl::rgbd::detail::FileSource; using ftl::codecs::codec_t; using ftl::codecs::Channel; +using cv::cuda::GpuMat; void FileSource::_createDecoder(int ix, const ftl::codecs::Packet &pkt) { if (decoders_[ix]) { @@ -23,11 +24,12 @@ void FileSource::_createDecoder(int ix, const ftl::codecs::Packet &pkt) { FileSource::FileSource(ftl::rgbd::Source *s, ftl::rgbd::Player *r, int sid) : ftl::rgbd::detail::Source(s) { reader_ = r; has_calibration_ = false; - decoders_[0] = nullptr; - decoders_[1] = nullptr; + + for (int i=0; i<32; ++i) decoders_[i] = nullptr; + cache_read_ = -1; cache_write_ = 0; - realtime_ = host_->value("realtime", true); + realtime_ = host_->value("realtime", false); timestamp_ = r->getStartTime(); sourceid_ = sid; freeze_ = host_->value("freeze", false); @@ -39,7 +41,7 @@ FileSource::FileSource(ftl::rgbd::Source *s, ftl::rgbd::Player *r, int sid) : ft }); r->onPacket(sid, [this](const ftl::codecs::StreamPacket &spkt, ftl::codecs::Packet &pkt) { - host_->notifyRaw(spkt, pkt); + //host_->notifyRaw(spkt, pkt); // Some channels are to be directly handled by the source object and // do not proceed to any subsequent step. @@ -60,6 +62,8 @@ FileSource::FileSource(ftl::rgbd::Source *s, ftl::rgbd::Player *r, int sid) : ft return; } + available_channels_ |= spkt.channel; + // FIXME: For bad and old FTL files where wrong channel is used if (pkt.codec == codec_t::POSE) { _processPose(pkt); @@ -77,9 +81,8 @@ FileSource::FileSource(ftl::rgbd::Source *s, ftl::rgbd::Player *r, int sid) : ft cache_[cache_write_].emplace_back(); auto &c = cache_[cache_write_].back(); - // TODO: Attempt to avoid this copy operation c.spkt = spkt; - c.pkt = pkt; + c.pkt = std::move(pkt); }); } @@ -161,9 +164,10 @@ bool FileSource::compute(int n, int b) { // Freeze frame requires a copy to be made each time... if (have_frozen_) { cv::cuda::GpuMat t1, t2; - if (!rgb_.empty()) rgb_.copyTo(t1); - if (!depth_.empty()) depth_.copyTo(t2); - host_->notify(timestamp_, t1, t2); + // FIXME: Implement this? + //if (!rgb_.empty()) rgb_.copyTo(t1); + //if (!depth_.empty()) depth_.copyTo(t2); + //host_->notify(timestamp_, t1, t2); return true; } @@ -173,6 +177,17 @@ bool FileSource::compute(int n, int b) { int64_t lastts = 0; int lastc = 0; + frame_.reset(); + + // Decide which channels to decode + decode_channels_ = Channel::Colour; + + if (available_channels_.has(host_->getChannel())) { + decode_channels_ |= host_->getChannel(); + } else if (host_->getChannel() == Channel::Depth && available_channels_.has(Channel::Right)) { + decode_channels_ |= Channel::Right; + } + // Go through previously read and cached frames in sequence // needs to be done due to P-Frames for (auto i=cache_[cache_read_].begin(); i!=cache_[cache_read_].end(); ++i) { @@ -187,32 +202,38 @@ bool FileSource::compute(int n, int b) { lastc++; } - if (c.spkt.channel == Channel::Colour) { - rgb_.create(cv::Size(ftl::codecs::getWidth(c.pkt.definition),ftl::codecs::getHeight(c.pkt.definition)), CV_8UC3); - _createDecoder(0, c.pkt); + // Has this channel been requested? + if (decode_channels_.has(c.spkt.channel)) { //c.spkt.channel == Channel::Colour || c.spkt.channel == host_->getChannel()) { - try { - decoders_[0]->decode(c.pkt, rgb_); - } catch (std::exception &e) { - LOG(INFO) << "Decoder exception: " << e.what(); + // Create channel only if not existing + if (!frame_.hasChannel(c.spkt.channel)) { + frame_.create<GpuMat>(c.spkt.channel); } - } else if (host_->getChannel() == c.spkt.channel) { - depth_.create(cv::Size(ftl::codecs::getWidth(c.pkt.definition),ftl::codecs::getHeight(c.pkt.definition)), CV_32F); - _createDecoder(1, c.pkt); - try { - decoders_[1]->decode(c.pkt, depth_); - } catch (std::exception &e) { - LOG(INFO) << "Decoder exception: " << e.what(); + + int channum = int(c.spkt.channel); + if (!ftl::codecs::isFloatChannel(c.spkt.channel)) { + frame_.get<GpuMat>(c.spkt.channel).create(cv::Size(ftl::codecs::getWidth(c.pkt.definition),ftl::codecs::getHeight(c.pkt.definition)), CV_8UC4); + _createDecoder(channum, c.pkt); + + try { + decoders_[channum]->decode(c.pkt, frame_.get<GpuMat>(c.spkt.channel)); + } catch (std::exception &e) { + LOG(INFO) << "Decoder exception: " << e.what(); + } + } else { + frame_.get<GpuMat>(c.spkt.channel).create(cv::Size(ftl::codecs::getWidth(c.pkt.definition),ftl::codecs::getHeight(c.pkt.definition)), CV_32F); + _createDecoder(channum, c.pkt); + try { + decoders_[channum]->decode(c.pkt, frame_.get<GpuMat>(c.spkt.channel)); + } catch (std::exception &e) { + LOG(INFO) << "Decoder exception: " << e.what(); + } } + + frame_.pushPacket(c.spkt.channel, c.pkt); } - - //_createDecoder((c.spkt.channel == Channel::Colour) ? 0 : 1, c.pkt); - - /*try { - decoders_[(c.spkt.channel == Channel::Colour) ? 0 : 1]->decode(c.pkt, (c.spkt.channel == Channel::Colour) ? rgb_ : depth_); - } catch (std::exception &e) { - LOG(INFO) << "Decoder exception: " << e.what(); - }*/ + + //host_->notifyRaw(c.spkt, c.pkt); } // FIXME: Consider case of Channel::None @@ -221,15 +242,17 @@ bool FileSource::compute(int n, int b) { return false; } + //LOG(INFO) << "MERGE " << cache_[cache_read_].size(); + cache_[cache_read_].clear(); - if (rgb_.empty() || depth_.empty()) return false; + //if (rgb_.empty() || depth_.empty()) return false; // Inform about a decoded frame pair if (freeze_ && !have_frozen_) { have_frozen_ = true; } else { - host_->notify(timestamp_, rgb_, depth_); + host_->notify(timestamp_, frame_); } return true; } diff --git a/components/rgbd-sources/src/sources/ftlfile/file_source.hpp b/components/rgbd-sources/src/sources/ftlfile/file_source.hpp index f8a26da37..839121f3a 100644 --- a/components/rgbd-sources/src/sources/ftlfile/file_source.hpp +++ b/components/rgbd-sources/src/sources/ftlfile/file_source.hpp @@ -40,12 +40,15 @@ class FileSource : public detail::Source { int cache_write_; int sourceid_; - ftl::codecs::Decoder *decoders_[2]; + ftl::codecs::Decoder *decoders_[32]; // 32 is max video channels bool realtime_; bool freeze_; bool have_frozen_; + ftl::codecs::Channels decode_channels_; + ftl::codecs::Channels available_channels_; + void _processCalibration(ftl::codecs::Packet &pkt); void _processPose(ftl::codecs::Packet &pkt); void _removeChannel(ftl::codecs::Channel channel); diff --git a/components/rgbd-sources/src/sources/ftlfile/player.cpp b/components/rgbd-sources/src/sources/ftlfile/player.cpp index cabbce642..9558c316c 100644 --- a/components/rgbd-sources/src/sources/ftlfile/player.cpp +++ b/components/rgbd-sources/src/sources/ftlfile/player.cpp @@ -34,7 +34,12 @@ Player::Player(std::istream &s) : stream_(&s), reader_(s) { c->on("reverse", [this,c](const ftl::config::Event &e) { reversed_ = c->value("reverse", false); }); - } + } else { + looping_ = true; + paused_ = false; + speed_ = 1.0f; + reversed_ = false; + } } Player::~Player() { diff --git a/components/rgbd-sources/src/sources/net/net.cpp b/components/rgbd-sources/src/sources/net/net.cpp index c29ad5491..452647008 100644 --- a/components/rgbd-sources/src/sources/net/net.cpp +++ b/components/rgbd-sources/src/sources/net/net.cpp @@ -22,6 +22,7 @@ using std::this_thread::sleep_for; using std::chrono::milliseconds; using std::tuple; using ftl::codecs::Channel; +using cv::cuda::GpuMat; using ftl::codecs::codec_t; // ===== NetFrameQueue ========================================================= @@ -92,6 +93,11 @@ void NetFrameQueue::freeFrame(NetFrame &f) { // ===== NetSource ============================================================= +int64_t NetSource::last_msg_ = 0; +float NetSource::req_bitrate_ = 0.0f; +float NetSource::sample_count_ = 0.0f; +MUTEX NetSource::msg_mtx_; + NetSource::NetSource(ftl::rgbd::Source *host) : ftl::rgbd::detail::Source(host), active_(false), minB_(9), maxN_(1), adaptive_(0), queue_(3) { @@ -266,10 +272,21 @@ void NetSource::_processPose(const ftl::codecs::Packet &pkt) { } } -void NetSource::_checkDataRate(size_t tx_size, int64_t tx_latency) { +void NetSource::_checkDataRate(size_t tx_size, int64_t tx_latency, int64_t ts) { float actual_mbps = (float(tx_size) * 8.0f * (1000.0f / float(tx_latency))) / 1048576.0f; float min_mbps = (float(tx_size) * 8.0f * (1000.0f / float(ftl::timer::getInterval()))) / 1048576.0f; - if (actual_mbps < min_mbps) LOG(WARNING) << "Bitrate = " << actual_mbps << "Mbps, min required = " << min_mbps << "Mbps"; + if (actual_mbps > 0.0f && actual_mbps < min_mbps) LOG(WARNING) << "Bitrate = " << actual_mbps << "Mbps, min required = " << min_mbps << "Mbps"; + + UNIQUE_LOCK(msg_mtx_,lk); + req_bitrate_ += float(tx_size) * 8.0f; + sample_count_ += 1.0f; + + if (ts - last_msg_ >= 1000) { + LOG(INFO) << "Required Bitrate = " << (req_bitrate_ / float(ts - last_msg_) * 1000.0f / 1048576.0f) << "Mbps"; + last_msg_ = ts; + req_bitrate_ = 0.0f; + sample_count_ = 0.0f; + } } void NetSource::_recvPacket(short ttimeoff, const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt) { @@ -299,7 +316,7 @@ void NetSource::_recvPacket(short ttimeoff, const ftl::codecs::StreamPacket &spk //LOG(INFO) << "PACKET: " << spkt.timestamp << ", " << (int)spkt.channel << ", " << (int)pkt.codec; const cv::Size size = cv::Size(ftl::codecs::getWidth(pkt.definition), ftl::codecs::getHeight(pkt.definition)); - NetFrame &frame = queue_.getFrame(spkt.timestamp, size, CV_8UC3, (isFloatChannel(chan) ? CV_32FC1 : CV_8UC3)); + NetFrame &frame = queue_.getFrame(spkt.timestamp, size, CV_8UC4, (isFloatChannel(chan) ? CV_32FC1 : CV_8UC4)); if (timestamp_ > 0 && frame.timestamp <= timestamp_) { LOG(ERROR) << "Duplicate frame - " << frame.timestamp << " received=" << int(rchan) << " uri=" << uri_; @@ -328,7 +345,7 @@ void NetSource::_recvPacket(short ttimeoff, const ftl::codecs::StreamPacket &spk // Update frame statistics frame.tx_size += pkt.data.size(); - frame.channel[channum].create(size, (isFloatChannel(rchan) ? CV_32FC1 : CV_8UC3)); + frame.channel[channum].create(size, (isFloatChannel(rchan) ? CV_32FC1 : CV_8UC4)); // Only decode if this channel is wanted. if (rchan == Channel::Colour || rchan == chan) { @@ -354,7 +371,7 @@ void NetSource::_recvPacket(short ttimeoff, const ftl::codecs::StreamPacket &spk // TODO:(Nick) Decode directly into double buffer if no scaling - _checkDataRate(pkt.data.size(), now-(spkt.timestamp+ttimeoff)); + _checkDataRate(pkt.data.size(), now-(spkt.timestamp+ttimeoff), spkt.timestamp); if (frame.chunk_count[channum] == frame.chunk_total[channum]) ++frame.channel_count; @@ -375,7 +392,13 @@ void NetSource::_completeFrame(NetFrame &frame, int64_t latency) { // Note: Not used currently adaptive_ = abr_.selectBitrate(frame); - host_->notify(frame.timestamp, frame.channel[0], frame.channel[1]); + frame_.reset(); + cv::cuda::swap(frame_.create<GpuMat>(Channel::Colour), frame.channel[0]); + + if (host_->getChannel() != Channel::None) + cv::cuda::swap(frame_.create<GpuMat>(host_->getChannel()), frame.channel[1]); + + host_->notify(frame.timestamp, frame_); queue_.freeFrame(frame); N_--; @@ -391,7 +414,7 @@ void NetSource::setPose(const Eigen::Matrix4d &pose) { active_ = false; } } catch (...) { - + LOG(ERROR) << "Exception when setting pose"; } //Source::setPose(pose); } @@ -450,11 +473,11 @@ bool NetSource::compute(int n, int b) { N_ = maxN_; // Verify depth destination is of required type - if (isFloatChannel(chan) && depth_.type() != CV_32F) { + /*if (isFloatChannel(chan)) { depth_.create(cv::Size(params_.width, params_.height), CV_32FC1); // 0.0f - } else if (!isFloatChannel(chan) && depth_.type() != CV_8UC3) { - depth_.create(cv::Size(params_.width, params_.height), CV_8UC3); // cv::Scalar(0,0,0) - } + } else if (!isFloatChannel(chan)) { + depth_.create(cv::Size(params_.width, params_.height), CV_8UC3); // FIXME: Use 8UC4 + }*/ if (prev_chan_ != chan) { host_->getNet()->send(peer_, "set_channel", *host_->get<string>("uri"), chan); diff --git a/components/rgbd-sources/src/sources/net/net.hpp b/components/rgbd-sources/src/sources/net/net.hpp index 515bb8a5f..299325643 100644 --- a/components/rgbd-sources/src/sources/net/net.hpp +++ b/components/rgbd-sources/src/sources/net/net.hpp @@ -63,6 +63,11 @@ class NetSource : public detail::Source { ftl::rgbd::detail::ABRController abr_; int last_bitrate_; + static int64_t last_msg_; + static float req_bitrate_; + static float sample_count_; + static MUTEX msg_mtx_; + //#ifdef HAVE_NVPIPE //NvPipe *nv_channel1_decoder_; //NvPipe *nv_channel2_decoder_; @@ -89,7 +94,7 @@ class NetSource : public detail::Source { void _processCalibration(const ftl::codecs::Packet &pkt); void _processConfig(const ftl::codecs::Packet &pkt); void _processPose(const ftl::codecs::Packet &pkt); - void _checkDataRate(size_t tx_size, int64_t tx_latency); + void _checkDataRate(size_t tx_size, int64_t tx_latency, int64_t ts); }; } diff --git a/components/rgbd-sources/src/sources/realsense/realsense_source.cpp b/components/rgbd-sources/src/sources/realsense/realsense_source.cpp index 2b55356c8..68c1a9acf 100644 --- a/components/rgbd-sources/src/sources/realsense/realsense_source.cpp +++ b/components/rgbd-sources/src/sources/realsense/realsense_source.cpp @@ -5,6 +5,8 @@ using ftl::rgbd::detail::RealsenseSource; using std::string; +using ftl::codecs::Channel; +using cv::cuda::GpuMat; RealsenseSource::RealsenseSource(ftl::rgbd::Source *host) : ftl::rgbd::detail::Source(host), align_to_depth_(RS2_STREAM_DEPTH) { @@ -56,12 +58,11 @@ bool RealsenseSource::compute(int n, int b) { cv::Mat tmp_depth(cv::Size((int)w, (int)h), CV_16UC1, (void*)depth.get_data(), depth.get_stride_in_bytes()); tmp_depth.convertTo(tmp_depth, CV_32FC1, scale_); - depth_.upload(tmp_depth); + frame_.get<GpuMat>(Channel::Depth).upload(tmp_depth); cv::Mat tmp_rgb(cv::Size(w, h), CV_8UC4, (void*)rscolour_.get_data(), cv::Mat::AUTO_STEP); - rgb_.upload(tmp_rgb); + frame_.get<GpuMat>(Channel::Colour).upload(tmp_rgb); - auto cb = host_->callback(); - if (cb) cb(timestamp_, rgb_, depth_); + host_->notify(timestamp_, frame_); return true; } diff --git a/components/rgbd-sources/src/sources/snapshot/snapshot_source.cpp b/components/rgbd-sources/src/sources/snapshot/snapshot_source.cpp index 61acdb9d8..95d8b9e07 100644 --- a/components/rgbd-sources/src/sources/snapshot/snapshot_source.cpp +++ b/components/rgbd-sources/src/sources/snapshot/snapshot_source.cpp @@ -9,6 +9,8 @@ using namespace ftl::rgbd; using ftl::rgbd::detail::SnapshotSource; +using cv::cuda::GpuMat; +using ftl::codecs::Channel; using std::string; using std::vector; @@ -64,13 +66,13 @@ bool SnapshotSource::compute(int n, int b) { //snap_rgb_.copyTo(rgb_); //snap_depth_.copyTo(depth_); cv::cuda::Stream cvstream = cv::cuda::StreamAccessor::wrapStream(stream_); - rgb_.upload(snap_rgb_, cvstream); - depth_.upload(snap_depth_, cvstream); + frame_.create<GpuMat>(Channel::Colour).upload(snap_rgb_, cvstream); + frame_.create<GpuMat>(Channel::Depth).upload(snap_depth_, cvstream); cudaStreamSynchronize(stream_); //auto cb = host_->callback(); //if (cb) cb(timestamp_, rgb_, depth_); - host_->notify(timestamp_, rgb_, depth_); + host_->notify(timestamp_, frame_); frame_idx_ = (frame_idx_ + 1) % snapshot_.getFramesCount(); diff --git a/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp b/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp index e8eff732b..78bc1dba3 100644 --- a/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp +++ b/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp @@ -209,7 +209,17 @@ bool StereoVideoSource::compute(int n, int b) { return false; } - if (chan == Channel::Depth) { + cv::cuda::GpuMat& left = frame.get<cv::cuda::GpuMat>(Channel::Left); + cv::cuda::GpuMat& right = frame.get<cv::cuda::GpuMat>(Channel::Right); + + if (left.empty() || right.empty()) { + return false; + } + + //stream_.waitForCompletion(); + host_->notify(timestamp_, frame); + + /*if (chan == Channel::Depth) { // stereo algorithms assume input same size as output bool resize = (depth_size_ != color_size_); @@ -235,24 +245,16 @@ bool StereoVideoSource::compute(int n, int b) { cv::cuda::swap(fullres_right_, right); } - host_->notify(timestamp_, - frame.get<cv::cuda::GpuMat>(Channel::Left), - frame.get<cv::cuda::GpuMat>(Channel::Depth)); + host_->notify(timestamp_, frame); } else if (chan == Channel::Right) { - stream_.waitForCompletion(); // TODO:(Nick) Move to getFrames - host_->notify(timestamp_, - frame.get<cv::cuda::GpuMat>(Channel::Left), - frame.get<cv::cuda::GpuMat>(Channel::Right)); + stream_.waitForCompletion(); + host_->notify(timestamp_, frame); } else { - stream_.waitForCompletion(); // TODO:(Nick) Move to getFrames - auto &left = frame.get<cv::cuda::GpuMat>(Channel::Left); - depth_.create(left.size(), left.type()); - host_->notify(timestamp_, - frame.get<cv::cuda::GpuMat>(Channel::Left), - depth_); - } + stream_.waitForCompletion(); + host_->notify(timestamp_, frame); + }*/ return true; } diff --git a/components/rgbd-sources/src/sources/virtual/virtual.cpp b/components/rgbd-sources/src/sources/virtual/virtual.cpp index a1d1040c6..0827c0ffd 100644 --- a/components/rgbd-sources/src/sources/virtual/virtual.cpp +++ b/components/rgbd-sources/src/sources/virtual/virtual.cpp @@ -80,17 +80,18 @@ class VirtualImpl : public ftl::rgbd::detail::Source { bool compute(int n, int b) override { if (callback) { - frame.reset(); + frame_.reset(); + bool goodFrame = false; try { - callback(frame); + goodFrame = callback(frame_); } catch (std::exception &e) { LOG(ERROR) << "Exception in render callback: " << e.what(); } catch (...) { LOG(ERROR) << "Unknown exception in render callback"; } - if (frame.hasChannel(Channel::Colour)) { + /*if (frame.hasChannel(Channel::Colour)) { //frame.download(Channel::Colour); cv::cuda::swap(frame.get<cv::cuda::GpuMat>(Channel::Colour), rgb_); } else { @@ -101,9 +102,9 @@ class VirtualImpl : public ftl::rgbd::detail::Source { frame.hasChannel(host_->getChannel())) { //frame.download(host_->getChannel()); cv::cuda::swap(frame.get<cv::cuda::GpuMat>(host_->getChannel()), depth_); - } + }*/ - host_->notify(timestamp_, rgb_, depth_); + if (goodFrame) host_->notify(timestamp_, frame_); } return true; } @@ -114,8 +115,8 @@ class VirtualImpl : public ftl::rgbd::detail::Source { bool isReady() override { return true; } - std::function<void(ftl::rgbd::Frame &)> callback; - ftl::rgbd::Frame frame; + std::function<bool(ftl::rgbd::Frame &)> callback; + //ftl::rgbd::Frame frame; ftl::rgbd::Camera params_right_; }; @@ -128,7 +129,7 @@ VirtualSource::~VirtualSource() { } -void VirtualSource::onRender(const std::function<void(ftl::rgbd::Frame &)> &f) { +void VirtualSource::onRender(const std::function<bool(ftl::rgbd::Frame &)> &f) { dynamic_cast<VirtualImpl*>(impl_)->callback = f; } diff --git a/components/rgbd-sources/src/streamer.cpp b/components/rgbd-sources/src/streamer.cpp index a672c1bd1..6492d2ccb 100644 --- a/components/rgbd-sources/src/streamer.cpp +++ b/components/rgbd-sources/src/streamer.cpp @@ -49,7 +49,7 @@ Streamer::Streamer(nlohmann::json &config, Universe *net) //group_.setFPS(value("fps", 20)); group_.setLatency(4); - group_.setName("StreamGroup"); + group_.setName("NetStreamer"); compress_level_ = value("compression", 1); @@ -196,7 +196,7 @@ void Streamer::add(Source *src) { s->hq_bitrate = ftl::codecs::kPresetBest; } - //LOG(INFO) << "RAW CALLBACK"; + LOG(INFO) << "RAW CALLBACK"; _transmitPacket(s, spkt, pkt, Quality::Any); }); } @@ -227,7 +227,7 @@ void Streamer::add(ftl::rgbd::Group *grp) { //group_.addSource(src); src->addRawCallback([this,s](Source *src, const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt) { - //LOG(INFO) << "RAW CALLBACK"; + LOG(INFO) << "RAW CALLBACK2"; _transmitPacket(s, spkt, pkt, Quality::Any); }); } @@ -266,6 +266,7 @@ void Streamer::_addClient(const string &source, int N, int rate, const ftl::UUID try { mastertime = net_->call<int64_t>(peer, "__ping__"); } catch (...) { + LOG(ERROR) << "Ping failed"; // Reset time peer and remove timer time_peer_ = ftl::UUID(0); return false; @@ -446,62 +447,107 @@ void Streamer::_process(ftl::rgbd::FrameSet &fs) { if (!fs.sources[j]->isReady()) continue; if (src->clientCount == 0) continue; //if (fs.channel1[j].empty() || (fs.sources[j]->getChannel() != ftl::rgbd::kChanNone && fs.channel2[j].empty())) continue; - if (!fs.frames[j].hasChannel(Channel::Colour) || !fs.frames[j].hasChannel(fs.sources[j]->getChannel())) continue; + if (!fs.frames[j].hasChannel(Channel::Colour) || !fs.frames[j].hasChannel(fs.sources[j]->getChannel())) { + LOG(WARNING) << "Missing required channel when streaming: " << (int)fs.sources[j]->getChannel(); + continue; + } - bool hasChan2 = fs.sources[j]->getChannel() != Channel::None; + bool hasChan2 = fs.sources[j]->getChannel() != Channel::None && + fs.frames[j].hasChannel(fs.sources[j]->getChannel()); totalclients += src->clientCount; // Do we need to do high quality encoding? if (src->hq_count > 0) { - if (!src->hq_encoder_c1) src->hq_encoder_c1 = ftl::codecs::allocateEncoder( - definition_t::HD1080, hq_devices_, hq_codec_); - if (!src->hq_encoder_c2 && hasChan2) src->hq_encoder_c2 = ftl::codecs::allocateEncoder( - definition_t::HD1080, hq_devices_, hq_codec_); + + auto chan = fs.sources[j]->getChannel(); // Do we have the resources to do a HQ encoding? - if (src->hq_encoder_c1 && (!hasChan2 || src->hq_encoder_c2)) { - auto *enc1 = src->hq_encoder_c1; - auto *enc2 = src->hq_encoder_c2; + ///if (src->hq_encoder_c1 && (!hasChan2 || src->hq_encoder_c2)) { + //auto *enc1 = src->hq_encoder_c1; + //auto *enc2 = src->hq_encoder_c2; MUTEX mtx; std::condition_variable cv; bool chan2done = false; if (hasChan2) { - ftl::pool.push([this,&fs,enc2,src,hasChan2,&cv,j,&chan2done](int id) { - // TODO: Stagger the reset between nodes... random phasing - if (fs.timestamp % (10*ftl::timer::getInterval()) == 0) enc2->reset(); - - auto chan = fs.sources[j]->getChannel(); - - try { - enc2->encode(fs.frames[j].get<cv::cuda::GpuMat>(chan), src->hq_bitrate, [this,src,hasChan2,chan,&cv,&chan2done](const ftl::codecs::Packet &blk){ - _transmitPacket(src, blk, chan, hasChan2, Quality::High); - chan2done = true; - cv.notify_one(); + if (fs.frames[j].getPackets(chan).size() == 0) { + + // Allocate an encoder + if (!src->hq_encoder_c2) src->hq_encoder_c2 = ftl::codecs::allocateEncoder( + definition_t::HD1080, hq_devices_, hq_codec_); + + auto *enc = src->hq_encoder_c2; + + // Do we have an encoder to continue with? + if (enc) { + ftl::pool.push([this,&fs,enc,src,hasChan2,&cv,j,&chan2done](int id) { + // TODO: Stagger the reset between nodes... random phasing + if (fs.timestamp % (10*ftl::timer::getInterval()) == 0) enc->reset(); + + auto chan = fs.sources[j]->getChannel(); + + try { + enc->encode(fs.frames[j].get<cv::cuda::GpuMat>(chan), src->hq_bitrate, [this,src,hasChan2,chan,&cv,&chan2done](const ftl::codecs::Packet &blk){ + _transmitPacket(src, blk, chan, hasChan2, Quality::High); + chan2done = true; + cv.notify_one(); + }); + } catch (std::exception &e) { + LOG(ERROR) << "Exception in encoder: " << e.what(); + chan2done = true; + cv.notify_one(); + } }); - } catch (std::exception &e) { - LOG(ERROR) << "Exception in encoder: " << e.what(); + } else { chan2done = true; - cv.notify_one(); + LOG(ERROR) << "Insufficient encoder resources"; } - }); + } else { + // Already have an encoding so send this + const auto &packets = fs.frames[j].getPackets(chan); + LOG(INFO) << "Send existing chan2 encoding: " << packets.size(); + for (const auto &i : packets) { + _transmitPacket(src, i, chan, hasChan2, Quality::High); + } + } } else { - if (enc2) enc2->reset(); + // No second channel requested... + if (src->hq_encoder_c2) src->hq_encoder_c2->reset(); chan2done = true; } + - // TODO: Stagger the reset between nodes... random phasing - if (fs.timestamp % (10*ftl::timer::getInterval()) == 0) enc1->reset(); - enc1->encode(fs.frames[j].get<cv::cuda::GpuMat>(Channel::Colour), src->hq_bitrate, [this,src,hasChan2](const ftl::codecs::Packet &blk){ - _transmitPacket(src, blk, Channel::Colour, hasChan2, Quality::High); - }); + // TODO: Use ColourHighQuality if available + if (fs.frames[j].getPackets(Channel::Colour).size() == 0) { + if (!src->hq_encoder_c1) src->hq_encoder_c1 = ftl::codecs::allocateEncoder( + definition_t::HD1080, hq_devices_, hq_codec_); + auto *enc = src->hq_encoder_c1; + + if (enc) { + // TODO: Stagger the reset between nodes... random phasing + if (fs.timestamp % (10*ftl::timer::getInterval()) == 0) enc->reset(); + enc->encode(fs.frames[j].get<cv::cuda::GpuMat>(Channel::Colour), src->hq_bitrate, [this,src,hasChan2](const ftl::codecs::Packet &blk){ + _transmitPacket(src, blk, Channel::Colour, hasChan2, Quality::High); + }); + } else { + LOG(ERROR) << "Insufficient encoder resources"; + } + } else { + const auto &packets = fs.frames[j].getPackets(Channel::Colour); + // FIXME: Adjust block number and total to match number of packets + // Also requires the receiver to decode in block number order. + LOG(INFO) << "Send existing encoding: " << packets.size(); + for (const auto &i : packets) { + _transmitPacket(src, i, Channel::Colour, hasChan2, Quality::High); + } + } // Ensure both channels have been completed. std::unique_lock<std::mutex> lk(mtx); cv.wait(lk, [&chan2done]{ return chan2done; }); - } + //} } // Do we need to do low quality encoding? @@ -546,7 +592,7 @@ void Streamer::_process(ftl::rgbd::FrameSet &fs) { // Make sure to unlock so clients can connect! //lk.unlock(); slk.unlock(); - sleep_for(milliseconds(50)); + //sleep_for(milliseconds(50)); } else _cleanUp(); } @@ -563,6 +609,7 @@ void Streamer::_transmitPacket(StreamSource *src, const ftl::codecs::Packet &pkt } void Streamer::_transmitPacket(StreamSource *src, const ftl::codecs::StreamPacket &spkt, const ftl::codecs::Packet &pkt, Quality q) { + //LOG(INFO) << "TRANSMIT: " << spkt.timestamp; // Lock to prevent clients being added / removed //SHARED_LOCK(src->mutex,lk); auto c = src->clients.begin(); -- GitLab