Skip to content
Snippets Groups Projects
Select Git revision
  • feature/SKR
  • master default protected
  • 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
26 results

main.cpp

Blame
  • Code owners
    Assign users and groups as approvers for specific file changes. Learn more.
    main.cpp 7.84 KiB
    /*
     * Copyright 2019 Nicolas Pope. All rights reserved.
     *
     * See LICENSE.
     */
    
    #define LOGURU_WITH_STREAMS 1
    #include <loguru.hpp>
    #include <ftl/configuration.hpp>
    #include <ctpl_stl.h>
    
    #include <string>
    #include <map>
    #include <vector>
    #include <fstream>
    #include <thread>
    
    #include <opencv2/opencv.hpp>
    #include <ftl/rgbd.hpp>
    #include <ftl/data/framepool.hpp>
    #include <ftl/streams/builder.hpp>
    //#include <ftl/middlebury.hpp>
    #include <ftl/net/universe.hpp>
    #include <ftl/master.hpp>
    #include <nlohmann/json.hpp>
    #include <ftl/operators/disparity.hpp>
    #include <ftl/operators/detectandtrack.hpp>
    
    #include <ftl/streams/netstream.hpp>
    #include <ftl/streams/sender.hpp>
    #include <ftl/streams/receiver.hpp>
    
    #include <ftl/audio/source.hpp>
    
    #include "opencv2/imgproc.hpp"
    #include "opencv2/imgcodecs.hpp"
    #include "opencv2/highgui.hpp"
    #include "opencv2/core/utility.hpp"
    
    #ifdef HAVE_PYLON
    #include <pylon/PylonIncludes.h>
    #endif
    
    #ifdef WIN32
    #pragma comment(lib, "Rpcrt4.lib")
    #endif
    
    using ftl::rgbd::Source;
    using ftl::rgbd::Camera;
    using ftl::codecs::Channel;
    using ftl::net::Universe;
    using std::string;
    using std::vector;
    using std::map;
    using std::condition_variable;
    using std::this_thread::sleep_for;
    using std::chrono::milliseconds;
    using cv::Mat;
    using json = nlohmann::json;
    
    static void run(ftl::Configurable *root) {
    	Universe *net = ftl::create<Universe>(root, "net");
    
    	ftl::timer::setHighPrecision(true);
    
    	if (root->value("time_master", false)) {
    		net->bind("time_master", [net]() {
    			return net->id();
    		});
    		LOG(INFO) << "Becoming a time master";
    	}
    
    	if (root->get<string>("time_peer")) {
    		if (!net->connect(*root->get<string>("time_peer"))->waitConnection()) {
    			LOG(ERROR) << "Could not connect to time master";
    		} else {
    			LOG(INFO) << "Connected to time master";
    		}
    	}
    
    	auto opt_time_master = net->findOne<ftl::UUID>(string("time_master"));
    	ftl::UUID time_peer(0);
    	if (opt_time_master) {
    		time_peer = *opt_time_master;
    		LOG(INFO) << "Found a time master: " << time_peer.to_string();
    	}
    	int sync_counter = 0;
    
    	ftl::ctrl::Master ctrl(root, net);
    
    	// Sync clocks!
    	auto timer = ftl::timer::add(ftl::timer::kTimerMain, [&time_peer,&sync_counter,net](int64_t ts) {
    		if (sync_counter-- <= 0 && time_peer != ftl::UUID(0) ) {
    			sync_counter = 20;
    			auto start = std::chrono::high_resolution_clock::now();
    
    			try {
    				net->asyncCall<int64_t>(time_peer, "__ping__", [start](const int64_t &mastertime) {
    					auto elapsed = std::chrono::high_resolution_clock::now() - start;
    					int64_t latency = std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count();
    					auto clock_adjust = mastertime + ((latency+500)/2000) - ftl::timer::get_time();
    
    					//LOG(INFO) << "LATENCY: " << float(latency)/1000.0f << "ms";
    
    					if (clock_adjust != 0) {
    						LOG(INFO) << "Clock adjustment: " << clock_adjust << ", latency=" << float(latency)/1000.0f << "ms";
    						ftl::timer::setClockAdjustment(clock_adjust);
    					}		
    				});
    			} catch (const std::exception &e) {
    				LOG(ERROR) << "Ping failed, could not time sync: " << e.what();
    				return true;
    			}
    		}
    		return true;
    	});
    
    	auto paths = root->get<vector<string>>("paths");
    	string file = "";
    	//if (paths && (*paths).size() > 0) file = (*paths)[(*paths).size()-1];
    
    	for (auto &x : *paths) {
    		//LOG(INFO) << "PATH - " << x;
    		if (x != "") {
    			ftl::URI uri(x);
    			if (uri.isValid()) {
    				switch (uri.getScheme()) {
    				case ftl::URI::SCHEME_WS		:
    				case ftl::URI::SCHEME_TCP		: net->connect(x)->waitConnection(); break;
    				case ftl::URI::SCHEME_DEVICE	:
    				case ftl::URI::SCHEME_FILE		: file = x; break;
    				default: break;
    				}
    			}
    		}
    	}
    
    	Source *source = nullptr;
    	source = ftl::create<Source>(root, "source", net);
    	if (file != "") {
    		//source->set("uri", file);
    		ftl::URI uri(file);
    		uri.to_json(source->getConfig());
    		source->set("uri", uri.getBaseURI());
    	}
    	
    	ftl::stream::Sender *sender = ftl::create<ftl::stream::Sender>(root, "sender");
    	ftl::stream::Net *outstream = ftl::create<ftl::stream::Net>(root, "stream", net);
    	outstream->set("uri", outstream->getID());
    	outstream->begin();
    	sender->setStream(outstream);
    
    	ftl::audio::Source *audioSrc = ftl::create<ftl::audio::Source>(root, "audio_test");
    
    	ftl::data::Pool pool(2,5);
    	auto *creator = new ftl::streams::IntervalSourceBuilder(&pool, 0, {source, audioSrc});
    	std::shared_ptr<ftl::streams::BaseBuilder> creatorptr(creator);
    
    	ftl::stream::Receiver *receiver = ftl::create<ftl::stream::Receiver>(root, "receiver", &pool);
    	receiver->setStream(outstream);
    	receiver->registerBuilder(creatorptr);
    
    	// Send channels on flush
    	auto flushhandle = pool.onFlushSet([sender](ftl::data::FrameSet &fs, ftl::codecs::Channel c) {
    		// TODO: Check the channel to see if it should be sent or not
    		switch (c) {
    		case Channel::Colour	:
    		case Channel::Colour2	:
    		case Channel::Depth		: sender->post(fs, c); break;
    		default: break;
    		}
    		return true;
    	});
    
    	int stats_count = 0;
    	int frames = 0;
    	float latency = 0.0f;
    
    	auto *pipeline = ftl::config::create<ftl::operators::Graph>(root, "pipeline");
    	pipeline->append<ftl::operators::DetectAndTrack>("facedetection")->value("enabled", false);
    	pipeline->append<ftl::operators::ArUco>("aruco")->value("enabled", false);
    	pipeline->append<ftl::operators::DepthChannel>("depth");  // Ensure there is a depth channel
    
    	bool busy = false;
    
    	auto h = creator->onFrameSet([sender,&stats_count,&latency,&frames,pipeline,&busy](const ftl::data::FrameSetPtr &fs) {
    		if (busy) return true;
    		busy = true;
    
    		// TODO: Remove, this is debug code
    		if (fs->firstFrame().changed(ftl::codecs::Channel::Control)) {
    			LOG(INFO) << "Got control: " << fs->firstFrame().get<int>(ftl::codecs::Channel::Control);
    		}
    
    		// Do all processing in another thread...
    		ftl::pool.push([sender,&stats_count,&latency,&frames,pipeline,&busy,fs](int id) {
    			// Do pipeline here...
    			pipeline->apply(*fs, *fs);
    
    			// Send any remaining channels...
    			// Note: Ensures these send now, otherwise waits until destructor
    			//fs->flush();
    
    			++frames;
    			latency += float(ftl::timer::get_time() - fs->timestamp());
    
    			if (--stats_count <= 0) {
    				latency /= float(frames);
    				LOG(INFO) << "Frame rate: " << frames << ", Latency: " << latency;
    				stats_count = 20;
    				frames = 0;
    				latency = 0.0f;
    			}
    
    			busy = false;
    		});
    
    		// Lock colour right now to encode in parallel
    		fs->flush(ftl::codecs::Channel::Colour);
    		fs->flush(ftl::codecs::Channel::AudioStereo);
    
    		return true;
    	});
    
    	// Start the timed generation of frames
    	creator->start();
    
    	// TODO: TEMPORARY
    	/*ftl::audio::Source *audioSrc = ftl::create<ftl::audio::Source>(root, "audio_test");
    	audioSrc->onFrameSet([sender](ftl::audio::FrameSet &fs) {
    		sender->post(fs);
    		return true;
    	}); */
    	
    	net->start();
    
    	LOG(INFO) << "Running...";
    	ftl::timer::start(true);
    	LOG(INFO) << "Stopping...";
    	ctrl.stop();
    	
    	net->shutdown();
    
    	ftl::pool.stop();
    
    	delete outstream;
    
    	//delete source;  // TODO(Nick) Add ftl::destroy
    	delete net;
    }
    
    static void threadSetCUDADevice() {
    	// Ensure all threads have correct cuda device
    	std::atomic<int> ijobs = 0;
    	for (int i=0; i<ftl::pool.size(); ++i) {
    		ftl::pool.push([&ijobs](int id) {
    			ftl::cuda::setDevice();
    			++ijobs;
    			while (ijobs < ftl::pool.size()) std::this_thread::sleep_for(std::chrono::milliseconds(10));
    		});
    	}
    	while (ijobs < ftl::pool.size()) std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    
    int main(int argc, char **argv) {
    #ifdef HAVE_PYLON
    	Pylon::PylonAutoInitTerm autoInitTerm;
    #endif
    
    #ifdef WIN32
    	SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
    #endif
    	std::cout << "FTL Vision Node " << FTL_VERSION_LONG << std::endl;
    	auto root = ftl::configure(argc, argv, "vision_default");
    
    	// Use other GPU if available.
    	//ftl::cuda::setDevice(ftl::cuda::deviceCount()-1);
    	
    	std::cout << "Loading..." << std::endl;
    	run(root);
    
    	delete root;
    	LOG(INFO) << "Terminating with code " << ftl::exit_code;
    	LOG(INFO) << "Branch: " << ftl::branch_name;
    	return ftl::exit_code;
    }