someone-in-the-world Claude Sonnet 4.6 commited on
Commit
f333d01
Β·
1 Parent(s): 0ce14db

Add Fast / High-Detail mode toggle (768px vs 2048px output)

Browse files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (5) hide show
  1. app.py +9 -7
  2. dimensions.py +1 -0
  3. static/app.css +13 -0
  4. static/run_preprocess.js +3 -2
  5. templates/app.html +14 -0
app.py CHANGED
@@ -33,7 +33,7 @@ torch.backends.cudnn.allow_tf32 = True
33
  print("[startup] TF32 enabled", flush=True)
34
 
35
  print("[startup] importing dimensions...", flush=True)
36
- from dimensions import compute_output_dimensions
37
  print("[startup] importing diffusers...", flush=True)
38
  from diffusers import FlowMatchEulerDiscreteScheduler
39
  print("[startup] importing QwenImageEditPlusPipeline...", flush=True)
@@ -207,11 +207,11 @@ def b64_to_pil_list(b64_json_str):
207
  return pil_images
208
 
209
 
210
- def update_dimensions_on_upload(image):
211
  if image is None:
212
- return MAX_OUTPUT_DIM, MAX_OUTPUT_DIM
213
  w, h = image.size
214
- return compute_output_dimensions(w, h)
215
 
216
 
217
  class _InferTimer:
@@ -330,7 +330,7 @@ with open("templates/app.html") as _f:
330
  # ── Gradio blocks ──────────────────────────────────────────────────────────────
331
 
332
  @spaces.GPU
333
- def infer(images_b64_json, prompt, seed, randomize_seed, guidance_scale, steps, progress=gr.Progress(track_tqdm=True)):
334
  _cuda_ok = torch.cuda.is_available()
335
  timer = _InferTimer(_cuda_ok)
336
  t0 = time.perf_counter()
@@ -359,7 +359,8 @@ def infer(images_b64_json, prompt, seed, randomize_seed, guidance_scale, steps,
359
 
360
  seed = _resolve_seed(seed, randomize_seed)
361
  generator = torch.Generator(device=device).manual_seed(seed)
362
- width, height = update_dimensions_on_upload(pil_images[0])
 
363
  print(f"[infer] input={pil_images[0].size}, output={width}x{height}, seed={seed}")
364
 
365
  # Per-step callback: syncs the GPU then records a CUDA event so elapsed_time()
@@ -419,6 +420,7 @@ with gr.Blocks() as demo:
419
  randomize_seed = gr.Checkbox(value=True, elem_id="gradio-randomize", elem_classes="hidden-input", container=False)
420
  guidance_scale = gr.Slider(minimum=1.0, maximum=10.0, step=0.1, value=1.0, elem_id="gradio-guidance", elem_classes="hidden-input", container=False)
421
  steps = gr.Slider(minimum=1, maximum=50, step=1, value=4, elem_id="gradio-steps", elem_classes="hidden-input", container=False)
 
422
  result = gr.Image(elem_id="gradio-result", elem_classes="hidden-input", container=False, format="png")
423
 
424
  example_idx = gr.Textbox(value="", elem_id="example-idx-input", elem_classes="hidden-input", container=False)
@@ -434,7 +436,7 @@ with gr.Blocks() as demo:
434
 
435
  run_btn.click(
436
  fn=infer,
437
- inputs=[hidden_images_b64, prompt, seed, randomize_seed, guidance_scale, steps],
438
  outputs=[result, seed],
439
  js=run_preprocess_js,
440
  )
 
33
  print("[startup] TF32 enabled", flush=True)
34
 
35
  print("[startup] importing dimensions...", flush=True)
36
+ from dimensions import compute_output_dimensions, MAX_OUTPUT_DIM_FAST
37
  print("[startup] importing diffusers...", flush=True)
38
  from diffusers import FlowMatchEulerDiscreteScheduler
39
  print("[startup] importing QwenImageEditPlusPipeline...", flush=True)
 
207
  return pil_images
208
 
209
 
210
+ def update_dimensions_on_upload(image, max_dim):
211
  if image is None:
212
+ return max_dim, max_dim
213
  w, h = image.size
214
+ return compute_output_dimensions(w, h, max_dim)
215
 
216
 
217
  class _InferTimer:
 
330
  # ── Gradio blocks ──────────────────────────────────────────────────────────────
331
 
332
  @spaces.GPU
333
+ def infer(images_b64_json, prompt, seed, randomize_seed, guidance_scale, steps, mode="fast", progress=gr.Progress(track_tqdm=True)):
334
  _cuda_ok = torch.cuda.is_available()
335
  timer = _InferTimer(_cuda_ok)
336
  t0 = time.perf_counter()
 
359
 
360
  seed = _resolve_seed(seed, randomize_seed)
361
  generator = torch.Generator(device=device).manual_seed(seed)
362
+ max_dim = MAX_OUTPUT_DIM_FAST if mode == "fast" else MAX_OUTPUT_DIM
363
+ width, height = update_dimensions_on_upload(pil_images[0], max_dim)
364
  print(f"[infer] input={pil_images[0].size}, output={width}x{height}, seed={seed}")
365
 
366
  # Per-step callback: syncs the GPU then records a CUDA event so elapsed_time()
 
420
  randomize_seed = gr.Checkbox(value=True, elem_id="gradio-randomize", elem_classes="hidden-input", container=False)
421
  guidance_scale = gr.Slider(minimum=1.0, maximum=10.0, step=0.1, value=1.0, elem_id="gradio-guidance", elem_classes="hidden-input", container=False)
422
  steps = gr.Slider(minimum=1, maximum=50, step=1, value=4, elem_id="gradio-steps", elem_classes="hidden-input", container=False)
423
+ mode = gr.Textbox(value="fast", elem_id="gradio-mode", elem_classes="hidden-input", container=False)
424
  result = gr.Image(elem_id="gradio-result", elem_classes="hidden-input", container=False, format="png")
425
 
426
  example_idx = gr.Textbox(value="", elem_id="example-idx-input", elem_classes="hidden-input", container=False)
 
436
 
437
  run_btn.click(
438
  fn=infer,
439
+ inputs=[hidden_images_b64, prompt, seed, randomize_seed, guidance_scale, steps, mode],
440
  outputs=[result, seed],
441
  js=run_preprocess_js,
442
  )
dimensions.py CHANGED
@@ -1,4 +1,5 @@
1
  MAX_OUTPUT_DIM = 2048
 
2
 
3
 
4
  def compute_output_dimensions(w, h, max_dim=MAX_OUTPUT_DIM):
 
1
  MAX_OUTPUT_DIM = 2048
2
+ MAX_OUTPUT_DIM_FAST = 768
3
 
4
 
5
  def compute_output_dimensions(w, h, max_dim=MAX_OUTPUT_DIM):
static/app.css CHANGED
@@ -206,6 +206,19 @@ body:not(.dark) .modern-tb-btn .tb-svg,body:not(.dark) .modern-tb-btn .tb-svg *{
206
  .toast-notification .toast-icon{font-size:16px;line-height:1}
207
  .toast-notification .toast-text{line-height:1.3}
208
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  .btn-run{
210
  display:flex;align-items:center;justify-content:center;gap:8px;width:100%;
211
  background:linear-gradient(135deg,#ff0080,#cc0066);border:none;border-radius:10px;
 
206
  .toast-notification .toast-icon{font-size:16px;line-height:1}
207
  .toast-notification .toast-text{line-height:1.3}
208
 
209
+ .mode-toggle-wrap{display:flex;gap:8px;padding:8px 20px 0}
210
+ .mode-btn{
211
+ flex:1;padding:8px 12px;border-radius:8px;border:1px solid #4d0030;background:transparent;
212
+ cursor:pointer;font-size:13px;font-weight:600;font-family:'Inter',sans-serif;
213
+ color:#a1a1aa;transition:all .2s ease;
214
+ }
215
+ .mode-btn-active{
216
+ background:linear-gradient(135deg,#ff0080,#cc0066);border-color:transparent;
217
+ color:#ffffff!important;-webkit-text-fill-color:#ffffff!important;
218
+ box-shadow:0 2px 8px rgba(255,0,128,.35);
219
+ }
220
+ .mode-btn:not(.mode-btn-active):hover{border-color:#ff0080;color:#ff0080}
221
+
222
  .btn-run{
223
  display:flex;align-items:center;justify-content:center;gap:8px;width:100%;
224
  background:linear-gradient(135deg,#ff0080,#cc0066);border:none;border-radius:10px;
static/run_preprocess.js CHANGED
@@ -1,8 +1,9 @@
1
- (imgs, p, s, rs, gs, st) => {
2
  const images = window.__uploadedImages || [];
3
  const b64Array = images.map(img => img.b64);
4
  const imgsJson = JSON.stringify(b64Array);
5
  const promptEl = document.getElementById('custom-prompt-input');
6
  const promptVal = promptEl ? promptEl.value : p;
7
- return [imgsJson, promptVal, s, rs, gs, st];
 
8
  }
 
1
+ (imgs, p, s, rs, gs, st, m) => {
2
  const images = window.__uploadedImages || [];
3
  const b64Array = images.map(img => img.b64);
4
  const imgsJson = JSON.stringify(b64Array);
5
  const promptEl = document.getElementById('custom-prompt-input');
6
  const promptVal = promptEl ? promptEl.value : p;
7
+ const mode = window.__selectedMode || 'fast';
8
+ return [imgsJson, promptVal, s, rs, gs, st, mode];
9
  }
templates/app.html CHANGED
@@ -82,6 +82,11 @@
82
  </div>
83
  </div>
84
 
 
 
 
 
 
85
  <div style="padding:12px 20px;">
86
  <button id="custom-run-btn" class="btn-run">
87
  <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 23c-3.6 0-8-2.69-8-7.5 0-3.5 3-6.5 4.5-8 .27-.27.75-.08.75.28v2.44c0 .42.5.63.72.28C12.28 7.5 13 3 13 1c0-.42.48-.64.8-.35C18 4.5 20 9 20 12c0 5.5-3.5 11-8 11z"/></svg>
@@ -174,3 +179,12 @@
174
  </details>
175
 
176
  </div>
 
 
 
 
 
 
 
 
 
 
82
  </div>
83
  </div>
84
 
85
+ <div class="mode-toggle-wrap">
86
+ <button id="mode-btn-fast" class="mode-btn mode-btn-active" onclick="window.__setMode('fast')">Fast</button>
87
+ <button id="mode-btn-hd" class="mode-btn" onclick="window.__setMode('high_detail')">High-Detail</button>
88
+ </div>
89
+
90
  <div style="padding:12px 20px;">
91
  <button id="custom-run-btn" class="btn-run">
92
  <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 23c-3.6 0-8-2.69-8-7.5 0-3.5 3-6.5 4.5-8 .27-.27.75-.08.75.28v2.44c0 .42.5.63.72.28C12.28 7.5 13 3 13 1c0-.42.48-.64.8-.35C18 4.5 20 9 20 12c0 5.5-3.5 11-8 11z"/></svg>
 
179
  </details>
180
 
181
  </div>
182
+
183
+ <script>
184
+ window.__selectedMode = 'fast';
185
+ window.__setMode = function(m) {
186
+ window.__selectedMode = m;
187
+ document.getElementById('mode-btn-fast').classList.toggle('mode-btn-active', m === 'fast');
188
+ document.getElementById('mode-btn-hd').classList.toggle('mode-btn-active', m === 'high_detail');
189
+ };
190
+ </script>