@@ -72,17 +72,56 @@ fn main() {
7272
7373 let cli = CCRSCli :: parse ( ) ;
7474 let detector = TagDetector :: new ( & cli. tag_family , None ) ;
75- let board = if let Some ( board_config_path) = cli. board_config {
76- Board :: from_config ( & object_from_json ( & board_config_path) )
75+ let board = setup_board ( & cli) ;
76+ let output_folder = setup_output_folder ( & cli) ;
77+
78+ let recording = rerun:: RecordingStreamBuilder :: new ( "calibration" )
79+ . save ( format ! ( "{}/logging.rrd" , output_folder) )
80+ . unwrap ( ) ;
81+ recording
82+ . log_static ( "/" , & rerun:: ViewCoordinates :: RDF ( ) )
83+ . unwrap ( ) ;
84+
85+ let cams_detected_feature_frames = load_feature_data ( & cli, & detector, & board, & recording) ;
86+
87+ let ( calibrated_intrinsics, cam_rtvecs) =
88+ calibrate_all_cameras ( & cli, & cams_detected_feature_frames, & recording) ;
89+
90+ let t_cam_i_0_init = init_camera_extrinsic ( & cam_rtvecs) ;
91+
92+ save_and_validate_results (
93+ & cli,
94+ & output_folder,
95+ & cams_detected_feature_frames,
96+ & calibrated_intrinsics,
97+ & cam_rtvecs,
98+ & t_cam_i_0_init,
99+ & recording,
100+ ) ;
101+ }
102+
103+ /// Loads the board configuration specified in the CLI arguments or creates a default one.
104+ ///
105+ /// If a config file path is provided via `--board-config`, it loads from that file.
106+ /// Otherwise, it creates a default 6x6 AprilGrid configuration and saves it to `default_board_config.json`.
107+ fn setup_board ( cli : & CCRSCli ) -> Board {
108+ if let Some ( board_config_path) = & cli. board_config {
109+ Board :: from_config ( & object_from_json ( board_config_path) )
77110 } else {
78111 let config = BoardConfig :: default ( ) ;
79112 object_to_json ( "default_board_config.json" , & config) ;
80113 Board :: from_config ( & config)
81- } ;
82- let dataset_root = & cli. path ;
83- let now = Instant :: now ( ) ;
84- let output_folder = if let Some ( output_folder) = cli. output_folder {
85- output_folder
114+ }
115+ }
116+
117+ /// Sets up the output directory for calibration results.
118+ ///
119+ /// If `--output-folder` is specified, uses that path.
120+ /// Otherwise, creates a directory named with the current timestamp under `results/`.
121+ /// Ensures the directory exists.
122+ fn setup_output_folder ( cli : & CCRSCli ) -> String {
123+ let output_folder = if let Some ( output_folder) = & cli. output_folder {
124+ output_folder. clone ( )
86125 } else {
87126 let now = OffsetDateTime :: now_local ( ) . unwrap ( ) ;
88127 format ! (
@@ -96,52 +135,84 @@ fn main() {
96135 )
97136 } ;
98137 std:: fs:: create_dir_all ( & output_folder) . expect ( "Valid path" ) ;
138+ output_folder
139+ }
99140
100- let recording = rerun:: RecordingStreamBuilder :: new ( "calibration" )
101- . save ( format ! ( "{}/logging.rrd" , output_folder) )
102- . unwrap ( ) ;
103- recording
104- . log_static ( "/" , & rerun:: ViewCoordinates :: RDF ( ) )
105- . unwrap ( ) ;
141+ /// Loads feature data from the dataset.
142+ ///
143+ /// Supports Euroc and General dataset formats.
144+ /// Uses the provided tag detector and board configuration to extract features.
145+ /// Logs images to Rerun if enabled.
146+ ///
147+ /// # Returns
148+ /// A vector of vectors, where each inner vector contains `Option<FrameFeature>` for a camera.
149+ fn load_feature_data (
150+ cli : & CCRSCli ,
151+ detector : & TagDetector ,
152+ board : & Board ,
153+ recording : & rerun:: RecordingStream ,
154+ ) -> Vec < Vec < Option < FrameFeature > > > {
106155 trace ! ( "Start loading data" ) ;
107156 println ! ( "Start loading images and detecting charts." ) ;
157+ let now = Instant :: now ( ) ;
108158 let mut cams_detected_feature_frames: Vec < Vec < Option < FrameFeature > > > = match cli. dataset_format
109159 {
110160 DatasetFormat :: Euroc => load_euroc (
111- dataset_root ,
112- & detector,
113- & board,
161+ & cli . path ,
162+ detector,
163+ board,
114164 cli. start_idx ,
115165 cli. step ,
116166 cli. cam_num ,
117- Some ( & recording) ,
167+ Some ( recording) ,
118168 ) ,
119169 DatasetFormat :: General => load_others (
120- dataset_root ,
121- & detector,
122- & board,
170+ & cli . path ,
171+ detector,
172+ board,
123173 cli. start_idx ,
124174 cli. step ,
125175 cli. cam_num ,
126- Some ( & recording) ,
176+ Some ( recording) ,
127177 ) ,
128178 } ;
129179 let duration_sec = now. elapsed ( ) . as_secs_f64 ( ) ;
130180 println ! ( "detecting feature took {:.6} sec" , duration_sec) ;
131- println ! ( "total: {} images" , cams_detected_feature_frames[ 0 ] . len( ) ) ;
181+ if !cams_detected_feature_frames. is_empty ( ) {
182+ println ! ( "total: {} images" , cams_detected_feature_frames[ 0 ] . len( ) ) ;
183+ println ! (
184+ "avg: {} sec" ,
185+ duration_sec / cams_detected_feature_frames[ 0 ] . len( ) as f64
186+ ) ;
187+ }
188+
132189 cams_detected_feature_frames
133190 . iter_mut ( )
134191 . for_each ( |f| f. truncate ( cli. max_images ) ) ;
135- println ! (
136- "avg: {} sec" ,
137- duration_sec / cams_detected_feature_frames[ 0 ] . len( ) as f64
138- ) ;
139- let ( calibrated_intrinsics, cam_rtvecs) : ( Vec < _ > , Vec < _ > ) = cams_detected_feature_frames
192+
193+ cams_detected_feature_frames
194+ }
195+
196+ /// Calibrates all cameras individually.
197+ ///
198+ /// Iterates through each camera, detecting features and running the optimization.
199+ /// Retries calibration up to 3 times if it fails.
200+ ///
201+ /// # Returns
202+ /// A tuple containing:
203+ /// - `Vec<GenericModel<f64>>`: The calibrated intrinsic models for each camera.
204+ /// - `Vec<HashMap<usize, RvecTvec>>`: estimated camera poses for each frame.
205+ fn calibrate_all_cameras (
206+ cli : & CCRSCli ,
207+ cams_detected_feature_frames : & [ Vec < Option < FrameFeature > > ] ,
208+ recording : & rerun:: RecordingStream ,
209+ ) -> ( Vec < GenericModel < f64 > > , Vec < HashMap < usize , RvecTvec > > ) {
210+ cams_detected_feature_frames
140211 . iter ( )
141212 . enumerate ( )
142213 . map ( |( cam_idx, feature_frames) | {
143214 let topic = format ! ( "/cam{}" , cam_idx) ;
144- log_feature_frames ( & recording, & topic, feature_frames) ;
215+ log_feature_frames ( recording, & topic, feature_frames) ;
145216 let mut calibrated_result: Option < ( GenericModel < f64 > , HashMap < usize , RvecTvec > ) > = None ;
146217 let max_trials = 3 ;
147218 let cam0_fixed_focal = if cam_idx == 0 { cli. fixed_focal } else { None } ;
@@ -153,9 +224,9 @@ fn main() {
153224 for trial in 0 ..max_trials {
154225 calibrated_result = init_and_calibrate_one_camera (
155226 cam_idx,
156- & cams_detected_feature_frames,
227+ cams_detected_feature_frames,
157228 & cli. model ,
158- & recording,
229+ recording,
159230 & calib_params,
160231 trial > 0 ,
161232 ) ;
@@ -169,19 +240,35 @@ fn main() {
169240 cam_idx, max_trials
170241 ) ;
171242 }
172- let ( final_result, rtvec_map) = calibrated_result. unwrap ( ) ;
173- ( final_result, rtvec_map)
243+ calibrated_result. unwrap ( )
174244 } )
175- . unzip ( ) ;
176- let t_cam_i_0_init = init_camera_extrinsic ( & cam_rtvecs) ;
177- for t in & t_cam_i_0_init {
245+ . unzip ( )
246+ }
247+
248+ /// Saves calibration results and performs validation.
249+ ///
250+ /// saves intrinsics, extrinsics, and pose data to JSON files.
251+ /// Generates a validation report and logs visualization data to Rerun.
252+ /// If multiple cameras are present, it also attempts to calibrate extrinsics between cameras.
253+ #[ allow( clippy:: too_many_arguments) ]
254+ fn save_and_validate_results (
255+ cli : & CCRSCli ,
256+ output_folder : & str ,
257+ cams_detected_feature_frames : & [ Vec < Option < FrameFeature > > ] ,
258+ intrinsics : & [ GenericModel < f64 > ] ,
259+ cam_rtvecs : & [ HashMap < usize , RvecTvec > ] ,
260+ t_cam_i_0_init : & [ RvecTvec ] ,
261+ recording : & rerun:: RecordingStream ,
262+ ) {
263+ for t in t_cam_i_0_init {
178264 println ! ( "r {} t {}" , t. na_rvec( ) , t. na_tvec( ) ) ;
179265 }
266+
180267 if let Some ( ( camera_intrinsics, t_i_0, board_rtvecs) ) = calib_all_camera_with_extrinsics (
181- & calibrated_intrinsics ,
182- & t_cam_i_0_init,
183- & cam_rtvecs,
184- & cams_detected_feature_frames,
268+ intrinsics ,
269+ t_cam_i_0_init,
270+ cam_rtvecs,
271+ cams_detected_feature_frames,
185272 cli. one_focal || cli. fixed_focal . is_some ( ) ,
186273 cli. disabled_distortion_num ,
187274 cli. fixed_focal . is_some ( ) ,
@@ -215,7 +302,7 @@ fn main() {
215302 intrinsic,
216303 & new_rtvec_map,
217304 & cams_detected_feature_frames[ cam_idx] ,
218- Some ( & recording) ,
305+ Some ( recording) ,
219306 ) ;
220307 rep_rms. push ( rep) ;
221308 println ! (
@@ -232,15 +319,13 @@ fn main() {
232319 ) ;
233320 } else {
234321 let mut rep_rms = Vec :: new ( ) ;
235- for ( cam_idx, ( intrinsic, rtvec_map) ) in
236- calibrated_intrinsics. iter ( ) . zip ( cam_rtvecs) . enumerate ( )
237- {
322+ for ( cam_idx, ( intrinsic, rtvec_map) ) in intrinsics. iter ( ) . zip ( cam_rtvecs) . enumerate ( ) {
238323 let rep = validation (
239324 cam_idx,
240325 intrinsic,
241- & rtvec_map,
326+ rtvec_map,
242327 & cams_detected_feature_frames[ cam_idx] ,
243- Some ( & recording) ,
328+ Some ( recording) ,
244329 ) ;
245330 rep_rms. push ( rep) ;
246331 println ! (
0 commit comments