Skip to content

Commit c1257e2

Browse files
committed
🖨️ Generate multiple texture variations at the same time.
1 parent 1d05ec0 commit c1257e2

File tree

2 files changed

+37
-24
lines changed

2 files changed

+37
-24
lines changed

src/texturize/__main__.py

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
|_| |_|\___|\__,_|_| \__,_|_| \__\___/_/\_\\__|\__,_|_| |_/___\___|
77
88
Usage:
9-
texturize SOURCE... [--size=WxH] [--output=FILE] [--seed=SEED] [--device=DEVICE]
9+
texturize SOURCE... [--size=WxH] [--output=FILE] [--variations=V] [--seed=SEED]
1010
[--octaves=O] [--precision=P] [--iterations=I]
11-
[--quiet] [--verbose]
11+
[--device=DEVICE] [--quiet] [--verbose]
1212
texturize --help
1313
1414
Examples:
@@ -20,12 +20,14 @@
2020
Options:
2121
SOURCE Path to source image to use as texture.
2222
-s WxH, --size=WxH Output resolution as WIDTHxHEIGHT. [default: 640x480]
23-
-o FILE, --output=FILE Filename for saving the result. [default: {source}_gen.png]
23+
-o FILE, --output=FILE Filename for saving the result, includes format variables.
24+
[default: {source}_gen{variation}.png]
25+
--variations=V Number of images to generate at same time. [default: 1]
2426
--seed=SEED Configure the random number generation.
25-
--device=DEVICE Hardware to use, either "cpu" or "cuda".
2627
--octaves=O Number of octaves to process. [default: 5]
2728
--precision=P Set the quality for the optimization. [default: 1e-4]
2829
--iterations=I Maximum number of iterations each octave. [default: 99]
30+
--device=DEVICE Hardware to use, either "cpu" or "cuda".
2931
--quiet Suppress any messages going to stdout.
3032
--verbose Display more information on stdout.
3133
-h --help Show this message.
@@ -59,10 +61,9 @@
5961

6062

6163
class OutputLog:
62-
6364
def __init__(self, config):
64-
self.quiet = config['--quiet']
65-
self.verbose = config['--verbose']
65+
self.quiet = config["--quiet"]
66+
self.verbose = config["--verbose"]
6667

6768
def create_progress_bar(self, iterations):
6869
widgets = [
@@ -151,16 +152,21 @@ class ansi:
151152

152153

153154
def process_file(config, source):
154-
log = config['--logger']
155+
log = config["--logger"]
155156
for octave, result_img in process_image(config, io.load_image_from_file(source)):
156-
# Save the files for each octave to disk.
157-
filename = config["--output"].format(
158-
octave=octave, source=os.path.splitext(os.path.basename(source))[0]
159-
)
160-
result_img.save(filename)
161-
log.debug("\n=> output:", filename)
157+
filenames = []
158+
for i, result in enumerate(result_img):
159+
# Save the files for each octave to disk.
160+
filename = config["--output"].format(
161+
octave=octave,
162+
source=os.path.splitext(os.path.basename(source))[0],
163+
variation=i,
164+
)
165+
result.save(filename)
166+
log.debug("\n=> output:", filename)
167+
filenames.append(filename)
162168

163-
return filename
169+
return filenames
164170

165171

166172
@torch.no_grad()
@@ -179,15 +185,20 @@ def process_image(config, source):
179185

180186
# Generate the starting image for the optimization.
181187
octaves = int(config["--octaves"])
182-
result_sz = list(map(int, config["--size"].split("x")))[::-1]
188+
result_size = list(map(int, config["--size"].split("x")))[::-1]
183189
result_img = torch.empty(
184-
(1, 3, result_sz[0] // 2 ** (octaves + 1), result_sz[1] // 2 ** (octaves + 1)),
190+
(
191+
int(config["--variations"]),
192+
3,
193+
result_size[0] // 2 ** (octaves + 1),
194+
result_size[1] // 2 ** (octaves + 1),
195+
),
185196
device=config["--device"],
186197
dtype=torch.float32,
187198
).uniform_(0.4, 0.6)
188199

189200
# Coarse-to-fine rendering, number of octaves specified by user.
190-
log = config['--logger']
201+
log = config["--logger"]
191202
for i, octave in enumerate(2 ** s for s in range(octaves - 1, -1, -1)):
192203
# Each octave we start a new optimization process.
193204
synth = TextureSynthesizer(
@@ -212,7 +223,7 @@ def process_image(config, source):
212223
del texture_cur
213224

214225
# Compute the seed image for this octave, sprinkling a bit of gaussian noise.
215-
size = result_sz[0] // octave, result_sz[1] // octave
226+
size = result_size[0] // octave, result_size[1] // octave
216227
seed_img = F.interpolate(result_img, size, mode="bicubic", align_corners=False)
217228
seed_img += torch.empty_like(seed_img, dtype=torch.float32).normal_(std=0.2)
218229
log.debug("<- seed:", tuple(seed_img.shape[2:]), "\n")
@@ -224,15 +235,17 @@ def process_image(config, source):
224235
pass
225236
del synth
226237

227-
yield octave, io.save_tensor_to_image(
228-
F.interpolate(result_img, size=result_sz, mode="nearest").cpu()
229-
)
238+
output_img = F.interpolate(result_img, size=result_size, mode="nearest").cpu()
239+
yield octave, [
240+
io.save_tensor_to_image(output_img[j : j + 1])
241+
for j in range(output_img.shape[0])
242+
]
230243

231244

232245
def main():
233246
# Parse the command-line options based on the script's documentation.
234247
config = docopt.docopt(__doc__[356:], version=__version__)
235-
config['--logger'] = log = OutputLog(config)
248+
config["--logger"] = log = OutputLog(config)
236249
log.info(ansi.PINK + " " + __doc__[:356] + ansi.ENDC)
237250

238251
# Determine which device to use by default, then set it up.

src/texturize/critics.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def __init__(self, layer, offset: float = -1.0):
2121

2222
def evaluate(self, features):
2323
current = self._prepare_gram(features)
24-
yield 1e4 * F.mse_loss(current, self.gram, reduction="mean")
24+
yield 1e4 * F.mse_loss(current, self.gram.expand_as(current), reduction="mean")
2525

2626
def from_features(self, features):
2727
self.gram = self._prepare_gram(features)

0 commit comments

Comments
 (0)