88Usage:
99 texturize SOURCE... [--size=WxH] [--output=FILE] [--seed=SEED] [--device=DEVICE]
1010 [--octaves=O] [--precision=P] [--iterations=I]
11+ [--quiet] [--verbose]
1112 texturize --help
1213
1314Examples:
1920Options:
2021 SOURCE Path to source image to use as texture.
2122 -s WxH, --size=WxH Output resolution as WIDTHxHEIGHT. [default: 640x480]
23+ -o FILE, --output=FILE Filename for saving the result. [default: {source}_gen.png]
2224 --seed=SEED Configure the random number generation.
2325 --device=DEVICE Hardware to use, either "cpu" or "cuda".
2426 --octaves=O Number of octaves to process. [default: 5]
2527 --precision=P Set the quality for the optimization. [default: 1e-4]
2628 --iterations=I Maximum number of iterations each octave. [default: 99]
27- -o FILE, --output=FILE Filename for saving the result. [default: {source}_gen.png]
29+ --quiet Suppress any messages going to stdout.
30+ --verbose Display more information on stdout.
2831 -h --help Show this message.
2932
3033"""
5558from . import io
5659
5760
61+ class OutputLog :
62+
63+ def __init__ (self , config ):
64+ self .quiet = config ['--quiet' ]
65+ self .verbose = config ['--verbose' ]
66+
67+ def create_progress_bar (self , iterations ):
68+ widgets = [
69+ progressbar .SimpleProgress (),
70+ " | " ,
71+ progressbar .Variable ("loss" , format = "{name}: {value:0.3e}" ),
72+ " " ,
73+ progressbar .Bar (marker = "■" , fill = "·" ),
74+ " " ,
75+ progressbar .ETA (),
76+ ]
77+ ProgressBar = progressbar .NullBar if self .quiet else progressbar .ProgressBar
78+ return ProgressBar (
79+ max_value = iterations , widgets = widgets , variables = {"loss" : float ("+inf" )}
80+ )
81+
82+ def debug (self , * args ):
83+ if self .verbose :
84+ print (* args )
85+
86+ def info (self , * args ):
87+ if not self .quiet :
88+ print (* args )
89+
90+
5891class TextureSynthesizer :
5992 def __init__ (self , device , encoder , lr , precision , max_iter ):
6093 self .device = device
@@ -70,26 +103,15 @@ def prepare(self, critics, image):
70103 for critic in critics :
71104 critic .from_features (feats )
72105
73- def run (self , seed_img , critics ):
106+ def run (self , logger , seed_img , critics ):
74107 """Run the optimizer on the image according to the loss returned by the critics.
75108 """
76109 image = seed_img .to (self .device ).requires_grad_ (True )
77110
78111 obj = MultiCriticObjective (self .encoder , critics )
79112 opt = SolverLBFGS (obj , image , lr = self .lr )
80113
81- widgets = [
82- progressbar .SimpleProgress (),
83- " | " ,
84- progressbar .Variable ("loss" , format = "{name}: {value:0.3e}" ),
85- " " ,
86- progressbar .Bar (marker = "■" , fill = "·" ),
87- " " ,
88- progressbar .ETA (),
89- ]
90- progress = progressbar .ProgressBar (
91- max_value = self .max_iter , widgets = widgets , variables = {"loss" : float ("+inf" )}
92- )
114+ progress = logger .create_progress_bar (self .max_iter )
93115
94116 try :
95117 for i , loss in self ._iterate (opt ):
@@ -129,13 +151,16 @@ class ansi:
129151
130152
131153def process_file (config , source ):
154+ log = config ['--logger' ]
132155 for octave , result_img in process_image (config , io .load_image_from_file (source )):
133156 # Save the files for each octave to disk.
134157 filename = config ["--output" ].format (
135158 octave = octave , source = os .path .splitext (os .path .basename (source ))[0 ]
136159 )
137160 result_img .save (filename )
138- print ("\n => output:" , filename )
161+ log .debug ("\n => output:" , filename )
162+
163+ return filename
139164
140165
141166@torch .no_grad ()
@@ -161,6 +186,8 @@ def process_image(config, source):
161186 dtype = torch .float32 ,
162187 ).uniform_ (0.4 , 0.6 )
163188
189+ # Coarse-to-fine rendering, number of octaves specified by user.
190+ log = config ['--logger' ]
164191 for i , octave in enumerate (2 ** s for s in range (octaves - 1 , - 1 , - 1 )):
165192 # Each octave we start a new optimization process.
166193 synth = TextureSynthesizer (
@@ -170,8 +197,8 @@ def process_image(config, source):
170197 precision = float (config ["--precision" ]),
171198 max_iter = int (config ["--iterations" ]),
172199 )
173- print (ansi .BLACK + "\n OCTAVE" , f"#{ i } " + ansi .ENDC )
174- print ("<- scale:" , f"1/{ octave } " )
200+ log . info (ansi .BLACK + "\n OCTAVE" , f"#{ i } " + ansi .ENDC )
201+ log . debug ("<- scale:" , f"1/{ octave } " )
175202
176203 # Create downscaled version of original texture to match this octave.
177204 texture_cur = F .interpolate (
@@ -181,19 +208,19 @@ def process_image(config, source):
181208 recompute_scale_factor = False ,
182209 ).to (config ["--device" ])
183210 synth .prepare (critics , texture_cur )
184- print ("<- texture:" , tuple (texture_cur .shape [2 :]))
211+ log . debug ("<- texture:" , tuple (texture_cur .shape [2 :]))
185212 del texture_cur
186213
187214 # Compute the seed image for this octave, sprinkling a bit of gaussian noise.
188215 size = result_sz [0 ] // octave , result_sz [1 ] // octave
189216 seed_img = F .interpolate (result_img , size , mode = "bicubic" , align_corners = False )
190217 seed_img += torch .empty_like (seed_img , dtype = torch .float32 ).normal_ (std = 0.2 )
191- print ("<- seed:" , tuple (seed_img .shape [2 :]), end = " \n \n " )
218+ log . debug ("<- seed:" , tuple (seed_img .shape [2 :]), " \n " )
192219 del result_img
193220
194221 # Now we can enable the automatic gradient computation to run the optimization.
195222 with torch .enable_grad ():
196- for _ , result_img in synth .run (seed_img , critics ):
223+ for _ , result_img in synth .run (log , seed_img , critics ):
197224 pass
198225 del synth
199226
@@ -204,8 +231,9 @@ def process_image(config, source):
204231
205232def main ():
206233 # Parse the command-line options based on the script's documentation.
207- print (ansi .PINK + " " + __doc__ [:356 ] + ansi .ENDC )
208234 config = docopt .docopt (__doc__ [356 :], version = __version__ )
235+ config ['--logger' ] = log = OutputLog (config )
236+ log .info (ansi .PINK + " " + __doc__ [:356 ] + ansi .ENDC )
209237
210238 # Determine which device to use by default, then set it up.
211239 if config ["--device" ] is None :
@@ -224,7 +252,8 @@ def main():
224252 # By default, disable autograd until the core optimization loop.
225253 with torch .no_grad ():
226254 try :
227- process_file (config , filename )
255+ result = process_file (config , filename )
256+ log .info (ansi .PINK + "\n => result:" , result , ansi .ENDC )
228257 except KeyboardInterrupt :
229258 print (ansi .PINK + "\n CTRL+C detected, interrupting..." + ansi .ENDC )
230259 break
0 commit comments