Skip to content

Commit afee7f9

Browse files
committed
Merge branch 'development' of github.com:deNULL/stable-diffusion into deNULL-development
2 parents 6531446 + 171f8db commit afee7f9

File tree

3 files changed

+100
-115
lines changed

3 files changed

+100
-115
lines changed

ldm/dream/server.py

Lines changed: 82 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,59 @@
1+
import argparse
12
import json
23
import base64
34
import mimetypes
45
import os
56
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
6-
from ldm.dream.pngwriter import PngWriter
7+
from ldm.dream.pngwriter import PngWriter, PromptFormatter
78
from threading import Event
89

10+
def build_opt(post_data, seed, gfpgan_model_exists):
11+
opt = argparse.Namespace()
12+
setattr(opt, 'prompt', post_data['prompt'])
13+
setattr(opt, 'init_img', post_data['initimg'])
14+
setattr(opt, 'strength', float(post_data['strength']))
15+
setattr(opt, 'iterations', int(post_data['iterations']))
16+
setattr(opt, 'steps', int(post_data['steps']))
17+
setattr(opt, 'width', int(post_data['width']))
18+
setattr(opt, 'height', int(post_data['height']))
19+
setattr(opt, 'seamless', 'seamless' in post_data)
20+
setattr(opt, 'fit', 'fit' in post_data)
21+
setattr(opt, 'mask', 'mask' in post_data)
22+
setattr(opt, 'invert_mask', 'invert_mask' in post_data)
23+
setattr(opt, 'cfg_scale', float(post_data['cfg_scale']))
24+
setattr(opt, 'sampler_name', post_data['sampler_name'])
25+
setattr(opt, 'gfpgan_strength', float(post_data['gfpgan_strength']) if gfpgan_model_exists else 0)
26+
setattr(opt, 'upscale', [int(post_data['upscale_level']), float(post_data['upscale_strength'])] if post_data['upscale_level'] != '' else None)
27+
setattr(opt, 'progress_images', 'progress_images' in post_data)
28+
setattr(opt, 'seed', seed if int(post_data['seed']) == -1 else int(post_data['seed']))
29+
setattr(opt, 'variation_amount', float(post_data['variation_amount']) if int(post_data['seed']) != -1 else 0)
30+
setattr(opt, 'with_variations', [])
31+
32+
broken = False
33+
if int(post_data['seed']) != -1 and post_data['with_variations'] != '':
34+
for part in post_data['with_variations'].split(','):
35+
seed_and_weight = part.split(':')
36+
if len(seed_and_weight) != 2:
37+
print(f'could not parse with_variation part "{part}"')
38+
broken = True
39+
break
40+
try:
41+
seed = int(seed_and_weight[0])
42+
weight = float(seed_and_weight[1])
43+
except ValueError:
44+
print(f'could not parse with_variation part "{part}"')
45+
broken = True
46+
break
47+
opt.with_variations.append([seed, weight])
48+
49+
if broken:
50+
raise CanceledException
51+
52+
if len(opt.with_variations) == 0:
53+
opt.with_variations = None
54+
55+
return opt
56+
957
class CanceledException(Exception):
1058
pass
1159

@@ -81,57 +129,15 @@ def do_POST(self):
81129

82130
content_length = int(self.headers['Content-Length'])
83131
post_data = json.loads(self.rfile.read(content_length))
84-
prompt = post_data['prompt']
85-
initimg = post_data['initimg']
86-
strength = float(post_data['strength'])
87-
iterations = int(post_data['iterations'])
88-
steps = int(post_data['steps'])
89-
width = int(post_data['width'])
90-
height = int(post_data['height'])
91-
fit = 'fit' in post_data
92-
seamless = 'seamless' in post_data
93-
cfgscale = float(post_data['cfgscale'])
94-
sampler_name = post_data['sampler']
95-
variation_amount = float(post_data['variation_amount'])
96-
with_variations = post_data['with_variations']
97-
gfpgan_strength = float(post_data['gfpgan_strength']) if gfpgan_model_exists else 0
98-
upscale_level = post_data['upscale_level']
99-
upscale_strength = post_data['upscale_strength']
100-
upscale = [int(upscale_level),float(upscale_strength)] if upscale_level != '' else None
101-
progress_images = 'progress_images' in post_data
102-
seed = None if int(post_data['seed']) == -1 else int(post_data['seed'])
103-
104-
if with_variations != '':
105-
parts = []
106-
broken = False
107-
for part in with_variations.split(','):
108-
seed_and_weight = part.split(':')
109-
if len(seed_and_weight) != 2:
110-
print(f'could not parse with_variation part "{part}"')
111-
broken = True
112-
break
113-
try:
114-
vseed = int(seed_and_weight[0])
115-
vweight = float(seed_and_weight[1])
116-
except ValueError:
117-
print(f'could not parse with_variation part "{part}"')
118-
broken = True
119-
break
120-
parts.append([vseed, vweight])
121-
if broken:
122-
raise CanceledException
123-
if len(parts) > 0:
124-
with_variations = parts
125-
else:
126-
with_variations = None
132+
opt = build_opt(post_data, self.model.seed, gfpgan_model_exists)
127133

128134
self.canceled.clear()
129-
print(f">> Request to generate with prompt: {prompt}")
135+
print(f">> Request to generate with prompt: {opt.prompt}")
130136
# In order to handle upscaled images, the PngWriter needs to maintain state
131137
# across images generated by each call to prompt2img(), so we define it in
132138
# the outer scope of image_done()
133139
config = post_data.copy() # Shallow copy
134-
config['initimg'] = config.pop('initimg_name','')
140+
config['initimg'] = config.pop('initimg_name', '')
135141

136142
images_generated = 0 # helps keep track of when upscaling is started
137143
images_upscaled = 0 # helps keep track of when upscaling is completed
@@ -144,9 +150,21 @@ def do_POST(self):
144150
# entry should not be inserted into the image list.
145151
def image_done(image, seed, upscaled=False):
146152
name = f'{prefix}.{seed}.png'
147-
path = pngwriter.save_image_and_prompt_to_png(image, f'{prompt} -S{seed}', name)
148-
149-
config['seed'] = seed
153+
iter_opt = argparse.Namespace(**vars(opt)) # copy
154+
if opt.variation_amount > 0:
155+
this_variation = [[seed, opt.variation_amount]]
156+
if opt.with_variations is None:
157+
iter_opt.with_variations = this_variation
158+
else:
159+
iter_opt.with_variations = opt.with_variations + this_variation
160+
iter_opt.variation_amount = 0
161+
elif opt.with_variations is None:
162+
iter_opt.seed = seed
163+
normalized_prompt = PromptFormatter(self.model, iter_opt).normalize_prompt()
164+
path = pngwriter.save_image_and_prompt_to_png(image, f'{normalized_prompt} -S{iter_opt.seed}', name)
165+
166+
if int(config['seed']) == -1:
167+
config['seed'] = seed
150168
# Append post_data to log, but only once!
151169
if not upscaled:
152170
with open(os.path.join(self.outdir, "dream_web_log.txt"), "a") as log:
@@ -157,24 +175,24 @@ def image_done(image, seed, upscaled=False):
157175
) + '\n',"utf-8"))
158176

159177
# control state of the "postprocessing..." message
160-
upscaling_requested = upscale or gfpgan_strength>0
178+
upscaling_requested = opt.upscale or opt.gfpgan_strength > 0
161179
nonlocal images_generated # NB: Is this bad python style? It is typical usage in a perl closure.
162180
nonlocal images_upscaled # NB: Is this bad python style? It is typical usage in a perl closure.
163181
if upscaled:
164182
images_upscaled += 1
165183
else:
166-
images_generated +=1
184+
images_generated += 1
167185
if upscaling_requested:
168186
action = None
169-
if images_generated >= iterations:
170-
if images_upscaled < iterations:
187+
if images_generated >= opt.iterations:
188+
if images_upscaled < opt.iterations:
171189
action = 'upscaling-started'
172190
else:
173191
action = 'upscaling-done'
174192
if action:
175-
x = images_upscaled+1
193+
x = images_upscaled + 1
176194
self.wfile.write(bytes(json.dumps(
177-
{'event':action,'processed_file_cnt':f'{x}/{iterations}'}
195+
{'event': action, 'processed_file_cnt': f'{x}/{opt.iterations}'}
178196
) + '\n',"utf-8"))
179197

180198
step_writer = PngWriter(os.path.join(self.outdir, "intermediates"))
@@ -187,60 +205,31 @@ def image_progress(sample, step):
187205
# since rendering images is moderately expensive, only render every 5th image
188206
# and don't bother with the last one, since it'll render anyway
189207
nonlocal step_index
190-
if progress_images and step % 5 == 0 and step < steps - 1:
208+
if opt.progress_images and step % 5 == 0 and step < opt.steps - 1:
191209
image = self.model.sample_to_image(sample)
192-
name = f'{prefix}.{seed}.{step_index}.png'
193-
metadata = f'{prompt} -S{seed} [intermediate]'
210+
name = f'{prefix}.{opt.seed}.{step_index}.png'
211+
metadata = f'{opt.prompt} -S{opt.seed} [intermediate]'
194212
path = step_writer.save_image_and_prompt_to_png(image, metadata, name)
195213
step_index += 1
196214
self.wfile.write(bytes(json.dumps(
197215
{'event': 'step', 'step': step + 1, 'url': path}
198216
) + '\n',"utf-8"))
199217

200218
try:
201-
if initimg is None:
219+
if opt.init_img is None:
202220
# Run txt2img
203-
self.model.prompt2image(prompt,
204-
iterations=iterations,
205-
cfg_scale = cfgscale,
206-
width = width,
207-
height = height,
208-
seed = seed,
209-
steps = steps,
210-
variation_amount = variation_amount,
211-
with_variations = with_variations,
212-
gfpgan_strength = gfpgan_strength,
213-
upscale = upscale,
214-
sampler_name = sampler_name,
215-
seamless = seamless,
216-
step_callback=image_progress,
217-
image_callback=image_done)
221+
self.model.prompt2image(**vars(opt), step_callback=image_progress, image_callback=image_done)
218222
else:
219223
# Decode initimg as base64 to temp file
220224
with open("./img2img-tmp.png", "wb") as f:
221-
initimg = initimg.split(",")[1] # Ignore mime type
225+
initimg = opt.init_img.split(",")[1] # Ignore mime type
222226
f.write(base64.b64decode(initimg))
227+
opt1 = argparse.Namespace(**vars(opt))
228+
opt1.init_img = "./img2img-tmp.png"
223229

224230
try:
225231
# Run img2img
226-
self.model.prompt2image(prompt,
227-
init_img = "./img2img-tmp.png",
228-
strength = strength,
229-
iterations = iterations,
230-
cfg_scale = cfgscale,
231-
seed = seed,
232-
steps = steps,
233-
variation_amount = variation_amount,
234-
with_variations = with_variations,
235-
sampler_name = sampler_name,
236-
width = width,
237-
height = height,
238-
fit = fit,
239-
seamless = seamless,
240-
gfpgan_strength=gfpgan_strength,
241-
upscale = upscale,
242-
step_callback=image_progress,
243-
image_callback=image_done)
232+
self.model.prompt2image(**vars(opt1), step_callback=image_progress, image_callback=image_done)
244233
finally:
245234
# Remove the temp file
246235
os.remove("./img2img-tmp.png")

static/dream_web/index.html

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,21 @@ <h1>Stable Diffusion Dream Server</h1>
3030
<input value="1" type="number" id="iterations" name="iterations" size="4">
3131
<label for="steps">Steps:</label>
3232
<input value="50" type="number" id="steps" name="steps">
33-
<label for="cfgscale">Cfg Scale:</label>
34-
<input value="7.5" type="number" id="cfgscale" name="cfgscale" step="any">
35-
<label for="sampler">Sampler:</label>
36-
<select id="sampler" name="sampler" value="k_lms">
33+
<label for="cfg_scale">Cfg Scale:</label>
34+
<input value="7.5" type="number" id="cfg_scale" name="cfg_scale" step="any">
35+
<label for="sampler_name">Sampler:</label>
36+
<select id="sampler_name" name="sampler_name" value="k_lms">
3737
<option value="ddim">DDIM</option>
3838
<option value="plms">PLMS</option>
3939
<option value="k_lms" selected>KLMS</option>
4040
<option value="k_dpm_2">KDPM_2</option>
4141
<option value="k_dpm_2_a">KDPM_2A</option>
4242
<option value="k_euler">KEULER</option>
43-
<option value="k_euler_a">KEULER_A</option>
43+
<option value="k_euler_a">KEULER_A</option>
4444
<option value="k_heun">KHEUN</option>
4545
</select>
4646
<input type="checkbox" name="seamless" id="seamless">
47-
<label for="seamless">Seamless circular tiling</label>
47+
<label for="seamless">Seamless circular tiling</label>
4848
<br>
4949
<label title="Set to multiple of 64" for="width">Width:</label>
5050
<select id="width" name="width" value="512">

static/dream_web/index.js

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,13 @@ function toBase64(file) {
99

1010
function appendOutput(src, seed, config) {
1111
let outputNode = document.createElement("figure");
12-
let altText = seed.toString() + " | " + config.prompt;
12+
13+
let variations = config.with_variations;
14+
if (config.variation_amount > 0) {
15+
variations = (variations ? variations + ',' : '') + seed + ':' + config.variation_amount;
16+
}
17+
let baseseed = (config.with_variations || config.variation_amount > 0) ? config.seed : seed;
18+
let altText = baseseed + ' | ' + (variations ? variations + ' | ' : '') + config.prompt;
1319

1420
// img needs width and height for lazy loading to work
1521
const figureContents = `
@@ -25,7 +31,7 @@ function appendOutput(src, seed, config) {
2531
`;
2632

2733
outputNode.innerHTML = figureContents;
28-
let figcaption = outputNode.querySelector('figcaption')
34+
let figcaption = outputNode.querySelector('figcaption');
2935

3036
// Reload image config
3137
figcaption.addEventListener('click', () => {
@@ -34,21 +40,11 @@ function appendOutput(src, seed, config) {
3440
if (k == 'initimg') { continue; }
3541
form.querySelector(`*[name=${k}]`).value = config[k];
3642
}
37-
if (config.variation_amount > 0 || config.with_variations != '') {
38-
document.querySelector("#seed").value = config.seed;
39-
} else {
40-
document.querySelector("#seed").value = seed;
41-
}
4243

43-
if (config.variation_amount > 0) {
44-
let oldVarAmt = document.querySelector("#variation_amount").value
45-
let oldVariations = document.querySelector("#with_variations").value
46-
let varSep = ''
47-
document.querySelector("#variation_amount").value = 0;
48-
if (document.querySelector("#with_variations").value != '') {
49-
varSep = ","
50-
}
51-
document.querySelector("#with_variations").value = oldVariations + varSep + seed + ':' + config.variation_amount
44+
document.querySelector("#seed").value = baseseed;
45+
document.querySelector("#with_variations").value = variations || '';
46+
if (document.querySelector("#variation_amount").value <= 0) {
47+
document.querySelector("#variation_amount").value = 0.2;
5248
}
5349

5450
saveFields(document.querySelector("#generate-form"));

0 commit comments

Comments
 (0)