Newer
Older
#include <ftl/configuration.hpp>
#include <ftl/net/universe.hpp>
#include <ftl/rgbd/source.hpp>
#include <ftl/rgbd/group.hpp>
#include <ftl/master.hpp>
#include <ftl/streams/receiver.hpp>
#include <ftl/streams/netstream.hpp>
#include <opencv2/core.hpp>
#include <opencv2/aruco.hpp>
#include <opencv2/core/eigen.hpp>
#include <algorithm>
#include <numeric>
#include "util.hpp"
#include "multicalibrate.hpp"
using std::string;
using std::optional;
using std::list;
using std::vector;
using std::map;
using std::pair;
using std::make_pair;
using cv::Mat;
using cv::Scalar;
using cv::Size;
using cv::Point2f;
using cv::Point2d;
using cv::Point3f;
using cv::Point3d;
using cv::Vec4f;
using cv::Vec4d;
using ftl::net::Universe;
using ftl::rgbd::Source;
vector<Point3d> calibration_target = {
Point3d(0.0, 0.0, 0.0),
Point3d(0.15, 0.0, 0.0),
Point3d(0.15, 0.15, 0.0),
Point3d(0.0, 0.15, 0.0),
Point3d(0.25, 0.0, 0.0),
Point3d(0.40, 0.0, 0.0),
Point3d(0.40, 0.15, 0.0),
Point3d(0.25, 0.15, 0.0)
};
Mat createCameraMatrix(const ftl::rgbd::Camera ¶meters) {
Mat m = (cv::Mat_<double>(3,3) <<
parameters.fx, 0.0, -parameters.cx,
0.0, parameters.fy, -parameters.cy,
0.0, 0.0, 1.0);
struct CalibrationParams {
string output_path;
string registration_file;
vector<size_t> idx_cameras;
bool save_extrinsic = true;
bool save_intrinsic = false;
bool optimize_intrinsic = false;
int reference_camera = -1;
double alpha = 0.0;
Size size;
////////////////////////////////////////////////////////////////////////////////
// Visualization
////////////////////////////////////////////////////////////////////////////////
void stack(const vector<Mat> &img, Mat &out, const int rows, const int cols) {
Size size = img[0].size();
Size size_out = Size(size.width * cols, size.height * rows);
if (size_out != out.size() || out.type() != CV_8UC3) {
out = Mat(size_out, CV_8UC3, Scalar(0, 0, 0));
}
for (size_t i = 0; i < img.size(); i++) {
int row = i % rows;
int col = i / rows;
auto rect = cv::Rect(size.width * col, size.height * row, size.width, size.height);
img[i].copyTo(out(rect));
}
}
void stack(const vector<Mat> &img, Mat &out) {
// TODO
int rows = 2;
stack(img, out, rows, cols);
}
void visualizeCalibration( MultiCameraCalibrationNew &calib, Mat &out,
vector<Mat> &rgb, const vector<Mat> &map1,
const vector<Mat> &map2, const vector<cv::Rect> &roi)
{
vector<Scalar> colors = {
Scalar(64, 64, 255),
Scalar(64, 64, 255),
Scalar(64, 255, 64),
Scalar(64, 255, 64),
};
vector<int> markers = {cv::MARKER_SQUARE, cv::MARKER_DIAMOND};
for (size_t c = 0; c < rgb.size(); c++) {
cv::remap(rgb[c], rgb[c], map1[c], map2[c], cv::INTER_CUBIC);
cv::rectangle(rgb[c], roi[c], Scalar(24, 224, 24), 2);
for (int r = 50; r < rgb[c].rows; r = r+50) {
cv::line(rgb[c], cv::Point(0, r), cv::Point(rgb[c].cols-1, r), cv::Scalar(0,0,255), 1);
}
cv::putText(rgb[c],
"Camera " + std::to_string(c),
Point2i(roi[c].x + 10, roi[c].y + 30),
cv::FONT_HERSHEY_COMPLEX_SMALL, 1.0, Scalar(64, 64, 255), 1);
}
////////////////////////////////////////////////////////////////////////////////
// RPC
////////////////////////////////////////////////////////////////////////////////
// Using Mat directly
vector<Mat> getDistortionParametersRPC(ftl::net::Universe* net, ftl::stream::Net* nstream) {
return net->call<vector<Mat>>(nstream->getPeer(), "get_distortion");
}
bool setRectifyRPC( ftl::net::Universe* net, ftl::stream::Net* nstream,
bool enabled) {
return net->call<bool>(nstream->getPeer(), "set_rectify", enabled);
}
bool setIntrinsicsRPC( ftl::net::Universe* net, ftl::stream::Net* nstream,
const Size &size, const vector<Mat> &K, const vector<Mat> &D) {
return net->call<bool>(nstream->getPeer(), "set_intrinsics",
size, K[0], D[0], K[1], D[1] );
bool setExtrinsicsRPC( ftl::net::Universe* net, ftl::stream::Net* nstream,
const Mat &R, const Mat &t) {
return net->call<bool>(nstream->getPeer(), "set_extrinsics", R, t);
bool setPoseRPC(ftl::net::Universe* net, ftl::stream::Net* nstream,
const Mat &pose) {
return net->call<bool>(nstream->getPeer(), "set_pose", pose);
}
bool saveCalibrationRPC(ftl::net::Universe* net, ftl::stream::Net* nstream) {
return net->call<bool>(nstream->getPeer(), "save_calibration");
}
////////////////////////////////////////////////////////////////////////////////
/* run calibration and perform RPC to update calibration on nodes */
void calibrateRPC( ftl::net::Universe* net,
MultiCameraCalibrationNew &calib,
const CalibrationParams ¶ms,
vector<ftl::stream::Net*> &nstreams,
vector<Mat> &map1,
vector<Mat> &map2,
vector<cv::Rect> &roi) {
int reference_camera = params.reference_camera;
if (params.reference_camera < 0) {
reference_camera = calib.getOptimalReferenceCamera();
reference_camera -= (reference_camera & 1);
LOG(INFO) << "optimal camera (automatic): " << reference_camera;
LOG(INFO) << "reference camera: " << reference_camera;
if (params.optimize_intrinsic) calib.setFixIntrinsic(0);
calib.calibrateAll(reference_camera);
vector<Mat> R, t;
calib.getCalibration(R, t);
size_t n_cameras = calib.getCamerasCount();
vector<Mat> R_rect(n_cameras), t_rect(n_cameras);
vector<Mat> Rt_out(n_cameras);
map1.resize(n_cameras);
map2.resize(n_cameras);
for (size_t c = 0; c < n_cameras; c += 2) {
Mat K1 = calib.getCameraMat(c);
Mat K2 = calib.getCameraMat(c + 1);
Mat D1 = calib.getDistCoeffs(c);
Mat D2 = calib.getDistCoeffs(c + 1);
Mat P1, P2, Q;
Mat R1, R2;
Mat R_c1c2, T_c1c2;
calculateTransform(R[c], t[c], R[c + 1], t[c + 1], R_c1c2, T_c1c2);
cv::stereoRectify(K1, D1, K2, D2, params.size, R_c1c2, T_c1c2, R1, R2, P1, P2, Q, 0, params.alpha);
R_c1c2 = R_c1c2.clone();
T_c1c2 = T_c1c2.clone();
// calculate extrinsics from rectified parameters
Mat _t = Mat(Size(1, 3), CV_64FC1, Scalar(0.0));
//Rt_out[c] = getMat4x4(R[c], t[c]) * getMat4x4(R1, _t).inv();
//Rt_out[c + 1] = getMat4x4(R[c + 1], t[c + 1]) * getMat4x4(R2, _t).inv();
LOG(INFO) << K1;
LOG(INFO) << K2;
LOG(INFO) << R_c1c2;
LOG(INFO) << T_c1c2;
LOG(INFO) << "--------------------------------------------------------";
auto *nstream = nstreams[c/2];
while(true) {
try {
if (params.save_intrinsic && params.optimize_intrinsic) {
// never update distortion during extrinsic calibration
setIntrinsicsRPC(net, nstream, params.size, {K1, K2}, {Mat(0, 0, CV_64FC1), Mat(0, 0, CV_64FC1)});
if (params.save_extrinsic) {
setExtrinsicsRPC(net, nstream, R_c1c2, T_c1c2);
setPoseRPC(net, nstream, getMat4x4(R[c], t[c]));
}
if (params.save_intrinsic || params.save_extrinsic) {
saveCalibrationRPC(net, nstream);
LOG(INFO) << "CALIBRATION SENT";
}
} catch (std::exception &ex) {
std::this_thread::sleep_for(std::chrono::seconds(1));
cv::stereoRectify(K1, D1, K2, D2, params.size, R_c1c2, T_c1c2, R1, R2, P1, P2, Q, 0, 1.0, new_size, &roi[c], &roi[c + 1]);
//roi[c] = cv::Rect(0, 0, params.size.width, params.size.height);
//roi[c+1] = cv::Rect(0, 0, params.size.width, params.size.height);
cv::initUndistortRectifyMap(K1, D1, R1, P1, params.size, CV_16SC2, map1[c], map2[c]);
cv::initUndistortRectifyMap(K2, D2, R2, P2, params.size, CV_16SC2, map1[c + 1], map2[c + 1]);
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
std::vector<cv::Point2d> findCalibrationTarget(const cv::Mat &im, const cv::Mat &K) {
// check input type
std::vector<std::vector<cv::Point2f>> corners;
std::vector<int> ids;
auto params = cv::aruco::DetectorParameters::create();
params->cornerRefinementMinAccuracy = 0.01;
params->cornerRefinementMethod = cv::aruco::CORNER_REFINE_CONTOUR;
cv::aruco::detectMarkers(im, cv::aruco::getPredefinedDictionary(cv::aruco::DICT_5X5_100), corners, ids, params, cv::noArray(), K);
if (corners.size() > 2) { LOG(ERROR) << "Too many ArUco tags in image"; }
if (corners.size() != 2) { return {}; }
const size_t ncorners = 4;
const size_t ntags = ids.size();
std::vector<cv::Point2d> points;
if (ids[0] == 1) {
std::swap(ids[0], ids[1]);
std::swap(corners[0], corners[1]);
}
if (ids[0] != 0) {
LOG(ERROR) << "Tags ID0 and ID1 expected";
return {};
}
points.reserve(ntags*ncorners);
for (size_t i = 0; i < ntags; i++) {
for (size_t j = 0; j < ncorners; j++) {
points.push_back(corners[i][j]);
}
}
return points;
}
void runCameraCalibration( ftl::Configurable* root,
int n_views, int min_visible,
string path, string filename,
bool save_input,
CalibrationParams ¶ms)
{
Universe *net = ftl::create<Universe>(root, "net");
net->start();
net->waitConnections();
ftl::stream::Muxer *stream = ftl::create<ftl::stream::Muxer>(root, "muxstream");
ftl::stream::Receiver *gen = ftl::create<ftl::stream::Receiver>(root, "receiver");
gen->setStream(stream);
auto stream_uris = net->findAll<std::string>("list_streams");
std::vector<ftl::stream::Net*> nstreams;
int count = 0;
for (auto &s : stream_uris) {
LOG(INFO) << " --- found stream: " << s;
auto *nstream = ftl::create<ftl::stream::Net>(stream, std::to_string(count), net);
std::string name = *(nstream->get<std::string>("name"));
nstream->set("uri", s);
nstreams.push_back(nstream);
stream->add(nstream);
++count;
}
const size_t n_sources = nstreams.size();
const size_t n_cameras = n_sources * 2;
size_t reference_camera = 0;
std::mutex mutex;
std::atomic<bool> new_frames = false;
vector<Mat> rgb_(n_cameras), rgb_new(n_cameras);
vector<Mat> camera_parameters(n_cameras);
Size res;
gen->onFrameSet([stream, &mutex, &new_frames, &rgb_new, &camera_parameters, &res](ftl::rgbd::FrameSet &fs) {
stream->select(fs.id, Channel::Left + Channel::Right);
if (fs.frames.size() != (rgb_new.size()/2)) {
// nstreams.size() == (rgb_new.size()/2)
LOG(ERROR) << "frames.size() != nstreams.size(), "
<< fs.frames.size() << " != " << (rgb_new.size()/2);
}
for (size_t i = 0; i < fs.frames.size(); i ++) {
if (!fs.frames[i].hasChannel(Channel::Left)) {
good = false;
LOG(ERROR) << "No left channel";
break;
}
if (!fs.frames[i].hasChannel(Channel::Right)) {
good = false;
LOG(ERROR) << "No right channel";
break;
}
auto idx = stream->originStream(0, i);
fs.frames[i].download(Channel::Left+Channel::Right);
Mat &left = fs.frames[i].get<Mat>(Channel::Left);
Mat &right = fs.frames[i].get<Mat>(Channel::Right);
/*
// note: also returns empty sometimes
fs.frames[i].upload(Channel::Left+Channel::Right);
Mat left, right;
fs.frames[i].get<cv::cuda::GpuMat>(Channel::Left).download(left);
fs.frames[i].get<cv::cuda::GpuMat>(Channel::Right).download(right);
*/
CHECK(!left.empty() && !right.empty());
cv::cvtColor(left, rgb_new[2*idx], cv::COLOR_BGRA2BGR);
cv::cvtColor(right, rgb_new[2*idx+1], cv::COLOR_BGRA2BGR);
camera_parameters[2*idx] = ftl::calibration::scaleCameraMatrix(createCameraMatrix(fs.frames[i].getLeftCamera()),
rgb_new[2*idx].size(), Size(fs.frames[i].getLeftCamera().width, fs.frames[i].getLeftCamera().height));
camera_parameters[2*idx+1] = ftl::calibration::scaleCameraMatrix(createCameraMatrix(fs.frames[i].getRightCamera()),
rgb_new[2*idx].size(), Size(fs.frames[i].getRightCamera().width, fs.frames[i].getRightCamera().height));
if (res.empty()) res = rgb_new[2*idx].size();
catch (std::exception ex) {
LOG(ERROR) << "exception: " << ex.what();
good = false;
}
catch (...) {
LOG(ERROR) << "unknown exception";
}
new_frames = good;
return true;
});
stream->begin();
ftl::timer::start(false);
while(true) {
if (!res.empty()) {
params.size = res;
LOG(INFO) << "Camera resolution: " << params.size;
break;
std::this_thread::sleep_for(std::chrono::seconds(1));
bool res = true;
while(res) {
try { res = setRectifyRPC(net, nstream, false); }
catch (...) {}
if (res) {
LOG(ERROR) << "set_rectify() failed for " << *(nstream->get<string>("uri"));
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
else {
LOG(INFO) << "rectification disabled for " << *(nstream->get<string>("uri"));
}
// TODO: parameter for calibration target type
auto calib = MultiCameraCalibrationNew( n_cameras, reference_camera,
int iter = 0;
Mat show;
vector<int> visible;
vector<vector<Point2d>> points(n_cameras);
std::this_thread::sleep_for(std::chrono::seconds(3)); // rectification disabled, has some delay
while(calib.getMinVisibility() < n_views) {
while (true) {
if (new_frames) {
UNIQUE_LOCK(mutex, LOCK);
rgb.swap(rgb_new);
new_frames = false;
break;
}
cv::waitKey(10);
for (Mat &im : rgb) {
if (im.empty()) {
LOG(ERROR) << "EMPTY";
goto loop;
}
}
for (size_t i = 0; i < n_cameras; i++) {
auto new_points = findCalibrationTarget(rgb[i], camera_parameters[i]);
if (new_points.size() == 0) {
points[i] = vector(8, Point2d(0.0,0.0));
}
else {
points[i] = new_points;
visible[i] = 1;
n_found++;
}
}
if (n_found >= min_visible) {
calib.addPoints(points, visible);
if (save_input) {
for (size_t i = 0; i < n_cameras; i++) {
cv::imwrite(path + std::to_string(i) + "_" + std::to_string(iter) + ".jpg", rgb[i]);
}
}
iter++;
}
for (size_t i = 0; i < n_cameras; i++) {
if (visible[i]) {
for (int j = 0; j < points[i].size(); j++) {
cv::drawMarker( rgb[i], points[i][j], Scalar(42, 255, 42), cv::MARKER_CROSS, 25, 1);
cv::putText(rgb[i], std::to_string(j), Point2i(points[i][j]),
cv::FONT_HERSHEY_COMPLEX_SMALL, 1.0, Scalar(64, 64, 255), 1);
}
cv::putText(rgb[i],
"Camera " + std::to_string(i),
Point2i(10, 30),
cv::FONT_HERSHEY_COMPLEX_SMALL, 1.0, Scalar(64, 64, 255), 1);
// resolution
cv::putText(rgb[i],
"[" + std::to_string(rgb[i].size().width) + "x" + std::to_string(rgb[i].size().height) + "]",
Point2i(rgb[i].size().width-150, 30),
cv::FONT_HERSHEY_COMPLEX_SMALL, 1.0, Scalar(64, 64, 255), 1);
// uri
cv::putText(rgb[i],
stream_uris[i/2],
Point2i(10, rgb[i].rows-10),
cv::FONT_HERSHEY_COMPLEX_SMALL, 1.0, Scalar(64, 64, 255), 1);
cv::putText(rgb[i],
std::to_string(std::max(0, (int) (n_views - calib.getViewsCount(i)))),
cv::FONT_HERSHEY_COMPLEX_SMALL, 1.0, Scalar(64, 64, 255), 1);
}
stack(rgb, show);
cv::namedWindow("Cameras", cv::WINDOW_KEEPRATIO | cv::WINDOW_NORMAL);
cv::imshow("Cameras", show);
}
cv::destroyWindow("Cameras");
for (size_t i = 0; i < nstreams.size(); i++) {
while(true) {
try {
vector<Mat> D = getDistortionParametersRPC(net, nstreams[i]);
LOG(INFO) << "K[" << 2*i << "] = \n" << camera_parameters[2*i];
LOG(INFO) << "D[" << 2*i << "] = " << D[0];
LOG(INFO) << "K[" << 2*i+1 << "] = \n" << camera_parameters[2*i+1];
LOG(INFO) << "D[" << 2*i+1 << "] = " << D[1];
calib.setCameraParameters(2*i, camera_parameters[2*i], D[0]);
calib.setCameraParameters(2*i+1, camera_parameters[2*i+1], D[1]);
break;
}
catch (...) {}
}
Mat out;
vector<Mat> map1, map2;
vector<cv::Rect> roi;
calibrateRPC(net, calib, params, nstreams, map1, map2, roi);
if (save_input) {
cv::FileStorage fs(path + filename, cv::FileStorage::WRITE);
calib.saveInput(fs);
fs.release();
}
while(cv::waitKey(10) != 27) {
while (!new_frames) {
if (cv::waitKey(50) != -1) { ftl::running = false; }
}
{
UNIQUE_LOCK(mutex, LOCK)
rgb.swap(rgb_new);
new_frames = false;
}
visualizeCalibration(calib, out, rgb, map1, map2, roi);
cv::namedWindow("Calibration", cv::WINDOW_KEEPRATIO | cv::WINDOW_NORMAL);
cv::imshow("Calibration", out);
}
for (size_t i = 0; i < nstreams.size(); i++) {
while(true) {
try {
setRectifyRPC(net, nstreams[i], true);
break;
}
catch (...) {}
}
}
ftl::running = false;
ftl::timer::stop();
ftl::pool.stop(true);
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
void runCameraCalibrationPath( ftl::Configurable* root,
string path, string filename,
CalibrationParams ¶ms)
{
Universe *net = ftl::create<Universe>(root, "net");
ftl::ctrl::Master ctrl(root, net);
net->start();
net->waitConnections();
ftl::stream::Muxer *stream = ftl::create<ftl::stream::Muxer>(root, "muxstream");
ftl::stream::Receiver *gen = ftl::create<ftl::stream::Receiver>(root, "receiver");
gen->setStream(stream);
auto stream_uris = net->findAll<std::string>("list_streams");
std::sort(stream_uris.begin(), stream_uris.end());
std::vector<ftl::stream::Net*> nstreams;
int count = 0;
for (auto &s : stream_uris) {
LOG(INFO) << " --- found stream: " << s;
auto *nstream = ftl::create<ftl::stream::Net>(stream, std::to_string(count), net);
std::string name = *(nstream->get<std::string>("name"));
nstream->set("uri", s);
nstreams.push_back(nstream);
stream->add(nstream);
++count;
}
const size_t n_sources = nstreams.size();
const size_t n_cameras = n_sources * 2;
size_t reference_camera = 0;
std::mutex mutex;
std::atomic<bool> new_frames = false;
vector<Mat> rgb_(n_cameras), rgb_new(n_cameras);
vector<Mat> camera_parameters(n_cameras);
Size res;
gen->onFrameSet([stream, &mutex, &new_frames, &rgb_new, &camera_parameters, &res](ftl::rgbd::FrameSet &fs) {
stream->select(fs.id, Channel::Left + Channel::Right);
if (fs.frames.size() != (rgb_new.size()/2)) {
// nstreams.size() == (rgb_new.size()/2)
LOG(ERROR) << "frames.size() != nstreams.size(), "
<< fs.frames.size() << " != " << (rgb_new.size()/2);
}
UNIQUE_LOCK(mutex, CALLBACK);
bool good = true;
try {
for (size_t i = 0; i < fs.frames.size(); i ++) {
if (!fs.frames[i].hasChannel(Channel::Left)) {
good = false;
LOG(ERROR) << "No left channel";
break;
}
if (!fs.frames[i].hasChannel(Channel::Right)) {
good = false;
LOG(ERROR) << "No right channel";
break;
}
auto idx = stream->originStream(0, i);
CHECK(idx >= 0) << "negative index";
fs.frames[i].download(Channel::Left+Channel::Right);
Mat &left = fs.frames[i].get<Mat>(Channel::Left);
Mat &right = fs.frames[i].get<Mat>(Channel::Right);
CHECK(!left.empty() && !right.empty());
cv::cvtColor(left, rgb_new[2*idx], cv::COLOR_BGRA2BGR);
cv::cvtColor(right, rgb_new[2*idx+1], cv::COLOR_BGRA2BGR);
/*
camera_parameters[2*idx] = ftl::calibration::scaleCameraMatrix(createCameraMatrix(fs.frames[i].getLeftCamera()),
rgb_new[2*idx].size(), Size(fs.frames[i].getLeftCamera().width, fs.frames[i].getLeftCamera().height));
camera_parameters[2*idx+1] = ftl::calibration::scaleCameraMatrix(createCameraMatrix(fs.frames[i].getRightCamera()),
rgb_new[2*idx].size(), Size(fs.frames[i].getRightCamera().width, fs.frames[i].getRightCamera().height));
if (res.empty()) res = rgb_new[2*idx].size();*/
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
}
}
catch (std::exception ex) {
LOG(ERROR) << "exception: " << ex.what();
good = false;
}
catch (...) {
LOG(ERROR) << "unknown exception";
good = false;
}
new_frames = good;
return true;
});
stream->begin();
ftl::timer::start(false);
for (auto *nstream: nstreams) {
bool res = true;
while(res) {
try { res = setRectifyRPC(net, nstream, false); }
catch (...) {}
if (res) {
LOG(ERROR) << "set_rectify() failed for " << *(nstream->get<string>("uri"));
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
else {
LOG(INFO) << "rectification disabled for " << *(nstream->get<string>("uri"));
}
}
}
// TODO: parameter for calibration target type
auto calib = MultiCameraCalibrationNew( n_cameras, reference_camera,
params.size, CalibrationTarget(0.150)
);
calib.object_points_ = calibration_target;
cv::FileStorage fs(path + filename, cv::FileStorage::READ);
fs["resolution"] >> params.size;
params.idx_cameras.resize(n_cameras);
std::iota(params.idx_cameras.begin(), params.idx_cameras.end(), 0);
calib.loadInput(path + filename, params.idx_cameras);
/*for (size_t i = 0; i < nstreams.size(); i++) {
while(true) {
try {
if (camera_parameters[2*i].empty() || camera_parameters[2*i+1].empty()) {
std::this_thread::sleep_for(std::chrono::seconds(1));
continue;
}
vector<Mat> D = getDistortionParametersRPC(net, nstreams[i]);
LOG(INFO) << "K[" << 2*i << "] = \n" << camera_parameters[2*i];
LOG(INFO) << "D[" << 2*i << "] = " << D[0];
LOG(INFO) << "K[" << 2*i+1 << "] = \n" << camera_parameters[2*i+1];
LOG(INFO) << "D[" << 2*i+1 << "] = " << D[1];
calib.setCameraParameters(2*i, camera_parameters[2*i], D[0]);
calib.setCameraParameters(2*i+1, camera_parameters[2*i+1], D[1]);
break;
}
catch (...) {}
}
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
Mat out;
vector<Mat> map1, map2;
vector<cv::Rect> roi;
vector<size_t> idx;
calibrateRPC(net, calib, params, nstreams, map1, map2, roi);
vector<Mat> rgb(n_cameras);
// visualize
while(cv::waitKey(10) != 27) {
while (!new_frames) {
if (cv::waitKey(50) != -1) { ftl::running = false; }
}
{
UNIQUE_LOCK(mutex, LOCK)
rgb.swap(rgb_new);
new_frames = false;
}
visualizeCalibration(calib, out, rgb, map1, map2, roi);
cv::namedWindow("Calibration", cv::WINDOW_KEEPRATIO | cv::WINDOW_NORMAL);
cv::imshow("Calibration", out);
}
for (size_t i = 0; i < nstreams.size(); i++) {
while(true) {
try {
setRectifyRPC(net, nstreams[i], true);
break;
}
catch (...) {}
}
}
ftl::running = false;
ftl::timer::stop();
ftl::pool.stop(true);
}
int main(int argc, char **argv) {
auto options = ftl::config::read_options(&argv, &argc);
auto root = ftl::configure(argc, argv, "registration_default");
// run calibration from saved input?
const bool load_input = root->value<bool>("load_input", false);
// should calibration input be saved
const bool save_input = root->value<bool>("save_input", false);
// should extrinsic calibration be saved (only used with load_input)
const bool save_extrinsic = root->value<bool>("save_extrinsic", true);
// should intrinsic calibration be saved
const bool save_intrinsic = root->value<bool>("save_intrinsic", false);
const bool optimize_intrinsic = root->value<bool>("optimize_intrinsic", false);
// directory where calibration data and images are saved, if save_input enabled
const string calibration_data_dir = root->value<string>("calibration_data_dir", "./");
// file to save calibration input (2d points and visibility)
const string calibration_data_file = root->value<string>("calibration_data_file", "data.yml");
// in how many cameras should the pattern be visible
const int min_visible = root->value<int>("min_visible", 3);
// minimum for how many times pattern is seen per camera
const int n_views = root->value<int>("n_views", 500);
// reference camera, -1 for automatic
const int ref_camera = root->value<int>("reference_camera", -1);
// registration file path
const string registration_file = root->value<string>("registration_file", FTL_LOCAL_CONFIG_ROOT "/registration.json");
// location where extrinsic calibration files saved
const string output_directory = root->value<string>("output_directory", "./");
const bool offline = root->value<bool>("offline", false);
CalibrationParams params;
params.save_extrinsic = save_extrinsic;
params.save_intrinsic = save_intrinsic;
params.optimize_intrinsic = optimize_intrinsic;
params.output_path = output_directory;
params.registration_file = registration_file;
params.reference_camera = ref_camera;
LOG(INFO) << "\n"
<< "\n"
<< "\n save_input: " << (int) save_input
// << "\n load_input: " << (int) load_input
// << "\n save_extrinsic: " << (int) save_extrinsic
// << "\n save_intrinsic: " << (int) save_intrinsic
<< "\n optimize_intrinsic: " << (int) optimize_intrinsic
// << "\n calibration_data_dir: " << calibration_data_dir
// << "\n calibration_data_file: " << calibration_data_file
<< "\n min_visible: " << min_visible
<< "\n n_views: " << n_views
<< "\n reference_camera: " << ref_camera << (ref_camera != -1 ? "" : " (automatic)")
// << "\n registration_file: " << registration_file
// << "\n output_directory: " << output_directory
<< "\n";
if (load_input) {
runCameraCalibrationPath(root, calibration_data_dir, calibration_data_file, params);
runCameraCalibration(root, n_views, min_visible, calibration_data_dir, calibration_data_file, save_input, params);
}
return 0;
}