18[[nodiscard]]
auto count_occurrences(std::string_view text, std::string_view needle)
23 std::size_t count = 0;
26 pos = text.find(needle, pos);
27 if (pos == std::string_view::npos) {
40 -> std::vector<PlanarView> {
41 std::vector<PlanarView> planar_views;
45 if (img.points.size() < opts.min_corners_per_view) {
49 std::transform(img.points.begin(), img.points.end(), view.begin(), [&](
const auto& pt) {
50 PlanarObservation obs;
51 obs.object_xy = Eigen::Vector2d(pt.local_x, pt.local_y);
52 obs.image_uv = Eigen::Vector2d(pt.x, pt.y);
55 views.push_back({img.file, view.size()});
56 planar_views.push_back(std::move(view));
62 const double width =
static_cast<double>(image_size[0]);
63 const double height =
static_cast<double>(image_size[1]);
64 const double short_side = std::min(width, height);
65 const double long_side = std::max(width, height);
69 b.
fx_max = b.
fy_max = std::numeric_limits<double>::max();
74 const double skew_limit = 0.05 * long_side;
88 std::vector<ActiveView> active_views;
92 if (planar_views.size() < 4) {
93 std::ostringstream oss;
94 oss <<
"Need at least 4 views with >= " << cfg.options.min_corners_per_view
95 <<
" corners. Only " << planar_views.size() <<
" usable views.";
96 throw std::runtime_error(oss.str());
100 std::string captured_warnings;
104 captured_warnings = capture.
str();
106 output.
invalid_k_warnings = count_occurrences(captured_warnings,
"Invalid camera matrix K");
107 output.
pose_warnings = count_occurrences(captured_warnings,
"Homography decomposition failed");
109 std::cerr <<
"[" << cam_cfg.camera_id
112 <<
" decomposition failures" <<
'\n';
115 throw std::runtime_error(
"Linear intrinsic estimation failed to converge.");
118 std::vector<std::size_t> linear_view_indices(linear.
views.size());
119 std::transform(linear.
views.begin(), linear.
views.end(), linear_view_indices.begin(),
120 [](
const auto& v) { return v.view_index; });
123 if (cfg.options.refine) {
125 std::vector<Eigen::Isometry3d> init_c_se3_t(planar_views.size());
126 std::transform(planar_views.begin(), planar_views.end(), init_c_se3_t.begin(),
127 [&](
const auto& view) { return estimate_planar_pose(view, linear.kmtx); });
133 std::cerr <<
"Warning: Non-linear refinement did not converge. Using linear result."
142 if (refine.
camera.distortion.coeffs.size() == 0) {
143 refine.
camera.distortion.coeffs = Eigen::VectorXd::Zero(5);
161 out <<
"== Camera " << cam_cfg.
camera_id <<
" ==\n";
165 <<
" homography decompositions\n";
170 out <<
"Refined fx/fy/cx/cy: " << refined.kmtx.fx <<
", " << refined.kmtx.fy <<
", "
171 << refined.kmtx.cx <<
", " << refined.kmtx.cy <<
'\n';
172 out <<
"Distortion coeffs: " << outputs.
refine_result.camera.distortion.coeffs.transpose()
176 out <<
"Per-view RMS (px):";
184 std::ifstream stream(path);
185 nlohmann::json json_cfg;
192 -> std::optional<IntrinsicCalibrationConfig> {
195 }
catch (
const std::exception& e) {
196 std::cerr <<
"Failed to load calibration config from " << path <<
": " << e.what()
Pinhole camera model with intrinsics and distortion correction.
auto str() const -> std::string
auto calibrate(const IntrinsicCalibrationConfig &cfg, const CameraConfig &cam_cfg, const PlanarDetections &detections) const -> IntrinsicCalibrationOutputs
PlanarDetections detections
auto collect_planar_views(const PlanarDetections &detections, const IntrinsicCalibrationOptions &opts, std::vector< ActiveView > &views) -> std::vector< PlanarView >
auto load_calibration_config(const std::filesystem::path &path) -> std::optional< IntrinsicCalibrationConfig >
auto bounds_from_image_size(const std::array< int, 2 > &image_size) -> CalibrationBounds
void print_calibration_summary(std::ostream &out, const CameraConfig &cam_cfg, const IntrinsicCalibrationOutputs &outputs)
auto load_calibration_config_impl(const std::filesystem::path &path) -> IntrinsicCalibrationConfig
auto estimate_intrinsics(const std::vector< PlanarView > &views, const IntrinsicsEstimOptions &opts={}) -> IntrinsicsEstimateResult
Estimate camera intrinsics from planar views using a linear method.
auto optimize_intrinsics(const std::vector< PlanarView > &views, const CameraT &init_camera, std::vector< Eigen::Isometry3d > init_c_se3_t, const IntrinsicsOptimOptions &opts={}) -> IntrinsicsOptimizationResult< CameraT >
std::vector< PlanarObservation > PlanarView
Result of linear intrinsic estimation.
std::vector< ViewEstimateData > views
Per-view estimation data.
CameraMatrix kmtx
Estimated intrinsic matrix.
OptimResult core
Optimization result details.
CameraT camera
Estimated camera parameters.
std::size_t invalid_k_warnings
IntrinsicsOptimizationResult< PinholeCamera< BrownConradyd > > refine_result
std::size_t total_points_used
std::size_t pose_warnings
std::vector< std::size_t > linear_view_indices
std::vector< ActiveView > active_views
std::size_t accepted_views
std::size_t min_corner_threshold
std::size_t total_input_views
std::vector< PlanarImageDetections > images