Newer
Older

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

Nicolas Pope
committed
#include <string>
#include <vector>
#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>
#include <ftl/utility/opencv_to_pcl.hpp>
#include <ftl/registration.hpp>

Nicolas Pope
committed
#ifdef WIN32
#pragma comment(lib, "Rpcrt4.lib")
#endif
#ifdef HAVE_PCL
#include <pcl/point_cloud.h>
#include <pcl/common/transforms.h>
#include <pcl/filters/uniform_sampling.h>
#endif

Nicolas Pope
committed
using ftl::net::Universe;
using ftl::Display;

Nicolas Pope
committed
using std::string;
using std::vector;

Nicolas Pope
committed
using json = nlohmann::json;
using std::this_thread::sleep_for;
using std::chrono::milliseconds;
using std::mutex;
using std::unique_lock;
using std::vector;
#ifdef HAVE_PCL
using pcl::PointCloud;
using pcl::PointXYZ;
using pcl::PointXYZRGB;
#endif
using cv::Mat;
string uri_;
Mat rgb_, disp_;
Mat q_;
std::mutex m_;
#ifdef HAVE_PCL
static PointCloud<PointXYZRGB>::Ptr _getPointCloud(Mat rgb, Mat disp, Mat q) {
cv::Mat_<cv::Vec3f> XYZ(disp.rows, disp.cols);
reprojectImageTo3D(disp, XYZ, q, false);
return ftl::utility::matToPointXYZ(XYZ, rgb);
}
#endif
InputStereo(string uri, const Mat Q): uri_(uri), q_(Q) {};
void set(Mat &rgb, Mat &disp) {
unique_lock<mutex> lk(m_);
rgb_ = rgb;
disp_ = disp;
string getURI() { return uri_; }
Mat getQ() { return q_; }
#ifdef HAVE_PCL
PointCloud<PointXYZRGB>::Ptr getPointCloud() { return _getPointCloud(rgb_, disp_, q_); }
#endif
Mat getRGB() { return rgb_; }
Mat getDisparity() { return disp_; }
/* Mat getDepth() {} */
/* Mat getLeftRGB() {} */
/* Mat getRightRGB() {} */
unique_lock<mutex> lock() { return unique_lock<mutex>(m_); } // use recursive mutex instead (and move locking to methods)?
};
#ifdef HAVE_PCL
#include <pcl/filters/uniform_sampling.h>
/*
class InputMerged {
private:
// todo: Abstract input systems, can also use other 3D inputs (like InputMerged)
vector<InputStereo> inputs_;
vector<Eigen::Matrix4f> T_;
public:
PointCloud<PointXYZRGB>::Ptr getPointCloud() {
PointCloud<PointXYZRGB>::Ptr result(new PointCloud<PointXYZRGB>);
for (size_t i = 0; i < T_.size(); i++) {
inputs_[i].lock();
PointCloud<PointXYZRGB>::Ptr cloud = inputs_[i].getPointCloud();
// Documentation: Can be used with cloud_in equal to cloud_out
pcl::transformPointCloud(*cloud, *cloud, T_[i]);
*result += *cloud;
}
PointCloud<PointXYZRGB>::Ptr result_sampled(new PointCloud<PointXYZRGB>);
pcl::UniformSampling<PointXYZRGB> uniform_sampling;
uniform_sampling.setInputCloud(result);
uniform_sampling.setRadiusSearch(0.1f); // todo parametrize
uniform_sampling.filter(*result_sampled);
return result_sampled;
};
*/
std::map<string, Eigen::Matrix4f> loadRegistration() {
std::map<string, Eigen::Matrix4f> registration;
std::ifstream file(string(FTL_LOCAL_CONFIG_ROOT) + "/registration.json");
nlohmann::json load;
file >> load;
for (auto it = load.begin(); it != load.end(); ++it) {
Eigen::Matrix4f m;
auto data = m.data();
for(size_t i = 0; i < 16; i++) {;
data[i] = it.value()[i];
}
registration[it.key()] = m;
}
return registration;
}
void saveRegistration(std::map<string, Eigen::Matrix4f> registration) {
nlohmann::json save;
for (auto &item : registration) {
auto val = nlohmann::json::array();
for(size_t j = 0; j < 16; j++) { val.push_back((float) item.second.data()[j]); }
save[item.first] = val;
std::ofstream file(string(FTL_LOCAL_CONFIG_ROOT) + "/registration.json");
file << save;
}
template <template<class> class Container>
std::map<string, Eigen::Matrix4f> runRegistration(Container<InputStereo> &inputs) {

Nicolas Pope
committed
std::map<string, Eigen::Matrix4f> registration;
// NOTE: uses config["registration"]
if (!config["registration"].is_object()) {
LOG(FATAL) << "Configuration missing \"registration\" entry!";
return registration;
int iter = (int) config["registration"]["calibration"]["iterations"];
int delay = (int) config["registration"]["calibration"]["delay"];
string ref_uri = (string) config["registration"]["reference-source"];
cv::Size pattern_size( (int) config["registration"]["calibration"]["patternsize"][0],
(int) config["registration"]["calibration"]["patternsize"][1]);
// config["registration"] done
size_t ref_i;
bool found = false;
for (size_t i = 0; i < inputs.size(); i++) {
if (inputs[i].getURI() == ref_uri) {
ref_i = i;
found = true;
break;
}
if (!found) { LOG(ERROR) << "Reference input not found!"; return registration; }
for (auto &input : inputs) {
cv::namedWindow("Registration: " + input.getURI(), cv::WINDOW_KEEPRATIO|cv::WINDOW_NORMAL);
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
// vector for every input: vector of point clouds of patterns
vector<vector<PointCloud<PointXYZ>::Ptr>> patterns(inputs.size());
while (iter > 0) {
bool retval = true; // set to false if pattern not found in one of the sources
vector<PointCloud<PointXYZ>::Ptr> patterns_iter(inputs.size());
for (size_t i = 0; i < inputs.size(); i++) {
InputStereo &input = inputs[i];
Mat rgb, disp, Q;
while(true) {
auto lk = input.lock();
rgb = input.getRGB();
disp = input.getDisparity();
Q = input.getQ();
lk.unlock();
// todo solve this somewhere else
if ((rgb.cols > 0) && (disp.cols > 0)) { break; }
else { sleep_for(milliseconds(500)); }
}
retval &= ftl::registration::findChessboardCorners(rgb, disp, Q, pattern_size, patterns_iter[i]);
cv::imshow("Registration: " + input.getURI(), rgb);
}
cv::waitKey(delay);
// every camera found the pattern
if (retval) {
for (size_t i = 0; i < patterns_iter.size(); i++) {
patterns[i].push_back(patterns_iter[i]);
}
iter--;
}
else { LOG(WARNING) << "Pattern not detected on all inputs";}
}
for (auto &input : inputs) { cv::destroyWindow("Registration: " + input.getURI()); }
for (size_t i = 0; i < inputs.size(); i++) {
Eigen::Matrix4f T;
if (i == ref_i) {
T.setIdentity();
}
else {
T = ftl::registration::findTransformation(patterns[i], patterns[ref_i]);
}
registration[inputs[i].getURI()] = T;
}
saveRegistration(registration);
return registration;
}
#endif
bool getCalibration(Universe &net, string src, Mat &Q) {
Q = Mat(cv::Size(4,4), CV_32F);
while(true) {
auto buf = net.findOne<vector<unsigned char>>((string) src +"/calibration");
if (buf) {
memcpy(Q.data, (*buf).data(), (*buf).size());
if (Q.step*Q.rows != (*buf).size()) {
LOG(ERROR) << "Corrupted calibration";
return false;
}
return true;
}
else {
LOG(INFO) << "Could not get calibration, retrying";
sleep_for(milliseconds(500));
}
}
}
static void run() {
Universe net(config["net"]);
// Make sure connections are complete
sleep_for(milliseconds(500));
if (!config["sources"].is_array()) {
LOG(ERROR) << "No sources configured!";
return;
}
std::deque<InputStereo> inputs;
std::vector<Display> displays;
// todo networking and initilization for input should possibly be implemented in their own class
// with error handling etc.
Mat Q;
if (!getCalibration(net, src, Q)) { continue; } // skips input if calibration bad
InputStereo &in = inputs.emplace_back(src, Q);
displays.emplace_back(config["display"], src);
LOG(INFO) << src << " loaded";
net.subscribe(src, [&in](const vector<unsigned char> &jpg, const vector<unsigned char> &d) {
Mat rgb, disp;
cv::imdecode(jpg, cv::IMREAD_COLOR, &rgb);
Mat(rgb.size(), CV_16UC1);
cv::imdecode(d, cv::IMREAD_UNCHANGED, &disp);
disp.convertTo(disp, CV_32FC1, 1.0f/16.0f);
in.set(rgb, disp);
// Displays and Inputs configured
// load point cloud transformations
#ifdef HAVE_PCL
std::map<string, Eigen::Matrix4f> registration;
if (config["registration"]["calibration"]["run"]) {
registration = runRegistration(inputs);
}
else {
registration = loadRegistration();
}
vector<Eigen::Matrix4f> T;
for (auto &input : inputs) { T.push_back(registration[input.getURI()]); }
//
vector<PointCloud<PointXYZRGB>::Ptr> clouds(inputs.size());
Display display_merged(config["display"], "Merged"); // todo
int active = displays.size();
while (active > 0) {
active = 0;
net.broadcast("grab"); // To sync cameras
PointCloud<PointXYZRGB>::Ptr cloud(new PointCloud<PointXYZRGB>);
if (!display.active()) continue;
active += 1;
auto lk = input.lock();
//Mat rgb = input.getRGB();
//Mat disparity = input.getDisparity();
clouds[i] = input.getPointCloud();
display.render(clouds[i]);
//display.render(rgb, disparity);
for (size_t i = 0; i < clouds.size(); i++) {
pcl::transformPointCloud(*clouds[i], *clouds[i], T[i]);
*cloud += *clouds[i];
}
pcl::UniformSampling<PointXYZRGB> uniform_sampling;
uniform_sampling.setInputCloud(cloud);
uniform_sampling.filter(*cloud);
display_merged.render(cloud);
display_merged.wait(50);

Nicolas Pope
committed
}

Nicolas Pope
committed
}
int main(int argc, char **argv) {
ftl::configure(argc, argv, "representation"); // TODO(nick) change to "reconstruction"
run();