Skip to content
Snippets Groups Projects
Select Git revision
  • 0d9a91618b94353020ae2b3106740360c84d8958
  • master default protected
  • feature/SKR
  • censuseval
  • exp/labcolours
  • feature/disconflow
  • exp/multires-sgm
  • chromatest
  • bug/feedrecord
  • feature/375/fullres-fstream
  • feature/poses
  • feature/stereocalib
  • feature/gui2-pch
  • feature/gui2-nanogui-mitsuba
  • feature/use-new-frame
  • feature/depth-touch
  • feature/use10bit
  • bug/optflow-disparity
  • feature/websocket-pose
  • exp/candidatemask
  • feature/sgm-experimental
  • v0.0.6
  • v0.0.5
  • v0.0.4
  • v0.0.3
  • v0.0.2
  • v0.0.1
27 results

pylon.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    pylon.cpp 10.95 KiB
    #include "pylon.hpp"
    
    #include "rectification.hpp"
    
    #include <loguru.hpp>
    #include <ftl/threads.hpp>
    #include <ftl/rgbd/source.hpp>
    #include <ftl/profiler.hpp>
    #include <ftl/rgbd/frame.hpp>
    
    #include <pylon/PylonIncludes.h>
    #include <pylon/BaslerUniversalInstantCamera.h>
    
    #include <opencv2/imgproc.hpp>
    
    #include <nlohmann/json.hpp>
    
    using ftl::rgbd::detail::StereoRectification;
    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),
    		interpolation_(cv::INTER_CUBIC) {
    
    	auto &inst = CTlFactory::GetInstance();
    
    	Pylon::DeviceInfoList_t devices;
    	inst.EnumerateDevices(devices);
    
    	int dev_left_num = -1;
    	std::string dev_left;
    
    	if (getConfig()["device_left"].is_number()) {
    		dev_left = std::to_string(value("device_left",0));
    	} else {
    		dev_left = value("device_left", std::string("default"));
    	}
    
    	if (devices.size() == 0) {
    		LOG(ERROR) << "No Pylon devices attached";
    		return;
    	} else {
    		int i=0;
    		for (auto d : devices) {
    			if (std::string(d.GetSerialNumber()) == dev_left) {
    				dev_left_num = i;
    			}
    
    			if (dev_left_num == i) {
    				LOG(INFO) << " - found Pylon device - " << d.GetSerialNumber() << " (" << d.GetModelName() << ") [primary]";
    			} else {
    				LOG(INFO) << " - found Pylon device - " << d.GetSerialNumber() << " (" << d.GetModelName() << ")";
    			}
    
    			++i;
    		}
    	}
    
    	if (dev_left_num == -1) dev_left_num = 0;
    
    	name_ = devices[dev_left_num].GetModelName();
    	serial_ = devices[dev_left_num].GetSerialNumber();
    
    	try {
    		lcam_ = new CBaslerUniversalInstantCamera( CTlFactory::GetInstance().CreateDevice(devices[dev_left_num]));
    		lcam_->RegisterConfiguration( new Pylon::CSoftwareTriggerConfiguration, Pylon::RegistrationMode_ReplaceAll, Pylon::Cleanup_Delete);
    		lcam_->Open();
    
    		if (devices.size() >= 2) {
    			int dev_right = (dev_left_num == 0) ? 1 : 0;
    			rcam_ = new CBaslerUniversalInstantCamera( CTlFactory::GetInstance().CreateDevice(devices[dev_right]));
    			rcam_->RegisterConfiguration( new Pylon::CSoftwareTriggerConfiguration, Pylon::RegistrationMode_ReplaceAll, Pylon::Cleanup_Delete);
    			rcam_->Open();
    		}
    
    		_configureCamera(lcam_);
    		if (rcam_) _configureCamera(rcam_);
    
    		lcam_->StartGrabbing( Pylon::GrabStrategy_OneByOne);
    		if (rcam_) rcam_->StartGrabbing( Pylon::GrabStrategy_OneByOne);
    
    		if (rcam_) rcam_->WaitForFrameTriggerReady( 300, Pylon::TimeoutHandling_ThrowException);
    		lcam_->WaitForFrameTriggerReady( 300, Pylon::TimeoutHandling_ThrowException);
    
    		ready_ = true;
    	} catch (const Pylon::GenericException &e) {
    		// Error handling.
    		LOG(ERROR) << "Pylon: An exception occurred - " << e.GetDescription();
    	}
    
    	// Choose a good default depth res
    	//width_ = value("depth_width", std::min(1280u,fullwidth_)) & 0xFFFe;
    	//float aspect = float(fullheight_) / float(fullwidth_);
    	//height_ = value("depth_height", std::min(uint32_t(aspect*float(width_)), fullheight_)) & 0xFFFe;
    
    	//LOG(INFO) << "Depth resolution: " << width_ << "x" << height_;
    
    	// Allocate page locked host memory for fast GPU transfer
    	left_hm_ = cv::cuda::HostMem(fullheight_, fullwidth_, CV_8UC4);
    	right_hm_ = cv::cuda::HostMem(fullheight_, fullwidth_, CV_8UC4);
    	//hres_hm_ = cv::cuda::HostMem(fullheight_, fullwidth_, CV_8UC4);
    	//rtmp_.create(fullheight_, fullwidth_, CV_8UC4);
    
    	on("exposure", [this]() {
    		if (lcam_->GetDeviceInfo().GetModelName() != "Emulation") {
    			lcam_->ExposureTime.SetValue(value("exposure", 24000.0f));  // Exposure time in microseconds
    		}
    		if (rcam_ && rcam_->GetDeviceInfo().GetModelName() != "Emulation") {
    			rcam_->ExposureTime.SetValue(value("exposure", 24000.0f));  // Exposure time in microseconds
    		}
    	});
    
    	on("buffer_size", buffer_size_, 1);
    
    	interpolation_ = value("inter_cubic", true) ? cv::INTER_CUBIC : cv::INTER_LINEAR;
    	on("inter_cubic", [this](){
    		interpolation_ = value("inter_cubic", true) ?
    			cv::INTER_CUBIC : cv::INTER_LINEAR;
    	});
    
    	monitor_ = true;
    	temperature_monitor_ = ftl::timer::add(ftl::timer::timerlevel_t::kTimerIdle1, 10.0, [this](int64_t ts) {
    		float temperature = (rcam_) ? std::max(lcam_->DeviceTemperature(), rcam_->DeviceTemperature()) : lcam_->DeviceTemperature();
    
    		LOG_IF(WARNING, temperature > 53.0)
    			<< "Camera temperature over 50C (value: " << temperature << ")";
    
    		// TODO: check actual temperature status.
    		if (temperature > 65.0) {
    			LOG(FATAL) << "Cameras are overheating";
    		}
    
    		return true;
    	});
    }
    
    PylonDevice::~PylonDevice() {
    	monitor_ = false;
    	temperature_monitor_.cancel();
    
    	lcam_->Close();
    	rcam_->Close();
    }
    
    static std::vector<ftl::rgbd::detail::DeviceDetails> pylon_devices;
    static bool pylon_dev_init = false;
    
    std::vector<ftl::rgbd::detail::DeviceDetails> PylonDevice::listDevices() {
    	if (pylon_dev_init) return pylon_devices;
    	pylon_dev_init = true;
    
    	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;
    	}
    
    	pylon_devices = results;
    	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";
    	}
    
    	if (cam->GetDeviceInfo().GetModelName() != "Emulation") {
    		// Emulated device throws exception with these
    		cam->ExposureTime.SetValue(value("exposure", 24000.0f));  // Exposure time in microseconds
    		cam->AutoTargetBrightness.SetValue(0.3);
    		cam->LightSourcePreset.SetValue(Basler_UniversalCameraParams::LightSourcePreset_Tungsten2800K);  // White balance option
    		cam->BalanceWhiteAuto.SetValue(Basler_UniversalCameraParams::BalanceWhiteAuto_Once);
    		cam->GainAuto.SetValue(Basler_UniversalCameraParams::GainAuto_Once);
    	}
    }
    
    bool PylonDevice::grab() {
    	if (!isReady()) return false;
    
    	//int dev;
    	//cudaGetDevice(&dev);
    	//LOG(INFO) << "Current cuda device = " << dev;
    
    	try {
    		FTL_Profile("Frame Capture", 0.001);
    		if (rcam_) rcam_->WaitForFrameTriggerReady( 0, Pylon::TimeoutHandling_ThrowException);
    		lcam_->WaitForFrameTriggerReady( 0, 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::_retrieveFrames(Pylon::CGrabResultPtr &result, Pylon::CBaslerUniversalInstantCamera *cam) {
    	do {
    		if (cam->RetrieveResult(0, result, Pylon::TimeoutHandling_Return)) {
    			if (!result->GrabSucceeded()) {
    				LOG(ERROR) << "Retrieve failed " << result->GetErrorDescription();
    			}
    		} else {
    			LOG(ERROR) << "Pylon frame missing";
    			return false;
    		}
    	} while (!result->GrabSucceeded());
    	return true;
    }
    
    bool PylonDevice::get(ftl::rgbd::Frame &frame, StereoRectification *c, cv::cuda::Stream &stream) {
    	if (!isReady()) return false;
    
    	Mat l, r;
    
    	// Use page locked memory
    	l = left_hm_.createMatHeader();
    	r = right_hm_.createMatHeader();
    
    	if (isStereo()) {
    		auto lcount = lcam_->NumReadyBuffers.GetValue();
    		auto rcount = rcam_->NumReadyBuffers.GetValue();
    
    		/*if (left_fail_) {
    			left_fail_ = 0;
    			Pylon::CGrabResultPtr tmp_result;
    			lcam_->RetrieveResult(0, tmp_result, Pylon::TimeoutHandling_Return);
    		}
    		if (rcam_ && right_fail_) {
    			right_fail_ = 0;
    			Pylon::CGrabResultPtr tmp_result;
    			rcam_->RetrieveResult(0, tmp_result, Pylon::TimeoutHandling_Return);
    		}*/
    
    		if (rcount < buffer_size_ || lcount < buffer_size_) {
    			LOG(WARNING) << "Retrieve failed for L+R";
    			return false;
    		}
    
    		if (rcount > buffer_size_ && lcount > buffer_size_) {
    			LOG(WARNING) << "Pylon buffer latency problem : " << lcount << " vs " << rcount << " frames";
    			Pylon::CGrabResultPtr tmp_result;
    			//lcam_->RetrieveResult(0, tmp_result, Pylon::TimeoutHandling_Return);
    			//rcam_->RetrieveResult(0, tmp_result, Pylon::TimeoutHandling_Return);
    			_retrieveFrames(tmp_result, lcam_);
    			_retrieveFrames(tmp_result, rcam_);
    		} else if (rcount > buffer_size_) LOG(ERROR) << "Buffers (R) out of sync by " << rcount;
    		else if (lcount > buffer_size_) LOG(ERROR) << "Buffers (L) out of sync by " << lcount;
    	} else {
    		if (lcam_->NumReadyBuffers.GetValue() == 0) {
    			LOG(INFO) << "Retrieve failed for L";
    			return false;
    		}
    	}
    
    	try {
    		FTL_Profile("Frame Retrieve", 0.005);
    		bool res_r = false;
    
    		if (rcam_) {
    			Pylon::CGrabResultPtr result_right;
    
    			if (_retrieveFrames(result_right, rcam_)) {
    
    				cv::Mat wrap_right(
    				result_right->GetHeight(),
    				result_right->GetWidth(),
    				CV_8UC1,
    				(uint8_t*)result_right->GetBuffer());
    
    				{
    					FTL_Profile("Bayer Colour (R)", 0.005);
    					cv::cvtColor(wrap_right, rtmp_, cv::COLOR_BayerRG2BGRA);
    				}
    
    				{
    					FTL_Profile("Rectify (R)", 0.005);
    					c->rectify(rtmp_, r, Channel::Right);
    				}
    
    				auto& f_right = frame.create<ftl::rgbd::VideoFrame>(Channel::Right);
    				cv::cuda::GpuMat& r_out = f_right.createGPU();
    				cv::Mat &r_host = f_right.setCPU();
    
    				r_out.upload(r, stream);
    				r.copyTo(r_host);
    				res_r = true;
    			}
    		}
    
    		Pylon::CGrabResultPtr result_left;
    
    		if (!_retrieveFrames(result_left, lcam_)) {
    			return false;
    		}
    
    		cv::Mat wrap_left(
    			result_left->GetHeight(),
    			result_left->GetWidth(),
    			CV_8UC1,
    			(uint8_t*)result_left->GetBuffer());
    
    		{
    			FTL_Profile("Bayer Colour (L)", 0.005);
    			if (isStereo()) cv::cvtColor(wrap_left, ltmp_, cv::COLOR_BayerRG2BGRA);
    			else cv::cvtColor(wrap_left, l, cv::COLOR_BayerRG2BGRA);
    		}
    
    		{
    			FTL_Profile("Rectify (L)", 0.005);
    			if (isStereo()) {
    				c->rectify(ltmp_, l, Channel::Left);
    			}
    		}
    
    		auto& f_left = frame.create<ftl::rgbd::VideoFrame>(Channel::Left);
    		cv::cuda::GpuMat& l_out = f_left.createGPU();
    		cv::Mat &l_host = f_left.setCPU();
    
    		l_out.upload(l, stream);
    		l.copyTo(l_host);
    
    		if (rcam_) {
    			if (!res_r) return false;
    		}
    
    	} catch (const GenericException &e) {
    		LOG(ERROR) << "Pylon: An exception occurred - " << e.GetDescription();
    	}
    
    	return true;
    }
    
    bool PylonDevice::isReady() const {
    	return lcam_ && lcam_->IsOpen();
    }
    
    void PylonDevice::populateMeta(std::map<std::string,std::string> &meta) const {
    	meta["device"] = name_;
    	meta["serial"] = serial_;
    }