Newer
Older

Nicolas Pope
committed
/*
* Copyright 2019 Nicolas Pope. All rights reserved.
*
* See LICENSE.
*/
#include <glog/logging.h>
#include <ftl/config.h>
#include <zlib.h>
// #include <lz4.h>

Nicolas Pope
committed
#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>

Nicolas Pope
committed
#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;

Nicolas Pope
committed
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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"]);

Nicolas Pope
committed
mutex m;
Display disp(config["display"]);

Nicolas Pope
committed
// 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) {

Nicolas Pope
committed
unique_lock<mutex> lk(m);
cv::imdecode(jpg, cv::IMREAD_COLOR, &rgb);
//LOG(INFO) << "Received JPG : " << rgb.cols;

Nicolas Pope
committed
depth = Mat(rgb.size(), CV_16UC1);

Nicolas Pope
committed
/*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);

Nicolas Pope
committed
inflateEnd(&infstream);*/
//LZ4_decompress_safe((char*)d.data(), (char*)depth.data, d.size(), depth.step*depth.rows);

Nicolas Pope
committed
cv::imdecode(d, cv::IMREAD_UNCHANGED, &depth);
depth.convertTo(depth, CV_32FC1, 1.0f/256.0f); //, 1.0f/16.0f); //, 1.0f/256.0f);

Nicolas Pope
committed
});
while (disp.active()) {
Mat idepth;

Nicolas Pope
committed
unique_lock<mutex> lk(m);
if (depth.cols > 0) {
// If no calibration data then get it from the remote machine

Nicolas Pope
committed
// Must unlock before calling findOne to prevent net block!!
lk.unlock();
auto buf = net.findOne<vector<unsigned char>>((string)config["source"]+"/calibration");

Nicolas Pope
committed
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

Nicolas Pope
committed
lk.unlock();
//if (cv::waitKey(40) == 27) break;
disp.wait(40);

Nicolas Pope
committed
}
}
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] : "");
}