Program Listing for File room_calibration_manager.hpp#
↰ Return to documentation for file (src/calibration/room_calibration_manager.hpp)
#pragma once
#include <QVector>
#include <array>
#include <memory>
#include "calibration/room_frame_solver.hpp"
#include "core/settings.hpp"
#include "video/video_frame.hpp"
namespace mosaic {
/// @brief Multi-camera extrinsic ("room") calibration via a shared ChArUco board.
///
/// Companion to CalibrationManager, which only handles single-camera
/// intrinsics. A camera must already have a valid intrinsic CalibrationData
/// (set_camera_intrinsics()) before its views can contribute to a solve —
/// cv::solvePnP() needs real, non-identity intrinsics to produce a
/// meaningful board pose.
///
/// Requires @c MOSAIC_HAVE_OPENCV — is_available() returns @c false otherwise.
///
/// @par Workflow
/// @code{.cpp}
/// RoomCalibrationManager mgr;
/// mgr.set_board({7, 5, 40.0, 30.0});
/// for (int i = 0; i < cameraCount; ++i) mgr.set_camera_intrinsics(i,
/// settings.cameras[i].calibration);
///
/// // Repeat, moving the board through overlapping pairs of camera views:
/// mgr.feed_shot(simultaneousFrames);
///
/// const auto result = mgr.solve(cameraCount, /*referenceCameraIndex=*/0);
/// if (mgr.is_resolved(2)) { auto rt = mgr.extrinsic_for(2); ... }
///
/// std::array<double,3> planePoint, planeNormal;
/// mgr.use_shot_as_plane(lastShotIndex, 0, planePoint, planeNormal);
/// @endcode
class RoomCalibrationManager {
public:
/// @brief ChArUco board geometry.
struct BoardSpec {
int cols = 7; ///< Number of squares, horizontally.
int rows = 5; ///< Number of squares, vertically.
double squareLengthMm = 40.0; ///< Physical side length of one square, in mm.
double markerLengthMm = 30.0; ///< Physical side length of one ArUco marker, in mm.
};
/// @brief Outcome of one camera's board detection within a single shot.
struct CameraShotResult {
int cameraIndex = -1;
bool found = false; ///< true if enough ChArUco corners were found to solve a pose.
int cornerCount = 0;
};
/// @brief Outcome of a solve() call.
struct SolveResult {
std::vector<bool> resolved; ///< index = cameraIndex
std::vector<double> reprojectionRmsPx; ///< index = cameraIndex; -1 = never directly seen
};
RoomCalibrationManager();
~RoomCalibrationManager();
[[nodiscard]] static bool is_available();
/// Call before the first feed_shot(). Changing the spec after capturing
/// shots requires clear_shots() to discard incompatible data.
void set_board(const BoardSpec& spec);
/// Registers a camera's already-computed intrinsic calibration (from
/// CalibrationManager). Must be called once per camera before that
/// camera's frames in feed_shot() can be used — a camera with
/// @c !data.calibrated is skipped by feed_shot() with a warning.
void set_camera_intrinsics(int cameraIndex, const CalibrationData& data);
/// Runs ChArUco detection + per-camera cv::solvePnP() on one
/// simultaneous multi-camera shot. A camera missing from @p frames (the
/// UI's per-shot capture timed out waiting for it) or lacking prior
/// intrinsics is simply absent from the accepted shot data, reported as
/// `found = false` here. Returns one result per input frame, in order.
QVector<CameraShotResult> feed_shot(const QVector<VideoFrame>& frames);
[[nodiscard]] int shot_count() const;
void clear_shots();
/// Resolves every camera's extrinsicRt (pose relative to
/// referenceCameraIndex, which always resolves to identity) by BFS over
/// the shared-shot graph accumulated so far (room_frame::bfs_resolve()).
/// Also computes each directly-observed camera's own reprojection RMS
/// (its solvePnP fit quality, independent of any BFS chain length).
SolveResult solve(int cameraCount, int referenceCameraIndex);
[[nodiscard]] bool is_resolved(int cameraIndex) const;
[[nodiscard]] std::array<double, 16> extrinsic_for(int cameraIndex) const;
[[nodiscard]] double reprojection_rms_for(int cameraIndex) const;
/// Uses the board's own pose from a specific (shot, camera) pair — where
/// the board was presumably lying flat on the target surface — as the
/// room's reference plane. Both the shot's camera detection and that
/// camera's own extrinsic resolution (solve() already called) must
/// exist. outNormal is the board's printed-face normal (its local Z
/// axis) in room coordinates. Returns false if no matching detection
/// exists or the camera is unresolved.
[[nodiscard]] bool use_shot_as_plane(int shotIndex, int cameraIndex,
std::array<double, 3>& outPoint,
std::array<double, 3>& outNormal) const;
private:
struct Impl;
std::unique_ptr<Impl> d;
};
} // namespace mosaic