From 881f1e313a744d6df8ea3de247ff920a764ee1d5 Mon Sep 17 00:00:00 2001 From: Nicolas Pope <nwpope@utu.fi> Date: Sun, 7 Jun 2020 13:05:38 +0300 Subject: [PATCH] Refactor pylon as local source --- components/rgbd-sources/CMakeLists.txt | 11 +- components/rgbd-sources/src/source.cpp | 8 +- .../src/sources/stereovideo/calibrate.hpp | 1 - .../stereovideo/{local.cpp => opencv.cpp} | 2 +- .../stereovideo/{local.hpp => opencv.hpp} | 0 .../src/sources/stereovideo/pylon.cpp | 223 ++++++++++++++++++ .../src/sources/stereovideo/pylon.hpp | 60 +++++ .../src/sources/stereovideo/stereovideo.cpp | 63 +++-- 8 files changed, 328 insertions(+), 40 deletions(-) rename components/rgbd-sources/src/sources/stereovideo/{local.cpp => opencv.cpp} (99%) rename components/rgbd-sources/src/sources/stereovideo/{local.hpp => opencv.hpp} (100%) create mode 100644 components/rgbd-sources/src/sources/stereovideo/pylon.cpp create mode 100644 components/rgbd-sources/src/sources/stereovideo/pylon.hpp diff --git a/components/rgbd-sources/CMakeLists.txt b/components/rgbd-sources/CMakeLists.txt index d15fd8d61..7e47d77e4 100644 --- a/components/rgbd-sources/CMakeLists.txt +++ b/components/rgbd-sources/CMakeLists.txt @@ -1,6 +1,6 @@ set(RGBDSRC src/sources/stereovideo/calibrate.cpp - src/sources/stereovideo/local.cpp + src/sources/stereovideo/opencv.cpp src/source.cpp src/frame.cpp src/frameset.cpp @@ -18,16 +18,9 @@ if (HAVE_REALSENSE) endif() if (HAVE_PYLON) - list(APPEND RGBDSRC "src/sources/pylon/pylon.cpp") + list(APPEND RGBDSRC "src/sources/stereovideo/pylon.cpp") endif() -if (LibArchive_FOUND) - list(APPEND RGBDSRC - src/sources/snapshot/snapshot.cpp - src/sources/snapshot/snapshot_source.cpp - ) -endif (LibArchive_FOUND) - add_library(ftlrgbd ${RGBDSRC}) # target_compile_options(ftlrgbd PUBLIC $<$<COMPILE_LANGUAGE:CXX>:-fPIC>) diff --git a/components/rgbd-sources/src/source.cpp b/components/rgbd-sources/src/source.cpp index b7ff97b2f..4ced4ba02 100644 --- a/components/rgbd-sources/src/source.cpp +++ b/components/rgbd-sources/src/source.cpp @@ -133,14 +133,8 @@ static ftl::rgbd::BaseSourceImpl *createFileImpl(const ftl::URI &uri, Source *ho } static ftl::rgbd::BaseSourceImpl *createDeviceImpl(const ftl::URI &uri, Source *host) { - if (uri.getPathSegment(0) == "video" || uri.getPathSegment(0) == "camera") { + if (uri.getPathSegment(0) == "video" || uri.getPathSegment(0) == "camera" || uri.getPathSegment(0) == "pylon") { return new StereoVideoSource(host); - } else if (uri.getPathSegment(0) == "pylon") { -#ifdef HAVE_PYLON - return new PylonSource(host); -#else - LOG(ERROR) << "You did not build with 'pylon'"; -#endif } else if (uri.getPathSegment(0) == "realsense") { #ifdef HAVE_REALSENSE return new RealsenseSource(host); diff --git a/components/rgbd-sources/src/sources/stereovideo/calibrate.hpp b/components/rgbd-sources/src/sources/stereovideo/calibrate.hpp index 523608a04..3eaa1bc2d 100644 --- a/components/rgbd-sources/src/sources/stereovideo/calibrate.hpp +++ b/components/rgbd-sources/src/sources/stereovideo/calibrate.hpp @@ -7,7 +7,6 @@ #include <opencv2/core.hpp> #include <opencv2/core/cuda.hpp> -#include "local.hpp" #include <string> #include <vector> #include <ftl/rgbd/camera.hpp> diff --git a/components/rgbd-sources/src/sources/stereovideo/local.cpp b/components/rgbd-sources/src/sources/stereovideo/opencv.cpp similarity index 99% rename from components/rgbd-sources/src/sources/stereovideo/local.cpp rename to components/rgbd-sources/src/sources/stereovideo/opencv.cpp index 7b01d33f2..23c5165a8 100644 --- a/components/rgbd-sources/src/sources/stereovideo/local.cpp +++ b/components/rgbd-sources/src/sources/stereovideo/opencv.cpp @@ -9,7 +9,7 @@ #include <ftl/threads.hpp> #include <ftl/profiler.hpp> -#include "local.hpp" +#include "opencv.hpp" #include "calibrate.hpp" #include <opencv2/core.hpp> #include <opencv2/opencv.hpp> diff --git a/components/rgbd-sources/src/sources/stereovideo/local.hpp b/components/rgbd-sources/src/sources/stereovideo/opencv.hpp similarity index 100% rename from components/rgbd-sources/src/sources/stereovideo/local.hpp rename to components/rgbd-sources/src/sources/stereovideo/opencv.hpp diff --git a/components/rgbd-sources/src/sources/stereovideo/pylon.cpp b/components/rgbd-sources/src/sources/stereovideo/pylon.cpp new file mode 100644 index 000000000..80abb1463 --- /dev/null +++ b/components/rgbd-sources/src/sources/stereovideo/pylon.cpp @@ -0,0 +1,223 @@ +#include "pylon.hpp" + +#include "calibrate.hpp" +#include <loguru.hpp> +#include <ftl/threads.hpp> +#include <ftl/rgbd/source.hpp> + +#include <pylon/PylonIncludes.h> +#include <pylon/BaslerUniversalInstantCamera.h> + +#include <opencv2/imgproc.hpp> + +using ftl::rgbd::detail::PylonDevice; +using std::string; +using ftl::codecs::Channel; +using cv::cuda::GpuMat; +using cv::Mat; +using namespace Pylon; + +PylonDevice::PylonDevice(nlohmann::json &config) + : ftl::rgbd::detail::Device(config), ready_(false), lcam_(nullptr), rcam_(nullptr) { + + auto &inst = CTlFactory::GetInstance(); + + Pylon::DeviceInfoList_t devices; + inst.EnumerateDevices(devices); + + if (devices.size() == 0) { + LOG(ERROR) << "No Pylon devices attached"; + return; + } else { + for (auto d : devices) { + LOG(INFO) << " - found Pylon device - " << d.GetFullName() << "(" << d.GetModelName() << ")"; + } + } + + try { + lcam_ = new CBaslerUniversalInstantCamera( CTlFactory::GetInstance().CreateDevice(devices[0])); + lcam_->RegisterConfiguration( new Pylon::CSoftwareTriggerConfiguration, Pylon::RegistrationMode_ReplaceAll, Pylon::Cleanup_Delete); + lcam_->Open(); + + if (devices.size() >= 2) { + rcam_ = new CBaslerUniversalInstantCamera( CTlFactory::GetInstance().CreateDevice(devices[1])); + rcam_->RegisterConfiguration( new Pylon::CSoftwareTriggerConfiguration, Pylon::RegistrationMode_ReplaceAll, Pylon::Cleanup_Delete); + rcam_->Open(); + } + + _configureCamera(lcam_); + if (rcam_) _configureCamera(rcam_); + + lcam_->StartGrabbing( Pylon::GrabStrategy_OneByOne); + + ready_ = true; + } catch (const Pylon::GenericException &e) { + // Error handling. + LOG(ERROR) << "Pylon: An exception occurred - " << e.GetDescription(); + } + + width_ = value("depth_width", fullwidth_); + height_ = value("depth_height", fullheight_); + + // Allocate page locked host memory for fast GPU transfer + left_hm_ = cv::cuda::HostMem(height_, width_, CV_8UC4); + right_hm_ = cv::cuda::HostMem(height_, width_, CV_8UC4); + hres_hm_ = cv::cuda::HostMem(fullheight_, fullwidth_, CV_8UC4); +} + +PylonDevice::~PylonDevice() { + +} + +std::vector<ftl::rgbd::detail::DeviceDetails> PylonDevice::listDevices() { + auto &inst = CTlFactory::GetInstance(); + + Pylon::DeviceInfoList_t devices; + inst.EnumerateDevices(devices); + + std::vector<ftl::rgbd::detail::DeviceDetails> results; + + int count=0; + for (auto d : devices) { + //LOG(INFO) << " - found Pylon device - " << d.GetFullName() << "(" << d.GetModelName() << ")"; + auto &r = results.emplace_back(); + r.id = count++; + r.name = d.GetModelName(); + r.maxheight = 0; + r.maxwidth = 0; + } + + return results; +} + +void PylonDevice::_configureCamera(CBaslerUniversalInstantCamera *cam) { + // Get the camera control object. + GenApi::INodeMap& nodemap = cam->GetNodeMap(); + // Get the parameters for setting the image area of interest (Image AOI). + CIntegerParameter width(nodemap, "Width"); + CIntegerParameter height(nodemap, "Height"); + CIntegerParameter offsetX(nodemap, "OffsetX"); + CIntegerParameter offsetY(nodemap, "OffsetY"); + + fullwidth_ = width.GetValue(); + fullheight_ = height.GetValue(); + + LOG(INFO) << "Camera resolution = " << fullwidth_ << "x" << fullheight_; + + // Set the pixel data format. + CEnumParameter format(nodemap, "PixelFormat"); + LOG(INFO) << "Camera format: " << format.GetValue(); + + if (format.CanSetValue("BayerBG8")) { // YCbCr422_8 + format.SetValue("BayerBG8"); + } else { + LOG(WARNING) << "Could not change pixel format"; + } +} + +bool PylonDevice::grab() { + if (!isReady()) return false; + + try { + lcam_->WaitForFrameTriggerReady( 30, Pylon::TimeoutHandling_ThrowException); + if (rcam_) rcam_->WaitForFrameTriggerReady( 30, Pylon::TimeoutHandling_ThrowException); + + lcam_->ExecuteSoftwareTrigger(); + if (rcam_) rcam_->ExecuteSoftwareTrigger(); + } catch (const GenericException &e) { + LOG(ERROR) << "Pylon: Trigger exception - " << e.GetDescription(); + return false; + } + + return true; +} + +bool PylonDevice::get(cv::cuda::GpuMat &l_out, cv::cuda::GpuMat &r_out, cv::cuda::GpuMat &h_l, cv::Mat &h_r, Calibrate *c, cv::cuda::Stream &stream) { + if (!isReady()) return false; + + Mat l, r ,hres; + + // Use page locked memory + l = left_hm_.createMatHeader(); + r = right_hm_.createMatHeader(); + hres = hres_hm_.createMatHeader(); + + Mat &lfull = (!hasHigherRes()) ? l : hres; + Mat &rfull = (!hasHigherRes()) ? r : rtmp_; + + try { + Pylon::CGrabResultPtr result_left; + Pylon::CGrabResultPtr result_right; + + int lcount = 0; + if (lcam_->RetrieveResult(0, result_left, Pylon::TimeoutHandling_Return)) ++lcount; + + int rcount = 0; + if (rcam_ && rcam_->RetrieveResult(0, result_right, Pylon::TimeoutHandling_Return)) ++rcount; + + if (lcount == 0 || !result_left->GrabSucceeded()) { + LOG(ERROR) << "Retrieve failed"; + return false; + } + + cv::Mat wrap_left( + result_left->GetHeight(), + result_left->GetWidth(), + CV_8UC1, + (uint8_t*)result_left->GetBuffer()); + + cv::cvtColor(wrap_left, lfull, cv::COLOR_BayerBG2BGRA); + + if (isStereo()) { + c->rectifyLeft(lfull); + } + + if (hasHigherRes()) { + //FTL_Profile("Frame Resize", 0.01); + cv::resize(lfull, l, l.size(), 0.0, 0.0, cv::INTER_CUBIC); + h_l.upload(hres, stream); + } else { + h_l = cv::cuda::GpuMat(); + } + + l_out.upload(l, stream); + + // TODO Perhaps multithread this as for OpenCV device + if (rcount > 0 && result_right->GrabSucceeded()) { + cv::Mat wrap_right( + result_right->GetHeight(), + result_right->GetWidth(), + CV_8UC1, + (uint8_t*)result_right->GetBuffer()); + + cv::cvtColor(wrap_right, rfull, cv::COLOR_BayerBG2BGRA); + + if (isStereo()) { + c->rectifyRight(rfull); + + if (hasHigherRes()) { + // TODO: Use threads? + cv::resize(rfull, r, r.size(), 0.0, 0.0, cv::INTER_CUBIC); + h_r = rfull; + } + else { + h_r = Mat(); + } + } + + r_out.upload(r, stream); + + //frame.create<cv::cuda::GpuMat>(ftl::codecs::Channel::Colour2).upload(tmp_); + } + + } catch (const GenericException &e) { + LOG(ERROR) << "Pylon: An exception occurred - " << e.GetDescription(); + } + + return true; +} + +bool PylonDevice::isReady() const { + return lcam_ && lcam_->IsOpen(); +} + diff --git a/components/rgbd-sources/src/sources/stereovideo/pylon.hpp b/components/rgbd-sources/src/sources/stereovideo/pylon.hpp new file mode 100644 index 000000000..84b0742fb --- /dev/null +++ b/components/rgbd-sources/src/sources/stereovideo/pylon.hpp @@ -0,0 +1,60 @@ +#pragma once +#ifndef _FTL_RGBD_PYLONDEVICE_HPP_ +#define _FTL_RGBD_PYLONDEVICE_HPP_ + +#include "device.hpp" +#include <string> + +namespace Pylon { +class CBaslerUniversalInstantCamera; +} + +namespace ftl { +namespace rgbd { +namespace detail { + +class PylonDevice : public ftl::rgbd::detail::Device { + public: + explicit PylonDevice(nlohmann::json &config); + ~PylonDevice(); + + static std::vector<DeviceDetails> listDevices(); + + bool grab() override; + bool get(cv::cuda::GpuMat &l, cv::cuda::GpuMat &r, cv::cuda::GpuMat &h_l, cv::Mat &h_r, Calibrate *c, cv::cuda::Stream &stream) override; + + unsigned int width() const override { return width_; } + unsigned int height() const override { return height_; }; + + unsigned int fullWidth() const override { return fullwidth_; } + unsigned int fullHeight() const override { return fullheight_; } + + double getTimestamp() const override { return 0.0; } + + bool isStereo() const override { return lcam_ && rcam_; } + + bool isReady() const; + + private: + bool ready_; + Pylon::CBaslerUniversalInstantCamera *lcam_; + Pylon::CBaslerUniversalInstantCamera *rcam_; + cv::Mat tmp_; + uint32_t fullwidth_; + uint32_t fullheight_; + uint32_t width_; + uint32_t height_; + + cv::cuda::HostMem left_hm_; + cv::cuda::HostMem right_hm_; + cv::cuda::HostMem hres_hm_; + cv::Mat rtmp_; + + void _configureCamera(Pylon::CBaslerUniversalInstantCamera *cam); +}; + +} +} +} + +#endif // _FTL_RGBD_PYLON_HPP_ diff --git a/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp b/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp index 00805313c..bb33c6240 100644 --- a/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp +++ b/components/rgbd-sources/src/sources/stereovideo/stereovideo.cpp @@ -24,10 +24,13 @@ #include "ftl/threads.hpp" #include "calibrate.hpp" -#include "local.hpp" +#include "opencv.hpp" + +#ifdef HAVE_PYLON +#include "pylon.hpp" +#endif using ftl::rgbd::detail::Calibrate; -using ftl::rgbd::detail::OpenCVDevice; using ftl::rgbd::detail::StereoVideoSource; using ftl::codecs::Channel; using std::string; @@ -42,7 +45,13 @@ ftl::rgbd::detail::Device::~Device() { StereoVideoSource::StereoVideoSource(ftl::rgbd::Source *host) : ftl::rgbd::BaseSourceImpl(host), ready_(false) { - init(""); + auto uri = host->get<std::string>("uri"); + if (uri) { + init(*uri); + } else { + init(""); + } + } StereoVideoSource::StereoVideoSource(ftl::rgbd::Source *host, const string &file) @@ -59,27 +68,37 @@ StereoVideoSource::~StereoVideoSource() { void StereoVideoSource::init(const string &file) { capabilities_ = kCapVideo | kCapStereo; - /*if (ftl::is_video(file)) { - // Load video file - LOG(INFO) << "Using video file..."; - //lsrc_ = ftl::create<LocalSource>(host_, "feed", file); - } else if (ftl::is_directory(file)) { - // FIXME: This is not an ideal solution... - ftl::config::addPath(file); - - auto vid = ftl::locateFile("video.mp4"); - if (!vid) { - LOG(FATAL) << "No video.mp4 file found in provided paths (" << file << ")"; - } else { - LOG(INFO) << "Using test directory..."; - //lsrc_ = ftl::create<LocalSource>(host_, "feed", *vid); + ftl::URI uri(file); + + if (uri.getScheme() == ftl::URI::SCHEME_DEVICE) { + if (uri.getPathSegment(0) == "pylon") { + #ifdef HAVE_PYLON + LOG(INFO) << "Using Pylon..."; + lsrc_ = ftl::create<ftl::rgbd::detail::PylonDevice>(host_, "feed"); + #else + throw FTL_Error("Not built with pylon support"); + #endif + } else if (uri.getPathSegment(0) == "video" || uri.getPathSegment(0) == "video") { + // Now detect automatically which device to use + #ifdef HAVE_PYLON + auto pylon_devices = ftl::rgbd::detail::PylonDevice::listDevices(); + if (pylon_devices.size() > 0) { + LOG(INFO) << "Using Pylon..."; + lsrc_ = ftl::create<ftl::rgbd::detail::PylonDevice>(host_, "feed"); + } else { + // Use cameras + LOG(INFO) << "Using OpenCV cameras..."; + lsrc_ = ftl::create<ftl::rgbd::detail::OpenCVDevice>(host_, "feed"); + } + #else + // Use cameras + LOG(INFO) << "Using OpenCV cameras..."; + lsrc_ = ftl::create<ftl::rgbd::detail::OpenCVDevice>(host_, "feed"); + #endif } } - else {*/ - // Use cameras - LOG(INFO) << "Using cameras..."; - lsrc_ = ftl::create<OpenCVDevice>(host_, "feed"); - //} + + if (!lsrc_) return; // throw? color_size_ = cv::Size(lsrc_->width(), lsrc_->height()); -- GitLab