Spaces:
Running on Zero
Running on Zero
Commit ·
ca50d09
1
Parent(s): a532114
Add 2K/4K resolution selector to Advanced Settings
Browse files- Add `_resolve_output_dimensions` helper to encapsulate max_dim parsing
- Wire `max_output_dim` hidden Gradio Textbox (defaults to MAX_OUTPUT_DIM) through infer
- Add res-btn toggle group in Advanced Settings panel (HTML + CSS)
- Track selection in `window.__setResolution` / `window.__selectedMaxDim` (gallery.js)
- Pass selected resolution through run_preprocess.js to the backend
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- app.py +9 -3
- static/app.css +10 -0
- static/gallery.js +7 -0
- static/run_preprocess.js +3 -2
- templates/app.html +7 -0
app.py
CHANGED
|
@@ -263,6 +263,11 @@ def _resolve_seed(seed: int, randomize_seed: bool) -> int:
|
|
| 263 |
return random.randint(0, MAX_SEED) if randomize_seed else seed
|
| 264 |
|
| 265 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 266 |
def _spawn_log(pil_images, result_image, prompt, seed, steps, guidance_scale,
|
| 267 |
width, height, duration, success, error=""):
|
| 268 |
threading.Thread(
|
|
@@ -298,7 +303,7 @@ with open("templates/app.html") as _f:
|
|
| 298 |
# ── Gradio blocks ──────────────────────────────────────────────────────────────
|
| 299 |
|
| 300 |
@spaces.GPU
|
| 301 |
-
def infer(images_b64_json, prompt, seed, randomize_seed, guidance_scale, steps, progress=gr.Progress(track_tqdm=True)):
|
| 302 |
_cuda_ok = torch.cuda.is_available()
|
| 303 |
timer = _InferTimer(_cuda_ok)
|
| 304 |
t0 = time.perf_counter()
|
|
@@ -327,7 +332,7 @@ def infer(images_b64_json, prompt, seed, randomize_seed, guidance_scale, steps,
|
|
| 327 |
|
| 328 |
seed = _resolve_seed(seed, randomize_seed)
|
| 329 |
generator = torch.Generator(device=device).manual_seed(seed)
|
| 330 |
-
width, height =
|
| 331 |
print(f"[infer] input={pil_images[0].size}, output={width}x{height}, seed={seed}")
|
| 332 |
|
| 333 |
# Per-step callback: syncs the GPU then records a CUDA event so elapsed_time()
|
|
@@ -387,6 +392,7 @@ with gr.Blocks() as demo:
|
|
| 387 |
randomize_seed = gr.Checkbox(value=True, elem_id="gradio-randomize", elem_classes="hidden-input", container=False)
|
| 388 |
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)
|
| 389 |
steps = gr.Slider(minimum=1, maximum=50, step=1, value=4, elem_id="gradio-steps", elem_classes="hidden-input", container=False)
|
|
|
|
| 390 |
result = gr.Image(elem_id="gradio-result", elem_classes="hidden-input", container=False, format="png")
|
| 391 |
|
| 392 |
example_idx = gr.Textbox(value="", elem_id="example-idx-input", elem_classes="hidden-input", container=False)
|
|
@@ -402,7 +408,7 @@ with gr.Blocks() as demo:
|
|
| 402 |
|
| 403 |
run_btn.click(
|
| 404 |
fn=infer,
|
| 405 |
-
inputs=[hidden_images_b64, prompt, seed, randomize_seed, guidance_scale, steps],
|
| 406 |
outputs=[result, seed],
|
| 407 |
js=run_preprocess_js,
|
| 408 |
)
|
|
|
|
| 263 |
return random.randint(0, MAX_SEED) if randomize_seed else seed
|
| 264 |
|
| 265 |
|
| 266 |
+
def _resolve_output_dimensions(image: Image.Image, max_output_dim) -> tuple:
|
| 267 |
+
max_dim = int(max_output_dim) if str(max_output_dim).strip() else MAX_OUTPUT_DIM
|
| 268 |
+
return compute_output_dimensions(*image.size, max_dim=max_dim)
|
| 269 |
+
|
| 270 |
+
|
| 271 |
def _spawn_log(pil_images, result_image, prompt, seed, steps, guidance_scale,
|
| 272 |
width, height, duration, success, error=""):
|
| 273 |
threading.Thread(
|
|
|
|
| 303 |
# ── Gradio blocks ──────────────────────────────────────────────────────────────
|
| 304 |
|
| 305 |
@spaces.GPU
|
| 306 |
+
def infer(images_b64_json, prompt, seed, randomize_seed, guidance_scale, steps, max_output_dim, progress=gr.Progress(track_tqdm=True)):
|
| 307 |
_cuda_ok = torch.cuda.is_available()
|
| 308 |
timer = _InferTimer(_cuda_ok)
|
| 309 |
t0 = time.perf_counter()
|
|
|
|
| 332 |
|
| 333 |
seed = _resolve_seed(seed, randomize_seed)
|
| 334 |
generator = torch.Generator(device=device).manual_seed(seed)
|
| 335 |
+
width, height = _resolve_output_dimensions(pil_images[0], max_output_dim)
|
| 336 |
print(f"[infer] input={pil_images[0].size}, output={width}x{height}, seed={seed}")
|
| 337 |
|
| 338 |
# Per-step callback: syncs the GPU then records a CUDA event so elapsed_time()
|
|
|
|
| 392 |
randomize_seed = gr.Checkbox(value=True, elem_id="gradio-randomize", elem_classes="hidden-input", container=False)
|
| 393 |
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)
|
| 394 |
steps = gr.Slider(minimum=1, maximum=50, step=1, value=4, elem_id="gradio-steps", elem_classes="hidden-input", container=False)
|
| 395 |
+
max_output_dim = gr.Textbox(value=str(MAX_OUTPUT_DIM), elem_id="gradio-max-output-dim", elem_classes="hidden-input", container=False)
|
| 396 |
result = gr.Image(elem_id="gradio-result", elem_classes="hidden-input", container=False, format="png")
|
| 397 |
|
| 398 |
example_idx = gr.Textbox(value="", elem_id="example-idx-input", elem_classes="hidden-input", container=False)
|
|
|
|
| 408 |
|
| 409 |
run_btn.click(
|
| 410 |
fn=infer,
|
| 411 |
+
inputs=[hidden_images_b64, prompt, seed, randomize_seed, guidance_scale, steps, max_output_dim],
|
| 412 |
outputs=[result, seed],
|
| 413 |
js=run_preprocess_js,
|
| 414 |
)
|
static/app.css
CHANGED
|
@@ -301,6 +301,16 @@ body:not(.dark) #custom-run-btn *{color:#ffffff!important;-webkit-text-fill-colo
|
|
| 301 |
.checkbox-row{display:flex;align-items:center;gap:8px;font-size:13px;color:#a1a1aa}
|
| 302 |
.checkbox-row input[type="checkbox"]{accent-color:#ff0080;width:16px;height:16px;cursor:pointer}
|
| 303 |
.checkbox-row label{color:#a1a1aa;font-size:13px;cursor:pointer}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 304 |
|
| 305 |
.app-statusbar{
|
| 306 |
background:#280018;border-top:1px solid #4d0030;padding:6px 20px;
|
|
|
|
| 301 |
.checkbox-row{display:flex;align-items:center;gap:8px;font-size:13px;color:#a1a1aa}
|
| 302 |
.checkbox-row input[type="checkbox"]{accent-color:#ff0080;width:16px;height:16px;cursor:pointer}
|
| 303 |
.checkbox-row label{color:#a1a1aa;font-size:13px;cursor:pointer}
|
| 304 |
+
.res-row{display:flex;align-items:center;gap:10px;min-height:28px}
|
| 305 |
+
.res-row label{font-size:13px;font-weight:500;color:#a1a1aa;min-width:72px;flex-shrink:0}
|
| 306 |
+
.res-btn-group{display:flex;gap:4px}
|
| 307 |
+
.res-btn{
|
| 308 |
+
padding:4px 16px;font-size:12px;font-weight:600;border-radius:6px;cursor:pointer;
|
| 309 |
+
border:1px solid #4d0030;background:transparent;color:#71717a;
|
| 310 |
+
font-family:'Inter',sans-serif;transition:all .15s;
|
| 311 |
+
}
|
| 312 |
+
.res-btn.active{background:rgba(255,0,128,.2);border-color:rgba(255,0,128,.5);color:#ff4da6}
|
| 313 |
+
.res-btn:hover:not(.active){background:rgba(255,0,128,.1);border-color:rgba(255,0,128,.3);color:#a1a1aa}
|
| 314 |
|
| 315 |
.app-statusbar{
|
| 316 |
background:#280018;border-top:1px solid #4d0030;padding:6px 20px;
|
static/gallery.js
CHANGED
|
@@ -282,6 +282,13 @@ function init() {
|
|
| 282 |
|
| 283 |
if (runBtnEl) runBtnEl.addEventListener('click', () => window.__clickGradioRunBtn());
|
| 284 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 285 |
renderGallery();
|
| 286 |
updateCounts();
|
| 287 |
}
|
|
|
|
| 282 |
|
| 283 |
if (runBtnEl) runBtnEl.addEventListener('click', () => window.__clickGradioRunBtn());
|
| 284 |
|
| 285 |
+
window.__selectedMaxDim = 2048;
|
| 286 |
+
window.__setResolution = function(dim, btn) {
|
| 287 |
+
window.__selectedMaxDim = dim;
|
| 288 |
+
document.querySelectorAll('.res-btn').forEach(b => b.classList.remove('active'));
|
| 289 |
+
if (btn) btn.classList.add('active');
|
| 290 |
+
};
|
| 291 |
+
|
| 292 |
renderGallery();
|
| 293 |
updateCounts();
|
| 294 |
}
|
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 |
-
|
|
|
|
| 8 |
}
|
|
|
|
| 1 |
+
(imgs, p, s, rs, gs, st, mod) => {
|
| 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 maxDim = String(window.__selectedMaxDim || 2048);
|
| 8 |
+
return [imgsJson, promptVal, s, rs, gs, st, maxDim];
|
| 9 |
}
|
templates/app.html
CHANGED
|
@@ -123,6 +123,13 @@
|
|
| 123 |
<div class="settings-group">
|
| 124 |
<div class="settings-group-title">Advanced Settings</div>
|
| 125 |
<div class="settings-group-body">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
<div class="slider-row">
|
| 127 |
<label>Seed</label>
|
| 128 |
<input type="range" id="custom-seed" min="0" max="2147483647" step="1" value="0">
|
|
|
|
| 123 |
<div class="settings-group">
|
| 124 |
<div class="settings-group-title">Advanced Settings</div>
|
| 125 |
<div class="settings-group-body">
|
| 126 |
+
<div class="res-row">
|
| 127 |
+
<label>Resolution</label>
|
| 128 |
+
<div class="res-btn-group">
|
| 129 |
+
<button type="button" class="res-btn active" onclick="window.__setResolution(2048, this)">2K</button>
|
| 130 |
+
<button type="button" class="res-btn" onclick="window.__setResolution(4096, this)">4K</button>
|
| 131 |
+
</div>
|
| 132 |
+
</div>
|
| 133 |
<div class="slider-row">
|
| 134 |
<label>Seed</label>
|
| 135 |
<input type="range" id="custom-seed" min="0" max="2147483647" step="1" value="0">
|