1010# 
1111
1212import  os 
13+ import  subprocess 
1314import  logging 
1415from  argparse  import  ArgumentParser 
1516import  shutil 
3738magick_command  =  '"{}"' .format (args .magick_executable ) if  len (args .magick_executable ) >  0  else  "magick" 
3839use_gpu  =  1  if  not  args .no_gpu  else  0 
3940
40- EXIT_FAIL  =  1 
41+ # configure logging 
42+ logging .basicConfig (level  =  logging .INFO )
43+ 
44+ # execute a command after logging it and propagate failure correctly 
45+ def  exec (cmd ):
46+     logging .info (f"Executing: { cmd }  )
47+     try :
48+         subprocess .check_call (cmd , stdout = subprocess .DEVNULL , stderr = subprocess .STDOUT , shell = True )
49+     except  subprocess .CalledProcessError  as  e :
50+         logging .error (f"Command failed with code { e .returncode }  )
51+         exit (e .returncode )
4152
4253if  not  args .skip_matching :
4354    os .makedirs (args .source_path  +  "/distorted/sparse" , exist_ok = True )
4960\ 
5061+  args .camera  +  " \  
5162+  str (use_gpu )
52-     exit_code  =  os .system (feat_extracton_cmd )
53-     if  exit_code  !=  0 :
54-         logging .error (f"Feature extraction failed with code { exit_code }  )
55-         exit (EXIT_FAIL )
63+     exec (feat_extracton_cmd )
5664
5765    ## Feature matching 
5866    feat_matching_cmd  =  colmap_command  +  " exhaustive_matcher \  
5967+  args .source_path  +  "/distorted/database.db \  
6068+  str (use_gpu )
61-     exit_code  =  os .system (feat_matching_cmd )
62-     if  exit_code  !=  0 :
63-         logging .error (f"Feature matching failed with code { exit_code }  )
64-         exit (EXIT_FAIL )
69+     exec (feat_matching_cmd )
6570
6671    ### Bundle adjustment 
6772    # The default Mapper tolerance is unnecessarily large, 
7176+  args .source_path  +  "/input \  
7277+  args .source_path  +  "/distorted/sparse \  
7378
74-     exit_code  =  os .system (mapper_cmd )
75-     if  exit_code  !=  0 :
76-         logging .error (f"Mapper failed with code { exit_code }  )
77-         exit (EXIT_FAIL )
78- 
79+     exec (mapper_cmd )
7980
8081# select the largest submodel 
8182i  =  0 
107108+  distorted_sparse_path  +  " \  
108109+  args .source_path  +  "\  
109110
110- 
111- exit_code  =  os .system (img_undist_cmd )
112- if  exit_code  !=  0 :
113-     logging .error (f"image_undistorter failed with code { exit_code }  )
114-     exit (EXIT_FAIL )
111+ exec (img_undist_cmd )
115112
116113
117- def  remove_dir_if_exist (path ):
118-     if  Path (path ).exists ():
119-         shutil .rmtree (path )
120- 
114+ # Handle masks 
121115
122116if  args .masks_path  is  not None :
123-     remove_dir_if_exist (args .source_path  +  "/alpha_distorted_sparse_txt/" )
124-     Path (args .source_path  +  "/alpha_distorted_sparse_txt/" ).mkdir (exist_ok = True )
125-     # We need to "hack" colmap to undistort segmentation maps modify paths 
117+     # We need to modify the colmap database to reference the mask images 
118+     # which are always in png format. 
119+     mask_model_path  =  args .masks_path  +  "/model" 
120+     Path (mask_model_path ).mkdir (exist_ok = True )
121+ 
126122    # First convert model to text format 
127123    model_converter_cmd  =  (colmap_command  +  " model_converter \  
128124+  distorted_sparse_path  +  " \  
129- +  args . source_path  +  "/alpha_distorted_sparse_txt/  \  
125+ +  mask_model_path  +  " \  
130126
131-     exit_code  =  os .system (model_converter_cmd )
132-     if  exit_code  !=  0 :
133-         logging .error (f"model_converter failed with code { exit_code }  )
134-         exit (EXIT_FAIL )
135- 
136-     # replace '.jpg' to '.png' 
137-     with  open (args .source_path  +  "/alpha_distorted_sparse_txt/images.txt" , "r+" ) as  f :
138-         images_txt  =  f .read ()
139-         images_txt  =  images_txt .replace ('.jpg' , '.png' )
140-         f .seek (0 )
141-         f .write (images_txt )
142-         f .truncate ()
143- 
144-     # Undistort alpha masks 
127+     exec (model_converter_cmd )
128+ 
129+     # read images.txt 
130+     with  open (mask_model_path  +  "/images.txt" , 'r' ) as  file :
131+         lines  =  file .readlines ()
132+ 
133+     # replace image filenames with png extensions (and keep the list of renames for later) 
134+     filenames  =  []
135+     l  =  0 
136+     for  i  in  range (len (lines )):
137+         if  lines [i ].startswith ("#" ):
138+             # skip comments 
139+             continue 
140+         if  l  %  2  ==  0 :
141+             # handle every second line 
142+             words  =  lines [i ].rstrip ().split (" " )
143+             filename  =  words [- 1 ].split ("." )
144+             filename [- 1 ] =  "png" 
145+             new_filename  =  "." .join (filename )
146+             filenames .append ([words [- 1 ], new_filename ])
147+             words [- 1 ] =  new_filename 
148+             lines [i ] =  " " .join (words ) +  "\n " 
149+         l  +=  1 
150+ 
151+     # write modified images.txt 
152+     with  open (mask_model_path  +  "/images.txt" , 'w' ) as  file :
153+         file .writelines (lines )
154+ 
155+     # Undistort mask images 
145156    seg_undist_cmd  =  (colmap_command  +  " image_undistorter \  
146157+  args .masks_path  +  " \  
147- +  args . source_path  +  "/alpha_distorted_sparse_txt/  \  
148- +  args .source_path  +  "/alpha_undistorted_sparse  \  
158+ +  mask_model_path  +  " \  
159+ +  args .masks_path  +  "/undistorted  \  
149160
150-     exit_code  =  os .system (seg_undist_cmd )
151-     if  exit_code  !=  0 :
152-         logging .error (f"image_undistorter for segs failed with code { exit_code }  )
153-         exit (EXIT_FAIL )
154- 
155-     # switch images 
156-     remove_dir_if_exist (f'{ args .source_path }  )
157-     Path (f'{ args .source_path }  ).replace (f'{ args .source_path }  )
158-     remove_dir_if_exist (f'{ args .source_path }  )
159-     Path (f'{ args .source_path }  ).replace (f'{ args .source_path }  )
160- 
161-     # concat undistorted images with undistorted alpha masks - TODO: make parallel 
162-     remove_dir_if_exist (f'{ args .source_path }  )
163-     Path (f'{ args .source_path }  ).mkdir ()
164- 
165-     def  concat_alpha (seg_path ):
166-         seg  =  Image .open (seg_path ).convert ('L' )
167-         img  =  Image .open (f'{ args .source_path } { Path (seg_path ).stem }  )
168-         img .putalpha (seg )
169-         img .save (f'{ args .source_path } { Path (seg_path ).stem }  )
170- 
171-     all_masks_paths  =  glob (args .source_path  +  "/alpha_undistorted_sparse/alphas/*.png" )
172-     with  mp .Pool () as  pool :
173-         list (tqdm (pool .imap_unordered (concat_alpha , all_masks_paths ), total = len (all_masks_paths )))
174- 
175-     # switch models 
176-     remove_dir_if_exist (f'{ args .source_path }  )
177-     Path (f'{ args .source_path }  ).replace (f'{ args .source_path }  )
178-     Path (f'{ args .source_path }  ).replace (f'{ args .source_path }  )
179- 
180- if  args .generate_text_model :
181-     ### Convert model to text format so we can read cameras 
182-     convert_cmd  =  (colmap_command  +  " model_converter \  
183- +  args .source_path  +  "/sparse"  +  " \  
184- +  args .source_path  +  "/sparse"  +  " \  
185- 
186-     exit_code  =  os .system (convert_cmd )
187-     if  exit_code  !=  0 :
188-         logging .error (f"Convert failed with code { exit_code }  )
189-         exit (exit_code )
190- 
191- # move all files from sparse into sparse/0, as train.py expects it 
192- files  =  os .listdir (args .source_path  +  "/sparse" )
193- os .makedirs (args .source_path  +  "/sparse/0" , exist_ok = True )
194- # Copy each file from the source directory to the destination directory 
195- for  file  in  files :
196-     if  file  ==  "0" :
197-         continue 
198-     source_file  =  os .path .join (args .source_path , "sparse" , file )
199-     destination_file  =  os .path .join (args .source_path , "sparse" , "0" , file )
200-     shutil .move (source_file , destination_file )
201- 
202- if (args .resize ):
161+     exec (seg_undist_cmd )
162+ 
163+     # combine undistorted color and mask images 
164+     def  combine (color_path , alpha_path , output_path ):
165+         alpha  =  Image .open (alpha_path ).convert ('L' )
166+         clr  =  Image .open (color_path )
167+         clr .putalpha (alpha )
168+         clr .save (output_path )
169+ 
170+     for  i  in  range (len (filenames )):
171+         color_image  =  args .source_path  +  "/images/"  +  filenames [i ][0 ]
172+         mask_image  =  args .masks_path  +  "/undistorted/images/"  +  filenames [i ][1 ]
173+         output_image  =  args .source_path  +  "/images/"  +  filenames [i ][1 ]
174+         combine (color_image , mask_image , output_image )
175+ 
176+     # copy the modified database to final location for use in training 
177+     target_path  =  args .source_path  +  "/sparse/0" 
178+     Path (target_path ).mkdir (exist_ok = True )
179+ 
180+     source_path  =  args .masks_path  +  "/undistorted/sparse" 
181+     files  =  os .listdir (source_path )
182+     for  file  in  files :
183+         source_file  =  os .path .join (source_path , file )
184+         destination_file  =  os .path .join (target_path , file )
185+         shutil .move (source_file , destination_file )
186+ else :
187+     # move all files from sparse into sparse/0, as train.py expects it 
188+     files  =  os .listdir (args .source_path  +  "/sparse" )
189+     os .makedirs (args .source_path  +  "/sparse/0" , exist_ok = True )
190+     # Copy each file from the source directory to the destination directory 
191+     for  file  in  files :
192+         if  file  ==  "0" :
193+             continue 
194+         source_file  =  os .path .join (args .source_path , "sparse" , file )
195+         destination_file  =  os .path .join (args .source_path , "sparse" , "0" , file )
196+         shutil .move (source_file , destination_file )
197+ 
198+ if  (args .resize ):
203199    print ("Copying and resizing..." )
204200
205201    # Resize images. 
@@ -211,26 +207,17 @@ def concat_alpha(seg_path):
211207    # Copy each file from the source directory to the destination directory 
212208    for  file  in  files :
213209        source_file  =  os .path .join (args .source_path , "images" , file )
214- 
215-         destination_file  =  os .path .join (args .source_path , "images_2" , file )
216-         shutil .copy2 (source_file , destination_file )
217-         exit_code  =  os .system ("mogrify -resize 50% "  +  destination_file )
218-         if  exit_code  !=  0 :
219-             logging .error (f"50% resize failed with code { exit_code }  )
220-             exit (EXIT_FAIL )
221- 
222-         destination_file  =  os .path .join (args .source_path , "images_4" , file )
223-         shutil .copy2 (source_file , destination_file )
224-         exit_code  =  os .system ("mogrify -resize 25% "  +  destination_file )
225-         if  exit_code  !=  0 :
226-             logging .error (f"25% resize failed with code { exit_code }  )
227-             exit (EXIT_FAIL )
228- 
229-         destination_file  =  os .path .join (args .source_path , "images_8" , file )
230-         shutil .copy2 (source_file , destination_file )
231-         exit_code  =  os .system ("mogrify -resize 12.5% "  +  destination_file )
232-         if  exit_code  !=  0 :
233-             logging .error (f"12.5% resize failed with code { exit_code }  )
234-             exit (EXIT_FAIL )
210+         output_file2  =  os .path .join (args .source_path , "images_2" , file )
211+         output_file4  =  os .path .join (args .source_path , "images_4" , file )
212+         output_file8  =  os .path .join (args .source_path , "images_8" , file )
213+ 
214+         # generate the resized images in a single call 
215+         generate_thumbnails_cmd  =  ("convert " 
216+             # resize input file, uses less memory 
217+             f"{ source_file }  
218+             f" -write mpr:thumb -write { output_file2 }  
219+             f" mpr:thumb -resize 50% -write mpr:thumb -write { output_file4 }  
220+             f" mpr:thumb -resize 50% { output_file8 }  )
221+         exec (generate_thumbnails_cmd )
235222
236223print ("Done." )
0 commit comments