From a37f09edaa6829736b62e5b2aa5c42fe669ed0fc Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 5 Dec 2022 14:50:25 +0100 Subject: [PATCH] Measurement: optimize feature extraction: - do not call ransac when not needed - decrease number of ransac iterations - precalculate inverse of transformation matrix - pass the min error from ransac algorithm, do not calculate it again --- src/libslic3r/Geometry/Circle.cpp | 4 +- src/libslic3r/Geometry/Circle.hpp | 2 +- src/libslic3r/Measure.cpp | 83 ++++++++++-------------- src/libslic3r/Measure.hpp | 5 +- src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp | 1 - 5 files changed, 40 insertions(+), 55 deletions(-) diff --git a/src/libslic3r/Geometry/Circle.cpp b/src/libslic3r/Geometry/Circle.cpp index 4d7c38ccc..679667195 100644 --- a/src/libslic3r/Geometry/Circle.cpp +++ b/src/libslic3r/Geometry/Circle.cpp @@ -108,7 +108,7 @@ Circled circle_taubin_newton(const Vec2ds& input, size_t cycles) return out; } -Circled circle_ransac(const Vec2ds& input, size_t iterations) +Circled circle_ransac(const Vec2ds& input, size_t iterations, double* min_error) { if (input.size() < 3) return Circled::make_invalid(); @@ -132,6 +132,8 @@ Circled circle_ransac(const Vec2ds& input, size_t iterations) circle_best = c; } } + if (min_error) + *min_error = err_min; return circle_best; } diff --git a/src/libslic3r/Geometry/Circle.hpp b/src/libslic3r/Geometry/Circle.hpp index 39973d916..653102e2a 100644 --- a/src/libslic3r/Geometry/Circle.hpp +++ b/src/libslic3r/Geometry/Circle.hpp @@ -102,7 +102,7 @@ inline Vec2d circle_center_taubin_newton(const Vec2ds& input, size_t cycles = 20 Circled circle_taubin_newton(const Vec2ds& input, size_t cycles = 20); // Find circle using RANSAC randomized algorithm. -Circled circle_ransac(const Vec2ds& input, size_t iterations = 20); +Circled circle_ransac(const Vec2ds& input, size_t iterations = 20, double* min_error = nullptr); // Randomized algorithm by Emo Welzl, working with squared radii for efficiency. The returned circle radius is inflated by epsilon. template diff --git a/src/libslic3r/Measure.cpp b/src/libslic3r/Measure.cpp index 89af1cf82..747da622c 100644 --- a/src/libslic3r/Measure.cpp +++ b/src/libslic3r/Measure.cpp @@ -14,7 +14,7 @@ namespace Measure { constexpr double feature_hover_limit = 0.5; // how close to a feature the mouse must be to highlight it -static std::pair get_center_and_radius(const std::vector& points, const Transform3d& trafo) +static std::tuple get_center_and_radius(const std::vector& points, const Transform3d& trafo, const Transform3d& trafo_inv) { Vec2ds out; double z = 0.; @@ -24,18 +24,17 @@ static std::pair get_center_and_radius(const std::vector& out.emplace_back(pt_transformed.x(), pt_transformed.y()); } - auto circle = Geometry::circle_ransac(out, 20); // FIXME: iterations? + const int iter = points.size() < 10 ? 2 : + points.size() < 100 ? 4 : + 6; - return std::make_pair(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius); + double error = std::numeric_limits::max(); + auto circle = Geometry::circle_ransac(out, iter, &error); + + return std::make_tuple(trafo.inverse() * Vec3d(circle.center.x(), circle.center.y(), z), circle.radius, error); } -static bool circle_fit_is_ok(const std::vector& pts, const Vec3d& center, double radius) -{ - for (const Vec3d& pt : pts) - if (std::abs((pt - center).norm() - radius) > 0.05) - return false; - return true; -} + static std::array orthonormal_basis(const Vec3d& v) { @@ -66,7 +65,6 @@ public: float area; }; - std::vector get_all_features() const; std::optional get_feature(size_t face_idx, const Vec3d& point) const; int get_num_of_planes() const; const std::vector& get_plane_triangle_indices(int idx) const; @@ -267,7 +265,8 @@ void MeasuringImpl::extract_features() Eigen::Quaterniond q; q.setFromTwoVectors(plane.normal, Vec3d::UnitZ()); Transform3d trafo = Transform3d::Identity(); - trafo.rotate(q); + trafo.rotate(q); + const Transform3d trafo_inv = trafo.inverse(); for (const std::vector& border : plane.borders) { if (border.size() <= 1) @@ -275,28 +274,31 @@ void MeasuringImpl::extract_features() bool done = false; - if (const auto& [center, radius] = get_center_and_radius(border, trafo); - (border.size()>4) && circle_fit_is_ok(border, center, radius)) { - // The whole border is one circle. Just add it into the list of features - // and we are done. + if (border.size() > 4) { + const auto& [center, radius, err] = get_center_and_radius(border, trafo, trafo_inv); - bool is_polygon = border.size()>4 && border.size()<=8; - bool lengths_match = std::all_of(border.begin()+2, border.end(), [is_polygon](const Vec3d& pt) { - return Slic3r::is_approx((pt - *((&pt)-1)).squaredNorm(), (*((&pt)-1) - *((&pt)-2)).squaredNorm(), is_polygon ? 0.01 : 0.01); - }); + if (err < 0.05) { + // The whole border is one circle. Just add it into the list of features + // and we are done. - if (lengths_match && (is_polygon || border.size() > 8)) { - if (is_polygon) { - // This is a polygon, add the separate edges with the center. - for (int j=0; j4 && border.size()<=8; + bool lengths_match = std::all_of(border.begin()+2, border.end(), [is_polygon](const Vec3d& pt) { + return Slic3r::is_approx((pt - *((&pt)-1)).squaredNorm(), (*((&pt)-1) - *((&pt)-2)).squaredNorm(), is_polygon ? 0.01 : 0.01); + }); + + if (lengths_match && (is_polygon || border.size() > 8)) { + if (is_polygon) { + // This is a polygon, add the separate edges with the center. + for (int j=0; j 0.9*M_PI/2.; @@ -485,16 +487,6 @@ void MeasuringImpl::extract_features() -std::vector MeasuringImpl::get_all_features() const -{ - std::vector features; - //PlaneData& plane = m_planes[0]; - for (const PlaneData& plane : m_planes) - for (const SurfaceFeature& feature : plane.surface_features) - features.emplace_back(feature); - return features; -} - @@ -596,11 +588,6 @@ Measuring::Measuring(const indexed_triangle_set& its) Measuring::~Measuring() {} -std::vector Measuring::get_all_features() const -{ - return priv->get_all_features(); -} - std::optional Measuring::get_feature(size_t face_idx, const Vec3d& point) const { diff --git a/src/libslic3r/Measure.hpp b/src/libslic3r/Measure.hpp index b1d2e611f..a273135ca 100644 --- a/src/libslic3r/Measure.hpp +++ b/src/libslic3r/Measure.hpp @@ -93,10 +93,7 @@ public: // Construct the measurement object on a given its. explicit Measuring(const indexed_triangle_set& its); ~Measuring(); - - // Return a reference to a list of all features identified on the its. - // Use only for debugging. Expensive, do not call often. - std::vector get_all_features() const; + // Given a face_idx where the mouse cursor points, return a feature that // should be highlighted (if any). diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 0e3f480cc..6994bfa32 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -654,7 +654,6 @@ void GLGizmoMeasure::on_render() GLModel::Geometry init_data = init_plane_data(its, plane_triangles); m_plane.reset(); m_plane.mesh_raycaster = std::make_unique(std::make_shared(init_data.get_as_indexed_triangle_set())); - m_plane.model.init_from(std::move(init_data)); } m_raycasters.insert({ PLANE_ID, m_parent.add_raycaster_for_picking(SceneRaycaster::EType::Gizmo, PLANE_ID, *m_plane.mesh_raycaster) });