Calibration Library 1.0.0
A C++ library for camera calibration and vision-related geometric transformations
Loading...
Searching...
No Matches
linescan.h
Go to the documentation of this file.
1#pragma once
2
3#include <Eigen/Core>
4#include <Eigen/Geometry>
5#include <stdexcept>
6#include <string>
7
10#include "calib/estimation/linear/planarpose.h" // PlanarView
13
14namespace calib {
15
16struct LineScanView final {
18 std::vector<Eigen::Vector2d> laser_uv;
19};
20
22 Eigen::Vector4d plane;
23 Eigen::Matrix4d covariance;
24 Eigen::Matrix3d homography;
25 double rms_error = 0.0;
26 std::string summary;
27 std::size_t inlier_count = 0;
28};
29
34
35static_assert(serializable_aggregate<LineScanView>);
36static_assert(serializable_aggregate<LineScanCalibrationResult>);
37static_assert(serializable_aggregate<LineScanPlaneFitOptions>);
38
39inline void validate_observations(const std::vector<LineScanView>& views) {
40 if (views.size() < 2) {
41 throw std::invalid_argument("At least 2 views are required");
42 }
43 if (std::any_of(views.begin(), views.end(),
44 [](const auto& v) { return v.target_view.size() < 4; })) {
45 throw std::invalid_argument("Each view requires >=4 target correspondences");
46 }
47}
48
49inline Eigen::Matrix3d build_plane_homography(const Eigen::Vector4d& plane) {
50 Eigen::Vector3d nvec = plane.head<3>();
51 Eigen::Vector3d p0 = -plane[3] * nvec;
52 Eigen::Vector3d tmp =
53 (std::abs(nvec.z()) < 0.9) ? Eigen::Vector3d::UnitZ() : Eigen::Vector3d::UnitX();
54 Eigen::Vector3d e1 = nvec.cross(tmp).normalized();
55 Eigen::Vector3d e2 = nvec.cross(e1).normalized();
56 Eigen::Matrix3d plane_to_norm;
57 plane_to_norm.col(0) = e1;
58 plane_to_norm.col(1) = e2;
59 plane_to_norm.col(2) = p0;
60 return plane_to_norm.inverse();
61}
62
63template <camera_model CameraT>
64std::vector<Eigen::Vector3d> points_from_view(LineScanView view, const CameraT& camera) {
65 std::for_each(view.target_view.begin(), view.target_view.end(),
66 [&camera](PlanarObservation& item) {
67 item.image_uv = camera.template unproject<double>(item.image_uv);
68 });
69
70 auto hres = estimate_homography(view.target_view);
71 if (!hres.success) {
72 return {};
73 }
74
75 Eigen::Isometry3d pose = pose_from_homography_normalized(hres.hmtx);
76 Eigen::Matrix3d h_norm_to_obj = hres.hmtx.inverse();
77 if (std::abs(h_norm_to_obj(2, 2)) > 1e-15) {
78 h_norm_to_obj /= h_norm_to_obj(2, 2);
79 }
80
81 std::vector<Eigen::Vector3d> points;
82 points.reserve(view.laser_uv.size());
83 for (const auto& lpix : view.laser_uv) {
84 Eigen::Vector2d norm = camera.template unproject<double>(lpix);
85 Eigen::Vector3d hp = h_norm_to_obj * Eigen::Vector3d(norm.x(), norm.y(), 1.0);
86 Eigen::Vector2d plane_xy = hp.hnormalized();
87 Eigen::Vector3d obj_pt(plane_xy.x(), plane_xy.y(), 0.0);
88 points.push_back(pose * obj_pt);
89 }
90 return points;
91}
92
93inline double plane_rms(const std::vector<Eigen::Vector3d>& pts, const Eigen::Vector4d& plane) {
94 const double ss = std::accumulate(pts.begin(), pts.end(), 0.0, [&](double acc, const auto& p) {
95 double r = plane.head<3>().dot(p) + plane[3];
96 return acc + r * r;
97 });
98 return std::sqrt(ss / static_cast<double>(pts.size()));
99}
100
101template <camera_model CameraT>
102LineScanCalibrationResult calibrate_laser_plane(const std::vector<LineScanView>& views,
103 const CameraT& camera,
104 const LineScanPlaneFitOptions& opts = {}) {
106
107 LineScanCalibrationResult result;
108 std::vector<Eigen::Vector3d> all_points;
109 for (const auto& view : views) {
110 auto pts = points_from_view(view, camera);
111 all_points.insert(all_points.end(), pts.begin(), pts.end());
112 }
113 if (all_points.size() < 3) {
114 throw std::invalid_argument("Not enough laser points to fit a plane");
115 }
116
117 if (opts.use_ransac) {
118 auto ransac_result = fit_plane_ransac(all_points, opts.ransac_options);
119 if (!ransac_result.success) {
120 throw std::runtime_error("RANSAC plane fitting failed");
121 }
122 result.plane = ransac_result.plane;
123 result.summary = "ransac";
124 result.inlier_count = ransac_result.inliers.size();
125 if (!ransac_result.inliers.empty()) {
126 std::vector<Eigen::Vector3d> inlier_points(ransac_result.inliers.size());
127 std::transform(
128 ransac_result.inliers.begin(), ransac_result.inliers.end(), inlier_points.begin(),
129 [&all_points](int idx) { return all_points[static_cast<std::size_t>(idx)]; });
130 result.rms_error = plane_rms(inlier_points, result.plane);
131 } else {
132 result.rms_error = plane_rms(all_points, result.plane);
133 }
134 } else {
135 result.plane = fit_plane_svd(all_points);
136 result.summary = "linear_svd";
137 result.inlier_count = all_points.size();
138 result.rms_error = plane_rms(all_points, result.plane);
139 }
140
141 result.homography = build_plane_homography(result.plane);
142 result.covariance.setZero();
143 return result;
144}
145
146} // namespace calib
Linear multi-camera extrinsics initialisation (DLT)
void validate_observations(const std::vector< LineScanView > &views)
Definition linescan.h:39
std::vector< Eigen::Vector3d > points_from_view(LineScanView view, const CameraT &camera)
Definition linescan.h:64
Eigen::Matrix3d build_plane_homography(const Eigen::Vector4d &plane)
Definition linescan.h:49
auto estimate_homography(const PlanarView &data, std::optional< RansacOptions > ransac_opts=std::nullopt) -> HomographyResult
auto fit_plane_ransac(const std::vector< Eigen::Vector3d > &pts, const RansacOptions &opts={}) -> PlaneRansacResult
Definition planefit.cpp:87
double plane_rms(const std::vector< Eigen::Vector3d > &pts, const Eigen::Vector4d &plane)
Definition linescan.h:93
std::vector< PlanarObservation > PlanarView
Definition planarpose.h:26
auto fit_plane_svd(const std::vector< Eigen::Vector3d > &pts) -> Eigen::Vector4d
Definition planefit.cpp:68
LineScanCalibrationResult calibrate_laser_plane(const std::vector< LineScanView > &views, const CameraT &camera, const LineScanPlaneFitOptions &opts={})
Definition linescan.h:102
auto pose_from_homography_normalized(const Eigen::Matrix3d &hmtx) -> Eigen::Isometry3d
PlanarView target_view
Definition linescan.h:17
std::vector< Eigen::Vector2d > laser_uv
Definition linescan.h:18