From 2aed08ff83bfc3e43a778e5a6f62994f5e82179a Mon Sep 17 00:00:00 2001 From: Sebastian Hahta <joseha@utu.fi> Date: Thu, 6 Aug 2020 12:33:14 +0300 Subject: [PATCH] extrinsic can be freezed per camera --- .../src/modules/calibration/extrinsic.cpp | 8 ++- .../src/views/calibration/extrinsicview.cpp | 7 +- .../include/ftl/calibration/extrinsic.hpp | 15 +++-- components/calibration/src/extrinsic.cpp | 66 +++++++++---------- 4 files changed, 52 insertions(+), 44 deletions(-) diff --git a/applications/gui2/src/modules/calibration/extrinsic.cpp b/applications/gui2/src/modules/calibration/extrinsic.cpp index bf7c892f3..93da1494d 100644 --- a/applications/gui2/src/modules/calibration/extrinsic.cpp +++ b/applications/gui2/src/modules/calibration/extrinsic.cpp @@ -370,12 +370,14 @@ void ExtrinsicCalibration::run() { for (int c = 0; c < cameraCount(); c += 2) { auto l = state_.calib.calibrationOptimized(c); auto r = state_.calib.calibrationOptimized(c + 1); - LOG(INFO) << c << ": rvec " << l.extrinsic.rvec << "; tvec " << l.extrinsic.tvec; - LOG(INFO) << c + 1 << ": rvec " << r.extrinsic.rvec << "; tvec " << r.extrinsic.tvec; stereoRectify(c, c + 1, l, r); + /*LOG(INFO) << c << ": rvec " << l.extrinsic.rvec + << "; tvec " << l.extrinsic.tvec; + LOG(INFO) << c + 1 << ": rvec " << r.extrinsic.rvec + << "; tvec " << r.extrinsic.tvec; LOG(INFO) << "baseline (" << c << ", " << c + 1 << "): " - << cv::norm(l.extrinsic.tvec, r.extrinsic.tvec); + << cv::norm(l.extrinsic.tvec, r.extrinsic.tvec);*/ } } catch (ftl::exception &ex) { diff --git a/applications/gui2/src/views/calibration/extrinsicview.cpp b/applications/gui2/src/views/calibration/extrinsicview.cpp index c4071ccf1..c79d52e9b 100644 --- a/applications/gui2/src/views/calibration/extrinsicview.cpp +++ b/applications/gui2/src/views/calibration/extrinsicview.cpp @@ -405,6 +405,7 @@ void ExtrinsicCalibrationView::CalibrationWindow::build() { } //////////////////////////////////////////////////////////////////////////// + // TODO: selecting camera should also enable use existing above for same c new nanogui::Label(wfreeze, "Fix extrinsics for cameras: "); auto* fix_extrinsics = new nanogui::Widget(wfreeze); @@ -586,7 +587,7 @@ private: std::set<int> rows_; std::map<int, nanogui::Color> colors_; - int n_colors_ = 16; + int n_colors_ = 8; float alpha_threshold_ = 2.0f; public: @@ -645,14 +646,14 @@ void StereoCalibrationImageView::draw(NVGcontext* ctx) { nvgStrokeWidth(ctx, swidth); nvgStroke(ctx); - if (swidth*0.5f > alpha_threshold_) { + /*if (swidth*0.5f > alpha_threshold_) { nvgBeginPath(ctx); nvgMoveTo(ctx, x, p.y() - swidth*0.5f); nvgLineTo(ctx, x + w, p.y() - swidth*0.5f); nvgStrokeColor(ctx, nvgRGBA(0, 0, 0, 196)); nvgStrokeWidth(ctx, 1.0f); nvgStroke(ctx); - } + }*/ nvgResetScissor(ctx); y_im += h; } diff --git a/components/calibration/include/ftl/calibration/extrinsic.hpp b/components/calibration/include/ftl/calibration/extrinsic.hpp index ef2e48595..645674ddb 100644 --- a/components/calibration/include/ftl/calibration/extrinsic.hpp +++ b/components/calibration/include/ftl/calibration/extrinsic.hpp @@ -206,19 +206,19 @@ public: MSGPACK_DEFINE(points_, mask_, pairs_, calib_, is_calibrated_); protected: - /** Calculate initial pose and triangulate points **/ + /** Calculate initial pose and triangulate points for two cameras **/ void calculatePairPose(unsigned int c1, unsigned int c2); /** Only triangulate points using existing calibration */ void triangulate(unsigned int c1, unsigned int c2); - /** Initial pairwise calibration and triangulation. */ + /** (1) Initial pairwise calibration and triangulation. */ void calculatePairPoses(); - /** Calculate initial poses from pairs for non calibrated cameras */ + /** (2) Calculate initial poses from pairs for non calibrated cameras */ void calculateInitialPoses(); - /** Bundle adjustment on initial poses and triangulations. */ + /** (3) Bundle adjustment on initial poses and triangulations. */ double optimize(); /** Select optimal camera for chains. Optimal camera has most visibility and @@ -241,7 +241,12 @@ private: // skipped (and points are directly triangulated) std::vector<bool> is_calibrated_; - int min_points_ = 64; // minimum number of points required for pair calibration + // prune points which have higher reprojection error than given threshold + // and re-run bundle adjustment. (might be able to remove some problematic + // observations from extreme angles etc.) + std::vector<double> prune_observations_ = {2.5, 2.0, 2.0, 1.0}; + + int min_obs_ = 64; // minimum number of observations required for pair calibration // TODO: add map {c1,c2} for existing calibration which is used if available. // std::shared_ptr<std::string> status_; diff --git a/components/calibration/src/extrinsic.cpp b/components/calibration/src/extrinsic.cpp index 2857527b3..ea575608b 100644 --- a/components/calibration/src/extrinsic.cpp +++ b/components/calibration/src/extrinsic.cpp @@ -358,14 +358,6 @@ double calibratePair(const cv::Mat &K1, const cv::Mat &D1, // ==== Extrinsic Calibration ================================================== -/*unsigned int ExtrinsicCalibration::addCamera(const CalibrationData::Intrinsic &c) { - unsigned int idx = calib_.size(); - calib_.push_back({c, {}}); - calib_optimized_.push_back(calib_.back()); - is_calibrated_.push_back(false); - return idx; -}*/ - unsigned int ExtrinsicCalibration::addCamera(const CalibrationData::Calibration &c) { unsigned int idx = calib_.size(); calib_.push_back(c); @@ -374,18 +366,6 @@ unsigned int ExtrinsicCalibration::addCamera(const CalibrationData::Calibration return idx; } -/*unsigned int ExtrinsicCalibration::addStereoCamera(const CalibrationData::Intrinsic &c1, const CalibrationData::Intrinsic &c2) { - unsigned int idx = calib_.size(); - calib_.push_back({c1, {}}); - calib_optimized_.push_back(calib_.back()); - calib_.push_back({c2, {}}); - calib_optimized_.push_back(calib_.back()); - is_calibrated_.push_back(false); - is_calibrated_.push_back(false); - mask_.insert({idx, idx + 1}); - return idx; -}*/ - unsigned int ExtrinsicCalibration::addStereoCamera(const CalibrationData::Calibration &c1, const CalibrationData::Calibration &c2) { unsigned int idx = calib_.size(); calib_.push_back({c1.intrinsic, c1.extrinsic}); @@ -515,9 +495,9 @@ void ExtrinsicCalibration::calculatePairPoses() { } // require minimum number of visible points - if (points().visibility().count(c1, c2) < min_points_) { + if (points().visibility().count(c1, c2) < min_obs_) { LOG(WARNING) << "skipped pair (" << c1 << ", " << c2 << "), not enough points"; - return; + continue; } if (is_calibrated_[c1] && is_calibrated_[c2]) { @@ -552,9 +532,7 @@ void ExtrinsicCalibration::calculateInitialPoses() { // mask stereo cameras (do not pairwise calibrate a stereo pair; unreliable) auto visibility = points_.visibility(); - for (const auto& m: mask_) { - visibility.mask(m.first, m.second); - } + for (const auto& [c1, c2]: mask_) { visibility.mask(c1, c2); } // mask cameras which did not have enough points TODO: triangulation later // would still be useful (calculate initial poses, then triangulate) @@ -565,7 +543,9 @@ void ExtrinsicCalibration::calculateInitialPoses() { } }} - // select optimal camera to calculate chains to + // select optimal camera to calculate chains to. TODO: if any of the + // cameras is already calibrated, use most visible calibrated camera as + // target camera. auto c_ref = selectOptimalCamera(); auto paths = visibility.shortestPath(c_ref); @@ -637,6 +617,18 @@ static cv::Point3d absdiff(const std::vector<double> &xs, const std::vector<doub double ExtrinsicCalibration::optimize() { + // triangulate points for stereo pairs (all points triangulated after this) + updateStatus_("Triangulating remaining points"); + for (const auto& [c1, c2]: mask_) { + if (points().visibility().count(c1, c2) >= min_obs_) { + triangulate(c1, c2); + } + else { + LOG(INFO) << "Skipping triangulation for pair " << c1 << ", " << c2; + } + } + + // Build BA BundleAdjustment ba; std::vector<Camera> cameras; std::vector<cv::Mat> T; // camera to world @@ -660,7 +652,6 @@ double ExtrinsicCalibration::optimize() { // triangulated multiple times: use median values. Note T[] contains // inverse transformations, as points are transformed from camera to world // (instead the other way around by parameters in cameras[]). - updateStatus_("Calculating points in world coordinates"); // NOTE: above CalibrationPoints datastructure not optimal regarding how @@ -724,7 +715,7 @@ double ExtrinsicCalibration::optimize() { // TODO: desgin better check if (cv::norm(absdiff(px, py, pz)) > threshold_bad_) { n_points_bad++; - continue; + //continue; } ba.addPoint(vis, obs, p); @@ -740,7 +731,7 @@ double ExtrinsicCalibration::optimize() { } if (float(n_points_missing)/float(n_points - n_points_bad) > threhsold_warning_) { - // low number of points; most points only visible in pairs? + // this should not happen any more (all points should be triangulated). LOG(WARNING) << "Large number of points skipped. Are there enough " "visible points between stereo camera pairs?"; } @@ -755,10 +746,19 @@ double ExtrinsicCalibration::optimize() { LOG(INFO) << "fix distortion: " << (options_.fix_distortion ? "yes" : "no"); ba.run(options_); - LOG(INFO) << "removed points: " << ba.removeObservations(2.0); - ba.run(options_); - LOG(INFO) << "removed points: " << ba.removeObservations(1.0); - ba.run(options_); + + int n_removed = 0; + for (const auto& t : prune_observations_) { + n_removed += ba.removeObservations(t); + if (float(n_removed)/float(n_points) > threhsold_warning_) { + LOG(WARNING) << "significant number of observations removed"; + break; + } + else { + LOG(INFO) << "removed observations: " << n_removed; + ba.run(options_); + } + } calib_optimized_.resize(calib_.size()); rmse_.resize(calib_.size()); -- GitLab