Skip to content

Commit 1d05ec0

Browse files
committed
🌳 Configurable logging with quiet and verbose mode.
1 parent 78b8910 commit 1d05ec0

File tree

1 file changed

+51
-22
lines changed

1 file changed

+51
-22
lines changed

src/texturize/__main__.py

Lines changed: 51 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
Usage:
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
1314
Examples:
@@ -19,12 +20,14 @@
1920
Options:
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
"""
@@ -55,6 +58,36 @@
5558
from . 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+
5891
class 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

131153
def 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

205232
def 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 + "\nCTRL+C detected, interrupting..." + ansi.ENDC)
230259
break

0 commit comments

Comments
 (0)