diff --git a/applications/gui2/src/modules/calibration/extrinsic.cpp b/applications/gui2/src/modules/calibration/extrinsic.cpp index b0ced5bb10af804b3c3a1f999c4fa344bf2a69d6..c1e4c07e3764bcfe62f9eadf50952b81f0cc7c8d 100644 --- a/applications/gui2/src/modules/calibration/extrinsic.cpp +++ b/applications/gui2/src/modules/calibration/extrinsic.cpp @@ -56,6 +56,8 @@ void ExtrinsicCalibration::start(unsigned int fsid, std::vector<FrameID> sources reset(); state_.cameras.reserve(sources.size()*2); + state_.maps1.resize(sources.size()*2); + state_.maps2.resize(sources.size()*2); auto* filter = io->feed()->filter (std::unordered_set<uint32_t>{fsid}, {Channel::Left, Channel::Right}); @@ -75,11 +77,17 @@ void ExtrinsicCalibration::start(unsigned int fsid, std::vector<FrameID> sources auto sz = cv::Size((int) frame.getLeftCamera().width, (int) frame.getLeftCamera().height); state_.cameras.push_back(cl); state_.cameras.push_back(cr); + auto calibl = getCalibration(cl); + calibl.intrinsic = CalibrationData::Intrinsic(calibl.intrinsic, sz); + auto calibr = getCalibration(cr); + calibr.intrinsic = CalibrationData::Intrinsic(calibr.intrinsic, sz); // Scale intrinsics - state_.calib.addStereoCamera( - CalibrationData::Intrinsic(getCalibration(cl).intrinsic, sz), - CalibrationData::Intrinsic(getCalibration(cr).intrinsic, sz)); + state_.calib.addStereoCamera(calibl.intrinsic, calibr.intrinsic); + + // Update rectification + unsigned int idx = state_.cameras.size() - 2; + stereoRectify(idx, idx + 1, calibl, calibr); } // initialize last points structure; can't be resized while running (without @@ -298,6 +306,8 @@ void ExtrinsicCalibration::stereoRectify(int cl, int cr, CHECK_NE(l.extrinsic.tvec, r.extrinsic.tvec); CHECK_EQ(l.intrinsic.resolution, r.intrinsic.resolution); + CHECK_LT(cr, state_.maps1.size()); + CHECK_LT(cr, state_.maps2.size()); auto size = l.intrinsic.resolution; cv::Mat T = r.extrinsic.matrix() * inverse(l.extrinsic.matrix()); diff --git a/applications/gui2/src/views/calibration/extrinsicview.cpp b/applications/gui2/src/views/calibration/extrinsicview.cpp index c637ab249563d615984f2ab60f8c43862df5f65a..863d26ef97cd98c3d2c756c4392bd22cc3f53dd5 100644 --- a/applications/gui2/src/views/calibration/extrinsicview.cpp +++ b/applications/gui2/src/views/calibration/extrinsicview.cpp @@ -481,7 +481,7 @@ void ExtrinsicCalibrationView::ResultsWindow::draw(NVGcontext* ctx) { //////////////////////////////////////////////////////////////////////////////// -static void drawText(NVGcontext* ctx, nanogui::Vector2f &pos, const std::string& text, +static void drawText(NVGcontext* ctx, const nanogui::Vector2f &pos, const std::string& text, float size=12.0f, int align=NVG_ALIGN_MIDDLE|NVG_ALIGN_CENTER) { nvgFontSize(ctx, size); nvgFontFace(ctx, "sans-bold"); @@ -497,63 +497,88 @@ static void drawText(NVGcontext* ctx, nanogui::Vector2f &pos, const std::string class StereoCalibrationImageView : public ftl::gui2::StereoImageView { public: using ftl::gui2::StereoImageView::StereoImageView; + + virtual bool keyboardCharacterEvent(unsigned int codepoint) override; virtual bool mouseButtonEvent(const nanogui::Vector2i &p, int button, bool down, int modifiers) override; virtual void draw(NVGcontext* ctx) override; + + void reset(); + private: - bool draw_line_ = false; - float row_image_ = 0; + std::set<int> rows_; + std::map<int, nanogui::Color> colors_; + + int n_colors_ = 16; + float alpha_threshold_ = 2.0f; public: EIGEN_MAKE_ALIGNED_OPERATOR_NEW; }; +void StereoCalibrationImageView::reset() { + rows_.clear(); +} + +bool StereoCalibrationImageView::keyboardCharacterEvent(unsigned int codepoint) { + if (codepoint == 'r') { + reset(); + return true; + } + return StereoImageView::keyboardCharacterEvent(codepoint); +} + bool StereoCalibrationImageView::mouseButtonEvent(const nanogui::Vector2i &p, int button, bool down, int modifiers) { + nanogui::Widget::mouseButtonEvent(p, button, down, modifiers); if (button == GLFW_MOUSE_BUTTON_1 && !down) { - float row = round(imageCoordinateAt(p.cast<float>()).y()); - if (row == row_image_ && draw_line_) { - draw_line_ = false; - } - else { - draw_line_ = true; - row_image_ = row; - } + // half a pixel offset to match "square pixel" visualization + nanogui::Vector2f offset{left()->scale()/2.0f, left()->scale()/2.0f}; + float row = round(imageCoordinateAt(p.cast<float>() + offset).y()); + + if (rows_.count(row)) { rows_.erase(row); } + else { rows_.insert(row); } } return true; } void StereoCalibrationImageView::draw(NVGcontext* ctx) { StereoImageView::draw(ctx); - if (!draw_line_) { return; } - - // assumes vertical alignment (horizontal not tested) + // assumes vertical alignment (horizontal not implemented) CHECK(orientation() == nanogui::Orientation::Vertical); + int x = position().x(); int y = position().y(); int w = width(); int h = left()->height(); - nanogui::Vector2f l = left()->positionForCoordinate({0.0f, row_image_}) + left()->position().cast<float>(); - nanogui::Vector2f r = right()->positionForCoordinate({0.0f, row_image_}) + right()->position().cast<float>(); float swidth = std::max(1.0f, left()->scale()); + int c = 0; // color - for (auto& p : {l, r}) { - nvgScissor(ctx, x, y, w, h); - nvgBeginPath(ctx); - nvgMoveTo(ctx, x, p.y() - swidth*0.5f); // "half a pixel" shift - nvgLineTo(ctx, x + w, p.y() - swidth*0.5f); - nvgStrokeColor(ctx, nvgRGBA(255, 32, 32, (swidth == 1.0f) ? 255 : 96)); - nvgStrokeWidth(ctx, swidth); - nvgStroke(ctx); + for (int row : rows_) { + int y_im = y; + nanogui::Vector2f l = left()->positionForCoordinate({0.0f, row}) + left()->position().cast<float>(); + nanogui::Vector2f r = right()->positionForCoordinate({0.0f, row}) + right()->position().cast<float>(); + auto color = nvgHSLA(float(c%n_colors_)/float(n_colors_), 0.9, 0.5, (swidth < alpha_threshold_) ? 255 : 96); - if (swidth*0.5f > 2.0f) { + for (auto& p : {l, r}) { + nvgScissor(ctx, x, y_im, w, h); 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); + nvgStrokeColor(ctx, color); + nvgStrokeWidth(ctx, swidth); nvgStroke(ctx); + + 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; } - nvgResetScissor(ctx); - y += h; + c++; } } @@ -672,6 +697,8 @@ void ExtrinsicCalibrationView::draw(NVGcontext* ctx) { drawText(ctx, paths[p], std::to_string(p), 14.0f); } }*/ + + // TODO: move to stereocalibrateimageview nanogui::Vector2f tpos = wpos + nanogui::Vector2f{10.0f, 10.0f}; drawText(ctx, tpos, std::to_string(ctrl_->getFrameCount(i)), 20.0f, NVG_ALIGN_TOP|NVG_ALIGN_LEFT); @@ -680,6 +707,20 @@ void ExtrinsicCalibrationView::draw(NVGcontext* ctx) { nvgResetScissor(ctx); } + + { + float h = 14.0f; + for (const auto& text : {"Left click: draw line", + "Right click: pan", + "Scroll: zoom", + "C center", + "F fit", + "R clear lines" + }) { + drawText(ctx, {float(width()) - 60.0, h}, text, 14, NVGalign::NVG_ALIGN_BOTTOM | NVG_ALIGN_MIDDLE); + h += 20.0; + } + } } ExtrinsicCalibrationView::~ExtrinsicCalibrationView() {