1+ import argparse
2+ import os
3+ import shutil
4+ import pycolmap
5+ import subprocess
6+ from tools .save_cali import save_in
7+
8+ def run_colmap_command (args ):
9+ result = subprocess .run (args , capture_output = True , text = True )
10+ if result .returncode != 0 :
11+ print (f"Command failed: { ' ' .join (args )} " )
12+ print (f"Error: { result .stderr } " )
13+ raise RuntimeError ("COLMAP command failed" )
14+ return result
15+
16+ def main (input_imgs , output_dir , out , refine = True ):
17+ # Setup directory structure
18+ workspace_dir = os .path .join (output_dir , 'sfm_workspace' )
19+ image_dir = os .path .join (workspace_dir , 'images' )
20+ os .makedirs (image_dir , exist_ok = True )
21+
22+ # Copy images to workspace
23+ print (f"Copying images to workspace..." )
24+ for path in input_imgs :
25+ filename = path .split ('/' )[- 1 ]
26+ dst = os .path .join (image_dir , filename )
27+ shutil .copy (path , dst )
28+
29+ # Create database path
30+ database_path = os .path .join (workspace_dir , 'database.db' )
31+ if os .path .exists (database_path ):
32+ os .remove (database_path )
33+
34+ # Feature extraction
35+ print ("Extracting features..." )
36+ # pycolmap.extract_features(
37+ # database_path, image_dir,
38+ # camera_mode=pycolmap.CameraMode.SINGLE,
39+ # camera_model=pycolmap.CameraModelId.OPENCV
40+ # )
41+ run_colmap_command ([
42+ "colmap" , "feature_extractor" ,
43+ "--database_path" , database_path ,
44+ "--image_path" , image_dir ,
45+ "--ImageReader.single_camera" , "1" ,
46+ "--ImageReader.camera_model" , "OPENCV" ,
47+ "--SiftExtraction.estimate_affine_shape" , "1" ,
48+ "--SiftExtraction.domain_size_pooling" , "1"
49+ ])
50+
51+ # Feature matching
52+ print ("Matching features..." )
53+ match_options = pycolmap .SequentialMatchingOptions ()
54+ match_options .overlap = 2
55+ pycolmap .match_sequential (
56+ database_path ,
57+ matching_options = match_options
58+ )
59+ # Run SfM reconstruction
60+ mapper_options = pycolmap .IncrementalPipelineOptions ()
61+ if refine :
62+ mapper_options .ba_refine_focal_length = 1
63+ mapper_options .ba_refine_principal_point = 1
64+ mapper_options .ba_refine_extra_params = 1
65+ else :
66+ mapper_options .ba_refine_focal_length = 0
67+ mapper_options .ba_refine_principal_point = 0
68+ mapper_options .ba_refine_extra_params = 0
69+
70+ print ("Running incremental SfM..." )
71+ output_path = os .path .join (workspace_dir , 'sparse' )
72+ os .makedirs (output_path , exist_ok = True )
73+ reconstructions = pycolmap .incremental_mapping (
74+ database_path = database_path ,
75+ image_path = image_dir ,
76+ output_path = output_path ,
77+ options = mapper_options
78+ )
79+
80+ # Process results
81+ if not reconstructions :
82+ print ("SfM failed to reconstruct the scene!" )
83+ return
84+
85+ camera :pycolmap ._core .Camera = 0
86+ print ("\n Camera calibration parameters:" )
87+ for idx , reconstruction in reconstructions .items ():
88+ print (f"\n Reconstruction { idx + 1 } :" )
89+ for camera_id , camera in reconstruction .cameras .items ():
90+ print (f"\n Camera ID { camera_id } :" )
91+ print (f"Model: { camera .model } " )
92+ print (f"Parameters: { camera .params } " )
93+ print (f"Parameters info: { camera .params_info } " )
94+ # print(f"Focal length: {camera.focal_length}")
95+ print (f"Focal length x: { camera .focal_length_x } " )
96+ print (f"Focal length y: { camera .focal_length_y } " )
97+ print (f"Principal point x: { camera .principal_point_x } " )
98+ print (f"Principal point y: { camera .principal_point_y } " )
99+ save_in (
100+ path = out ,
101+ focal = [camera .focal_length_x ,camera .focal_length_y ],
102+ center = [camera .principal_point_x ,camera .principal_point_y ],
103+ distort = list (camera .params [4 :])+ [0.0 ],
104+ )
105+ print ("\n Calibration complete! Results saved to:" , workspace_dir )
106+
107+ if __name__ == "__main__" :
108+ parser = argparse .ArgumentParser (description = 'Camera calibration using SfM' )
109+ parser .add_argument ('--input_imgs' ,'-i' , nargs = '+' , help = 'List of input imgs' , required = True )
110+ parser .add_argument ('--workspace' ,'-w' , type = str , required = True ,
111+ help = 'Output directory for results' )
112+ parser .add_argument ('--out_file' ,'-o' , type = str , required = True ,
113+ help = 'output yaml file' )
114+ args = parser .parse_args ()
115+
116+ main (args .input_imgs , args .workspace , args .out_file )
0 commit comments