/* * Copyright 2019 Nicolas Pope. All rights reserved. * * See LICENSE. */ #include <glog/logging.h> #include <ftl/config.h> #include <zlib.h> #include <string> #include <map> #include <vector> #include <fstream> #include <thread> #include <chrono> #include <mutex> #include <opencv2/opencv.hpp> #include <ftl/net/universe.hpp> #include <ftl/display.hpp> #include <nlohmann/json.hpp> #include "opencv2/imgproc.hpp" #include "opencv2/imgcodecs.hpp" #include "opencv2/highgui.hpp" #include "opencv2/core/utility.hpp" #ifdef WIN32 #pragma comment(lib, "Rpcrt4.lib") #endif using ftl::net::Universe; using ftl::Display; using std::string; using std::vector; using std::map; using cv::Mat; using json = nlohmann::json; using std::ifstream; using std::this_thread::sleep_for; using std::chrono::milliseconds; using std::mutex; using std::unique_lock; // Store loaded configuration static json config; /** * Find and load a JSON configuration file */ static bool findConfiguration(const string &file) { ifstream i; if (file != "") i.open(file); if (!i.is_open()) i.open("./config.json"); if (!i.is_open()) i.open(FTL_LOCAL_CONFIG_ROOT "/config.json"); if (!i.is_open()) i.open(FTL_GLOBAL_CONFIG_ROOT "/config.json"); if (!i.is_open()) return false; i >> config; config = config["representation"]; return true; } /** * Generate a map from command line option to value */ map<string, string> read_options(char ***argv, int *argc) { map<string, string> opts; while (*argc > 0) { string cmd((*argv)[0]); if (cmd[0] != '-') break; size_t p; if ((p = cmd.find("=")) == string::npos) { opts[cmd.substr(2)] = "true"; } else { opts[cmd.substr(2, p-2)] = cmd.substr(p+1); } (*argc)--; (*argv)++; } return opts; } /** * Put command line options into json config. If config element does not exist * or is of a different type then report an error. */ static void process_options(const map<string, string> &opts) { for (auto opt : opts) { if (opt.first == "config") continue; if (opt.first == "version") { std::cout << "FTL Vision Node - v" << FTL_VERSION << std::endl; std::cout << FTL_VERSION_LONG << std::endl; exit(0); } try { auto ptr = json::json_pointer("/"+opt.first); // TODO(nick) Allow strings without quotes auto v = json::parse(opt.second); if (v.type() != config.at(ptr).type()) { LOG(ERROR) << "Incorrect type for argument " << opt.first; continue; } config.at(ptr) = v; } catch(...) { LOG(ERROR) << "Unrecognised option: " << opt.first; } } } static void run(const string &file) { Universe net(config["net"]); Mat rgb, depth, Q; mutex m; Display disp(config["display"]); // Make sure connections are complete sleep_for(milliseconds(500)); // TODO(nick) Allow for many sources net.subscribe(config["source"], [&rgb,&m,&depth](const vector<unsigned char> &jpg, const vector<unsigned char> &d) { unique_lock<mutex> lk(m); cv::imdecode(jpg, cv::IMREAD_COLOR, &rgb); //LOG(INFO) << "Received JPG : " << rgb.cols; depth = Mat(rgb.size(), CV_32FC1); z_stream infstream; infstream.zalloc = Z_NULL; infstream.zfree = Z_NULL; infstream.opaque = Z_NULL; // setup "b" as the input and "c" as the compressed output infstream.avail_in = (uInt)d.size(); // size of input infstream.next_in = (Bytef *)d.data(); // input char array infstream.avail_out = (uInt)depth.step*depth.rows; // size of output infstream.next_out = (Bytef *)depth.data; // output char array // the actual DE-compression work. inflateInit(&infstream); inflate(&infstream, Z_NO_FLUSH); inflateEnd(&infstream); //cv::imdecode(d, cv::IMREAD_GRAYSCALE, &depth); //depth.convertTo(depth, CV_32FC1); //, 1.0f/16.0f); //, 1.0f/256.0f); }); while (disp.active()) { Mat idepth; unique_lock<mutex> lk(m); if (depth.cols > 0) { // If no calibration data then get it from the remote machine if (Q.rows == 0) { // Must unlock before calling findOne to prevent net block!! lk.unlock(); auto buf = net.findOne<vector<unsigned char>>((string)config["source"]+"/calibration"); lk.lock(); if (buf) { Q = Mat(cv::Size(4,4), CV_32F); memcpy(Q.data, (*buf).data(), (*buf).size()); if (Q.step*Q.rows != (*buf).size()) LOG(ERROR) << "Corrupted calibration"; disp.setCalibration(Q); } } } if (rgb.cols > 0) { //cv::imshow("RGB", rgb); disp.render(rgb,depth); } // TODO(nick) Use a swap buffer so this can be unlocked earlier lk.unlock(); //if (cv::waitKey(40) == 27) break; disp.wait(40); } } int main(int argc, char **argv) { argc--; argv++; // Process Arguments auto options = read_options(&argv, &argc); if (!findConfiguration(options["config"])) { LOG(FATAL) << "Could not find any configuration!"; } process_options(options); run((argc > 0) ? argv[0] : ""); }