Spaces:
Running on Zero
Running on Zero
Upload app.py
Browse files
app.py
CHANGED
|
@@ -68,7 +68,6 @@ from qwenimage.qwen_fa3_processor import QwenDoubleStreamAttnProcessorFA3
|
|
| 68 |
|
| 69 |
import Age_classifier
|
| 70 |
|
| 71 |
-
from esrgan import load_esrgan, upscale_to_pixels
|
| 72 |
from security import (
|
| 73 |
FloodGuard,
|
| 74 |
check_admin_password,
|
|
@@ -120,10 +119,40 @@ if torch.cuda.is_available():
|
|
| 120 |
MODEL_QWEN = "qwen"
|
| 121 |
MODEL_FLUX = "flux"
|
| 122 |
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
|
| 128 |
from diffusers.utils import logging as diffusers_logging
|
| 129 |
|
|
@@ -219,7 +248,7 @@ def _enable_vae_tiling(p, tile=None):
|
|
| 219 |
print("[vae] tiling applied:", {k: getattr(vae, k, None) for k in ("tile_sample_min_height", "tile_sample_min_width", "tile_sample_stride_height", "tile_sample_stride_width")})
|
| 220 |
|
| 221 |
|
| 222 |
-
QWEN_VAE_TILE = int(os.environ.get("QWEN_VAE_TILE", "
|
| 223 |
|
| 224 |
|
| 225 |
pipe = QwenImageEditPlusPipeline.from_pretrained(
|
|
@@ -256,20 +285,16 @@ if getattr(pipe, "text_encoder", None) is not None:
|
|
| 256 |
except Exception as e:
|
| 257 |
print(f"[qwen] text_encoder quantization FAILED, staying bf16 (~+8GB VRAM): {e}")
|
| 258 |
|
| 259 |
-
|
| 260 |
-
_enable_vae_tiling(pipe, QWEN_VAE_TILE)
|
| 261 |
-
else:
|
| 262 |
-
print("[vae] Qwen VAE tiling OFF (full-frame decode)")
|
| 263 |
|
| 264 |
pipe.transformer.__class__ = QwenImageTransformer2DModel
|
| 265 |
pipe.transformer.set_attn_processor(QwenDoubleStreamAttnProcessorFA3())
|
| 266 |
|
| 267 |
FLUX_REPO = "black-forest-labs/FLUX.2-klein-9B"
|
| 268 |
-
FLUX_REFINE_PROMPT = "An ultra-high-resolution masterpiece with exceptional sharpness and detail, featuring only a subtle, barely perceptible amount of noise on the skin’s surface. The image remains entirely unzoomed and unaltered, preserving its original composition and proportions, with lighting and shadows meticulously controlled to remain perfectly faithful to the original
|
| 269 |
FLUX_REFINE_GUIDANCE_SCALE = 1
|
| 270 |
FLUX_NUM_INFERENCE_STEPS = 4
|
| 271 |
-
|
| 272 |
-
FLUX_MAX_PIXELS = 2073600
|
| 273 |
|
| 274 |
flux_pipe = Flux2KleinPipeline.from_pretrained(FLUX_REPO, torch_dtype=dtype)
|
| 275 |
|
|
@@ -288,33 +313,6 @@ try:
|
|
| 288 |
except Exception as e:
|
| 289 |
print(f"[flux] quantization FAILED, continuing in bf16 (roughly 2x the VRAM/transfer time): {e}")
|
| 290 |
|
| 291 |
-
if hasattr(flux_pipe, "vae") and flux_pipe.vae is not None:
|
| 292 |
-
try:
|
| 293 |
-
flux_pipe.vae.enable_slicing()
|
| 294 |
-
except Exception:
|
| 295 |
-
pass
|
| 296 |
-
|
| 297 |
-
_flux_ip = getattr(flux_pipe, "image_processor", None)
|
| 298 |
-
if _flux_ip is not None and hasattr(_flux_ip, "_resize_to_target_area"):
|
| 299 |
-
_orig_resize_to_target_area = _flux_ip._resize_to_target_area
|
| 300 |
-
|
| 301 |
-
def _flux_resize_with_higher_cap(image, target_area):
|
| 302 |
-
if target_area == 1024 * 1024:
|
| 303 |
-
w, h = image.size
|
| 304 |
-
if w * h <= FLUX_MAX_PIXELS:
|
| 305 |
-
return image
|
| 306 |
-
return _orig_resize_to_target_area(image, FLUX_MAX_PIXELS)
|
| 307 |
-
return _orig_resize_to_target_area(image, target_area)
|
| 308 |
-
|
| 309 |
-
_flux_ip._resize_to_target_area = _flux_resize_with_higher_cap
|
| 310 |
-
print(f"[flux] reference-image cap raised: no downscale up to {FLUX_MAX_PIXELS} px")
|
| 311 |
-
else:
|
| 312 |
-
print("[flux] WARNING: image_processor._resize_to_target_area not found; "
|
| 313 |
-
"FLUX will use its built-in ~1MP reference-image downscale")
|
| 314 |
-
|
| 315 |
-
esrgan_model = load_esrgan(device)
|
| 316 |
-
print("[esrgan] RealESRGAN_x2plus loaded")
|
| 317 |
-
|
| 318 |
# FLUX VAE tiling is off by default because tiled encode/decode shifted skin colour.
|
| 319 |
if os.environ.get("FLUX_VAE_TILING", "0") == "1":
|
| 320 |
_enable_vae_tiling(flux_pipe)
|
|
@@ -323,6 +321,12 @@ gc.collect()
|
|
| 323 |
|
| 324 |
|
| 325 |
MAX_SEED = 2**31 - 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 326 |
_VALID_FORMATS = ("PNG", "JPG", "WebP")
|
| 327 |
|
| 328 |
GPU_DURATION_MIN = 10
|
|
@@ -342,10 +346,6 @@ def _to_int(value, lo, hi, default):
|
|
| 342 |
return max(lo, min(hi, v))
|
| 343 |
|
| 344 |
|
| 345 |
-
LIMIT_GUIDANCE_MIN = 1.0
|
| 346 |
-
LIMIT_GUIDANCE_MAX = 2.0
|
| 347 |
-
|
| 348 |
-
|
| 349 |
def _to_float(value, lo, hi, default):
|
| 350 |
try:
|
| 351 |
v = float(value)
|
|
@@ -356,38 +356,42 @@ def _to_float(value, lo, hi, default):
|
|
| 356 |
return max(lo, min(hi, v))
|
| 357 |
|
| 358 |
|
| 359 |
-
def
|
| 360 |
-
|
| 361 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 362 |
|
| 363 |
-
def _ratio_size(ratio, pixels=QWEN_TARGET_PIXELS, multiple=16):
|
| 364 |
-
width = (pixels * ratio) ** 0.5
|
| 365 |
-
height = width / ratio
|
| 366 |
-
return (
|
| 367 |
-
max(multiple, int(width) // multiple * multiple),
|
| 368 |
-
max(multiple, int(height) // multiple * multiple),
|
| 369 |
-
)
|
| 370 |
|
| 371 |
-
|
| 372 |
-
def _image_ratio(image_1, image_2):
|
| 373 |
src = image_1 if image_1 is not None else image_2
|
| 374 |
if src is None:
|
| 375 |
-
return
|
| 376 |
try:
|
| 377 |
with Image.open(src if isinstance(src, str) else src.name) as im:
|
| 378 |
-
|
| 379 |
except Exception:
|
| 380 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 381 |
|
| 382 |
|
| 383 |
-
def _resolve_size(
|
| 384 |
try:
|
| 385 |
-
if
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
|
|
|
|
|
|
|
|
|
| 389 |
pass
|
| 390 |
-
return
|
| 391 |
|
| 392 |
|
| 393 |
def _fit_size(width, height, max_pixels=FLUX_MAX_PIXELS, multiple=16):
|
|
@@ -403,7 +407,12 @@ def _fit_size(width, height, max_pixels=FLUX_MAX_PIXELS, multiple=16):
|
|
| 403 |
def _load_image(img):
|
| 404 |
if not isinstance(img, Image.Image):
|
| 405 |
img = Image.open(img if isinstance(img, str) else img.name)
|
| 406 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 407 |
|
| 408 |
|
| 409 |
def _sanitize_common(seed, gpu_duration, output_format):
|
|
@@ -447,6 +456,7 @@ def _generate_core(
|
|
| 447 |
num_inference_steps,
|
| 448 |
height,
|
| 449 |
width,
|
|
|
|
| 450 |
progress=gr.Progress(track_tqdm=True),
|
| 451 |
):
|
| 452 |
if randomize_seed:
|
|
@@ -535,7 +545,6 @@ def _finalize(res, image_1, image_2, prompt, output_format):
|
|
| 535 |
else:
|
| 536 |
img.save(output_path)
|
| 537 |
output_paths.append(output_path)
|
| 538 |
-
print(f"[out] resolution={img.width}x{img.height}")
|
| 539 |
log_generation(
|
| 540 |
path=output_path,
|
| 541 |
prompt=prompt,
|
|
@@ -550,49 +559,30 @@ def _finalize(res, image_1, image_2, prompt, output_format):
|
|
| 550 |
inputs=input_paths,
|
| 551 |
)
|
| 552 |
has_output = bool(output_paths)
|
| 553 |
-
return output_paths, seed, gr.update(visible=has_output)
|
| 554 |
|
| 555 |
|
| 556 |
-
def
|
| 557 |
-
def
|
| 558 |
-
|
| 559 |
-
q = _generate_core(MODEL_QWEN, **qwen_kwargs)
|
| 560 |
-
if not q["images"]:
|
| 561 |
-
return q, None
|
| 562 |
-
_free_vram_cache()
|
| 563 |
-
img = q["images"][0]
|
| 564 |
-
w, h = _round_size(*img.size, multiple=16)
|
| 565 |
-
f = _generate_core(
|
| 566 |
-
MODEL_FLUX, image_1=img, image_2=None, prompt=FLUX_REFINE_PROMPT,
|
| 567 |
-
seed=FLUX_SEED, randomize_seed=False,
|
| 568 |
-
true_guidance_scale=FLUX_REFINE_GUIDANCE_SCALE,
|
| 569 |
-
num_inference_steps=FLUX_NUM_INFERENCE_STEPS,
|
| 570 |
-
height=h, width=w,
|
| 571 |
-
progress=qwen_kwargs["progress"],
|
| 572 |
-
)
|
| 573 |
-
if scale_up and f["images"]:
|
| 574 |
-
_free_vram_cache()
|
| 575 |
-
f = {**f, "images": [upscale_to_pixels(esrgan_model, f["images"][0], scale_up)]}
|
| 576 |
-
return q, f
|
| 577 |
|
| 578 |
-
|
| 579 |
-
|
| 580 |
-
return spaces.GPU(duration=duration_seconds, size=size)(
|
| 581 |
|
| 582 |
|
| 583 |
-
|
| 584 |
|
| 585 |
|
| 586 |
-
def
|
| 587 |
requested = _to_int(gpu_duration, GPU_DURATION_MIN, GPU_DURATION_MAX, GPU_DURATION_DEFAULT)
|
| 588 |
chosen = next((d for d in _GPU_DURATION_STEPS if d >= requested), _GPU_DURATION_STEPS[-1])
|
| 589 |
size = gpu_size if gpu_size in GPU_SIZE_OPTIONS else GPU_SIZE_DEFAULT
|
| 590 |
-
gpu_fn =
|
| 591 |
|
| 592 |
-
def _call(image_1, image_2, prompt,
|
| 593 |
-
|
| 594 |
-
|
| 595 |
-
return _finalize({**q, "images": final["images"]}, image_1, image_2, prompt, output_format)
|
| 596 |
|
| 597 |
return _call
|
| 598 |
|
|
@@ -691,7 +681,7 @@ def _run_limited(request, seed, run):
|
|
| 691 |
ok, key, msg = rate_acquire(request)
|
| 692 |
if not ok:
|
| 693 |
show_rate_message(msg)
|
| 694 |
-
return gr.update(), seed, gr.update()
|
| 695 |
refund = False
|
| 696 |
try:
|
| 697 |
return run()
|
|
@@ -702,7 +692,7 @@ def _run_limited(request, seed, run):
|
|
| 702 |
rate_release(key, refund=refund)
|
| 703 |
|
| 704 |
|
| 705 |
-
def
|
| 706 |
image_1,
|
| 707 |
image_2,
|
| 708 |
prompt,
|
|
@@ -712,10 +702,11 @@ def run_qwen_and_flux(
|
|
| 712 |
num_inference_steps,
|
| 713 |
gpu_duration,
|
| 714 |
gpu_size,
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
|
| 718 |
-
|
|
|
|
| 719 |
output_format,
|
| 720 |
age_confirmed=False,
|
| 721 |
progress=gr.Progress(track_tqdm=True),
|
|
@@ -725,34 +716,77 @@ def run_qwen_and_flux(
|
|
| 725 |
raise gr.Error("Please tick the box to confirm you are 18 or older.")
|
| 726 |
|
| 727 |
if not (isinstance(prompt, str) and prompt.strip()):
|
| 728 |
-
return gr.update(), seed, gr.update()
|
| 729 |
|
| 730 |
def run():
|
| 731 |
clean_seed, duration, fmt = _sanitize_common(seed, gpu_duration, output_format)
|
| 732 |
-
width, height = _resolve_size(
|
| 733 |
clean_prompt = prompt if isinstance(prompt, str) else ""
|
| 734 |
-
qwen_kwargs = dict(
|
| 735 |
-
image_1=image_1, image_2=image_2, prompt=clean_prompt,
|
| 736 |
-
seed=clean_seed, randomize_seed=bool(randomize_seed),
|
| 737 |
-
true_guidance_scale=_to_float(true_guidance_scale, LIMIT_GUIDANCE_MIN, LIMIT_GUIDANCE_MAX, 1.0),
|
| 738 |
-
num_inference_steps=int(num_inference_steps),
|
| 739 |
-
height=height, width=width,
|
| 740 |
-
progress=progress,
|
| 741 |
-
)
|
| 742 |
-
upscale_pixels = next(
|
| 743 |
-
(UPSCALE_PIXELS[name] for name, on in (("High", scale_high), ("Medium", scale_medium), ("Basic", scale_basic)) if on),
|
| 744 |
-
0,
|
| 745 |
-
)
|
| 746 |
try:
|
| 747 |
_screen_inputs(request, clean_prompt, image_1, image_2)
|
| 748 |
-
return _screen_outputs(
|
| 749 |
-
|
| 750 |
-
|
| 751 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 752 |
except gr.Error as exc:
|
| 753 |
if not _is_age_block(exc):
|
| 754 |
raise
|
| 755 |
-
return [_block_image()], seed, gr.update(visible=False)
|
| 756 |
|
| 757 |
return _run_limited(request, seed, run)
|
| 758 |
|
|
@@ -764,6 +798,11 @@ def use_output_as_input(output_images):
|
|
| 764 |
|
| 765 |
|
| 766 |
def _sync_result_followups(output_images):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 767 |
return gr.update(visible=bool(output_images) and not _is_block_image(output_images))
|
| 768 |
|
| 769 |
|
|
@@ -781,6 +820,23 @@ css = """
|
|
| 781 |
}
|
| 782 |
#logo-title {
|
| 783 |
text-align: center;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 784 |
}
|
| 785 |
|
| 786 |
@keyframes ast-title-flash {
|
|
@@ -802,11 +858,6 @@ css = """
|
|
| 802 |
@media (max-width: 480px) {
|
| 803 |
#ast-main-title { font-size: 5.8vw !important; }
|
| 804 |
}
|
| 805 |
-
#logo-title h1#ast-nfaa-title {
|
| 806 |
-
margin: 0 -0.3em 0 0 !important;
|
| 807 |
-
padding: 0 0.3em 0 0 !important;
|
| 808 |
-
line-height: 1.15 !important;
|
| 809 |
-
}
|
| 810 |
#ast-nfaa-title {
|
| 811 |
font-style: italic;
|
| 812 |
display: inline-block;
|
|
@@ -826,7 +877,7 @@ css = """
|
|
| 826 |
#ast-notice {
|
| 827 |
width: 100%;
|
| 828 |
max-width: 560px;
|
| 829 |
-
margin:
|
| 830 |
padding: 1px 0 1px 14px !important;
|
| 831 |
border-left: 2px solid transparent;
|
| 832 |
border-image: linear-gradient(180deg, #FF3CAC, #7B2FF7, #00E5FF) 1;
|
|
@@ -875,57 +926,63 @@ css = """
|
|
| 875 |
background-color: #FF1010 !important;
|
| 876 |
border-color: #FF1010 !important;
|
| 877 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 878 |
#seed-slider .wrap.generating,
|
| 879 |
#seed-slider .wrap.pending,
|
| 880 |
#seed-slider .progress-text,
|
| 881 |
#seed-slider .progress-level {
|
| 882 |
display: none !important;
|
| 883 |
}
|
| 884 |
-
|
| 885 |
-
|
| 886 |
-
|
| 887 |
-
|
| 888 |
-
|
| 889 |
-
|
| 890 |
-
|
| 891 |
-
|
| 892 |
-
|
| 893 |
-
|
| 894 |
-
|
| 895 |
-
|
| 896 |
-
|
| 897 |
-
|
| 898 |
-
|
| 899 |
-
|
| 900 |
-
|
| 901 |
-
|
| 902 |
-
|
| 903 |
-
|
| 904 |
-
|
| 905 |
-
|
| 906 |
-
|
| 907 |
-
|
| 908 |
-
|
| 909 |
-
|
| 910 |
-
|
| 911 |
-
|
| 912 |
-
|
| 913 |
-
|
| 914 |
-
|
| 915 |
-
|
| 916 |
-
|
| 917 |
-
|
| 918 |
-
|
| 919 |
-
css += _ratio_css()
|
| 920 |
-
css += f"""
|
| 921 |
-
#output-format {{ background: transparent !important; border: none !important; box-shadow: none !important; padding: 0 !important; }}
|
| 922 |
-
#output-format .wrap {{ display: flex !important; justify-content: center !important; gap: 1px !important; background: transparent !important; padding: 0 !important; margin: 0 !important; }}
|
| 923 |
-
#output-format label {{ flex: 1 1 0 !important; min-width: 0 !important; justify-content: center !important; align-items: center !important; height: 38px !important; min-height: 0 !important; margin: 0 !important; padding: 0 8px !important; border: none !important; border-radius: 0 !important; box-shadow: none !important; background: var(--block-background-fill) !important; }}
|
| 924 |
-
#output-format label:has(input:checked), #output-format label.selected, #lanczos-row .block:has(input:checked) {{ background-image: linear-gradient(color-mix(in srgb, {_RATIO_ACCENT} 14%, transparent), color-mix(in srgb, {_RATIO_ACCENT} 14%, transparent)) !important; }}
|
| 925 |
-
.lanczos-title {{ padding: 4px 0 3px 6px !important; background: var(--block-background-fill) !important; }}
|
| 926 |
-
"""
|
| 927 |
-
|
| 928 |
-
css += """
|
| 929 |
#gpu-size .wrap {
|
| 930 |
display: grid !important;
|
| 931 |
grid-template-columns: repeat(2, 1fr) !important;
|
|
@@ -958,8 +1015,32 @@ css += """
|
|
| 958 |
line-height: 1.5 !important;
|
| 959 |
opacity: 0.7;
|
| 960 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 961 |
"""
|
| 962 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 963 |
_theme_dir = os.path.dirname(os.path.abspath(__file__))
|
| 964 |
_theme_pick = os.environ.get("UI_THEME", "")
|
| 965 |
if _theme_pick == "off":
|
|
@@ -980,8 +1061,7 @@ with gr.Blocks(**({} if _GR6 else {"css": css})) as demo:
|
|
| 980 |
gr.HTML(
|
| 981 |
'<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;600&display=swap">'
|
| 982 |
'<div id="logo-title">'
|
| 983 |
-
'<h1 id="ast-main-title">
|
| 984 |
-
'<br>'
|
| 985 |
'<h1 id="ast-nfaa-title">Not For All Audiences</h1>'
|
| 986 |
'<div id="ast-notice">'
|
| 987 |
'<p><b>Adults only:</b> Zero tolerance for any content involving minors. '
|
|
@@ -997,7 +1077,8 @@ with gr.Blocks(**({} if _GR6 else {"css": css})) as demo:
|
|
| 997 |
with gr.Column():
|
| 998 |
with gr.Row():
|
| 999 |
image_1 = gr.Image(label="Image 1", type="filepath", interactive=True)
|
| 1000 |
-
|
|
|
|
| 1001 |
|
| 1002 |
prompt = gr.Text(
|
| 1003 |
label="Prompt 🪄",
|
|
@@ -1012,26 +1093,8 @@ with gr.Blocks(**({} if _GR6 else {"css": css})) as demo:
|
|
| 1012 |
)
|
| 1013 |
|
| 1014 |
with gr.Group():
|
| 1015 |
-
aspect_ratio = gr.Radio(
|
| 1016 |
-
label="Aspect Ratio",
|
| 1017 |
-
show_label=True,
|
| 1018 |
-
choices=ASPECT_RATIOS,
|
| 1019 |
-
value=RATIO_DEFAULT,
|
| 1020 |
-
elem_id="aspect-ratio",
|
| 1021 |
-
)
|
| 1022 |
-
|
| 1023 |
-
with gr.Group():
|
| 1024 |
-
gr.Markdown("Lanczos Upscale", elem_classes=["lanczos-title"])
|
| 1025 |
-
with gr.Row(elem_id="lanczos-row"):
|
| 1026 |
-
scale_basic = gr.Checkbox(label="Basic", value=False, min_width=0)
|
| 1027 |
-
scale_medium = gr.Checkbox(label="Medium", value=False, min_width=0)
|
| 1028 |
-
scale_high = gr.Checkbox(label="High", value=False, min_width=0)
|
| 1029 |
-
|
| 1030 |
-
with gr.Group():
|
| 1031 |
-
gr.Markdown("Output Format", elem_classes=["lanczos-title"])
|
| 1032 |
output_format = gr.Radio(
|
| 1033 |
label="Output Format",
|
| 1034 |
-
show_label=False,
|
| 1035 |
choices=["PNG", "JPG", "WebP"],
|
| 1036 |
value="PNG",
|
| 1037 |
elem_id="output-format",
|
|
@@ -1076,10 +1139,50 @@ with gr.Blocks(**({} if _GR6 else {"css": css})) as demo:
|
|
| 1076 |
elem_id="gpu-size-note",
|
| 1077 |
)
|
| 1078 |
|
| 1079 |
-
|
| 1080 |
-
|
| 1081 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1082 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1083 |
|
| 1084 |
with gr.Tab("🔒", visible=False) as admin_tab:
|
| 1085 |
with gr.Group(visible=True) as admin_lock_group:
|
|
@@ -1136,23 +1239,9 @@ with gr.Blocks(**({} if _GR6 else {"css": css})) as demo:
|
|
| 1136 |
**_PRIVATE,
|
| 1137 |
)
|
| 1138 |
|
| 1139 |
-
|
| 1140 |
-
(scale_basic, "(on) => [on, false, false]"),
|
| 1141 |
-
(scale_medium, "(on) => [false, on, false]"),
|
| 1142 |
-
(scale_high, "(on) => [false, false, on]"),
|
| 1143 |
-
):
|
| 1144 |
-
_box.input(
|
| 1145 |
-
fn=None,
|
| 1146 |
-
inputs=[_box],
|
| 1147 |
-
outputs=[scale_basic, scale_medium, scale_high],
|
| 1148 |
-
js=_js,
|
| 1149 |
-
show_progress="hidden",
|
| 1150 |
-
**_PRIVATE,
|
| 1151 |
-
)
|
| 1152 |
-
|
| 1153 |
-
edit_event = gr.on(
|
| 1154 |
triggers=[run_button.click, prompt.submit],
|
| 1155 |
-
fn=
|
| 1156 |
inputs=[
|
| 1157 |
image_1,
|
| 1158 |
image_2,
|
|
@@ -1163,28 +1252,54 @@ with gr.Blocks(**({} if _GR6 else {"css": css})) as demo:
|
|
| 1163 |
num_inference_steps,
|
| 1164 |
gpu_duration,
|
| 1165 |
gpu_size,
|
| 1166 |
-
|
| 1167 |
-
|
| 1168 |
-
|
| 1169 |
-
|
|
|
|
| 1170 |
output_format,
|
| 1171 |
age_confirm,
|
| 1172 |
],
|
| 1173 |
-
outputs=[result, seed, use_output_btn],
|
| 1174 |
**({"show_progress_on": [result]} if _SHOW_PROGRESS_ON else {}),
|
| 1175 |
-
)
|
| 1176 |
-
edit_event.then(
|
| 1177 |
fn=_sync_result_followups,
|
| 1178 |
inputs=result,
|
| 1179 |
-
outputs=use_output_btn,
|
| 1180 |
show_progress="hidden",
|
| 1181 |
)
|
| 1182 |
|
| 1183 |
use_output_btn.click(fn=use_output_as_input, inputs=[result], outputs=[image_1])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1184 |
result.change(
|
| 1185 |
fn=_sync_result_followups,
|
| 1186 |
inputs=result,
|
| 1187 |
-
outputs=use_output_btn,
|
| 1188 |
show_progress="hidden",
|
| 1189 |
)
|
| 1190 |
|
|
@@ -1206,6 +1321,45 @@ with gr.Blocks(**({} if _GR6 else {"css": css})) as demo:
|
|
| 1206 |
**_PRIVATE,
|
| 1207 |
)
|
| 1208 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1209 |
demo.load(fn=mark_browser_session, inputs=None, outputs=None, **_PRIVATE)
|
| 1210 |
demo.load(fn=check_admin_tab_visibility, inputs=None, outputs=[admin_tab], **_PRIVATE)
|
| 1211 |
|
|
|
|
| 68 |
|
| 69 |
import Age_classifier
|
| 70 |
|
|
|
|
| 71 |
from security import (
|
| 72 |
FloodGuard,
|
| 73 |
check_admin_password,
|
|
|
|
| 119 |
MODEL_QWEN = "qwen"
|
| 120 |
MODEL_FLUX = "flux"
|
| 121 |
|
| 122 |
+
PRESET_RESOLUTIONS = [
|
| 123 |
+
("720×1280 (9:16)", 720, 1280),
|
| 124 |
+
("864×1536 (9:16)", 864, 1536),
|
| 125 |
+
("1080×1920 (9:16)", 1080, 1920),
|
| 126 |
+
("1440×2560 (9:16)", 1440, 2560),
|
| 127 |
+
("1600×2560 (10:16)", 1600, 2560),
|
| 128 |
+
("1536×2728 (9:16)", 1536, 2728),
|
| 129 |
+
("1280×720 (16:9)", 1280, 720),
|
| 130 |
+
("1536×864 (16:9)", 1536, 864),
|
| 131 |
+
("1920×1080 (16:9)", 1920, 1080),
|
| 132 |
+
("2560×1440 (16:9)", 2560, 1440),
|
| 133 |
+
("2560×1600 (16:10)", 2560, 1600),
|
| 134 |
+
("2728×1536 (16:9)", 2728, 1536),
|
| 135 |
+
("768×1024 (3:4)", 768, 1024),
|
| 136 |
+
("1080×1440 (3:4)", 1080, 1440),
|
| 137 |
+
("1536×2048 (3:4)", 1536, 2048),
|
| 138 |
+
("1024×1536 (2:3)", 1024, 1536),
|
| 139 |
+
("1440×1800 (4:5)", 1440, 1800),
|
| 140 |
+
("1080×2400 (9:20)", 1080, 2400),
|
| 141 |
+
("1024×768 (4:3)", 1024, 768),
|
| 142 |
+
("1440×1080 (4:3)", 1440, 1080),
|
| 143 |
+
("2048×1536 (4:3)", 2048, 1536),
|
| 144 |
+
("1536×1024 (3:2)", 1536, 1024),
|
| 145 |
+
("1800×1440 (5:4)", 1800, 1440),
|
| 146 |
+
("2560×1080 (21:9)", 2560, 1080),
|
| 147 |
+
]
|
| 148 |
+
PRESET_RESOLUTION_MAP = {label: (w, h) for label, w, h in PRESET_RESOLUTIONS}
|
| 149 |
+
PRESET_TALL = [label for label, w, h in PRESET_RESOLUTIONS if w < h]
|
| 150 |
+
PRESET_WIDE = [label for label, w, h in PRESET_RESOLUTIONS if w > h]
|
| 151 |
+
PRESET_ALL = PRESET_TALL + PRESET_WIDE
|
| 152 |
+
PRESET_DEFAULT = "720×1280 (9:16)"
|
| 153 |
+
AUTO_SCALES = ["1x", "2x", "3x", "4x"]
|
| 154 |
+
AUTO_BASE_PIXELS = 1024 * 1024
|
| 155 |
+
AUTO_MAX_PIXELS = 4194304
|
| 156 |
|
| 157 |
from diffusers.utils import logging as diffusers_logging
|
| 158 |
|
|
|
|
| 248 |
print("[vae] tiling applied:", {k: getattr(vae, k, None) for k in ("tile_sample_min_height", "tile_sample_min_width", "tile_sample_stride_height", "tile_sample_stride_width")})
|
| 249 |
|
| 250 |
|
| 251 |
+
QWEN_VAE_TILE = int(os.environ.get("QWEN_VAE_TILE", "1600"))
|
| 252 |
|
| 253 |
|
| 254 |
pipe = QwenImageEditPlusPipeline.from_pretrained(
|
|
|
|
| 285 |
except Exception as e:
|
| 286 |
print(f"[qwen] text_encoder quantization FAILED, staying bf16 (~+8GB VRAM): {e}")
|
| 287 |
|
| 288 |
+
_enable_vae_tiling(pipe, QWEN_VAE_TILE)
|
|
|
|
|
|
|
|
|
|
| 289 |
|
| 290 |
pipe.transformer.__class__ = QwenImageTransformer2DModel
|
| 291 |
pipe.transformer.set_attn_processor(QwenDoubleStreamAttnProcessorFA3())
|
| 292 |
|
| 293 |
FLUX_REPO = "black-forest-labs/FLUX.2-klein-9B"
|
| 294 |
+
FLUX_REFINE_PROMPT = "An ultra-high-resolution masterpiece with exceptional sharpness and detail, featuring only a subtle, barely perceptible amount of noise on the skin’s surface. The image remains entirely unzoomed and unaltered, preserving its original composition and proportions, with lighting and shadows meticulously controlled to remain perfectly faithful to the original"
|
| 295 |
FLUX_REFINE_GUIDANCE_SCALE = 1
|
| 296 |
FLUX_NUM_INFERENCE_STEPS = 4
|
| 297 |
+
FLUX_MAX_PIXELS = 2048 * 2048
|
|
|
|
| 298 |
|
| 299 |
flux_pipe = Flux2KleinPipeline.from_pretrained(FLUX_REPO, torch_dtype=dtype)
|
| 300 |
|
|
|
|
| 313 |
except Exception as e:
|
| 314 |
print(f"[flux] quantization FAILED, continuing in bf16 (roughly 2x the VRAM/transfer time): {e}")
|
| 315 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 316 |
# FLUX VAE tiling is off by default because tiled encode/decode shifted skin colour.
|
| 317 |
if os.environ.get("FLUX_VAE_TILING", "0") == "1":
|
| 318 |
_enable_vae_tiling(flux_pipe)
|
|
|
|
| 321 |
|
| 322 |
|
| 323 |
MAX_SEED = 2**31 - 1
|
| 324 |
+
LIMIT_STEPS_MAX = 20
|
| 325 |
+
LIMIT_GUIDANCE_MIN = 1.0
|
| 326 |
+
LIMIT_GUIDANCE_MAX = 2.0
|
| 327 |
+
LIMIT_SIDE_MAX = 2728
|
| 328 |
+
LIMIT_PIXELS_MAX = 2048 * 2048
|
| 329 |
+
LIMIT_INPUT_PIXELS = 2560 * 2560
|
| 330 |
_VALID_FORMATS = ("PNG", "JPG", "WebP")
|
| 331 |
|
| 332 |
GPU_DURATION_MIN = 10
|
|
|
|
| 346 |
return max(lo, min(hi, v))
|
| 347 |
|
| 348 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 349 |
def _to_float(value, lo, hi, default):
|
| 350 |
try:
|
| 351 |
v = float(value)
|
|
|
|
| 356 |
return max(lo, min(hi, v))
|
| 357 |
|
| 358 |
|
| 359 |
+
def _clamp_size(width, height, multiple=8, lo=64):
|
| 360 |
+
w = _to_int(width, lo, LIMIT_SIDE_MAX, lo)
|
| 361 |
+
h = _to_int(height, lo, LIMIT_SIDE_MAX, lo)
|
| 362 |
+
if w * h > LIMIT_PIXELS_MAX:
|
| 363 |
+
scale = (LIMIT_PIXELS_MAX / (w * h)) ** 0.5
|
| 364 |
+
w, h = int(w * scale), int(h * scale)
|
| 365 |
+
return max(lo, (w // multiple) * multiple), max(lo, (h // multiple) * multiple)
|
| 366 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 367 |
|
| 368 |
+
def _auto_size(scale_choice, image_1, image_2):
|
|
|
|
| 369 |
src = image_1 if image_1 is not None else image_2
|
| 370 |
if src is None:
|
| 371 |
+
return None, None
|
| 372 |
try:
|
| 373 |
with Image.open(src if isinstance(src, str) else src.name) as im:
|
| 374 |
+
width, height = im.size
|
| 375 |
except Exception:
|
| 376 |
+
return None, None
|
| 377 |
+
factor = _to_int(str(scale_choice).rstrip("x"), 1, 4, 1)
|
| 378 |
+
pixels = min(AUTO_BASE_PIXELS * factor, AUTO_MAX_PIXELS)
|
| 379 |
+
ratio = width / height
|
| 380 |
+
out_w = (pixels * ratio) ** 0.5
|
| 381 |
+
return _clamp_size(int(out_w), int(out_w / ratio), multiple=16)
|
| 382 |
|
| 383 |
|
| 384 |
+
def _resolve_size(mode, preset, custom_width, custom_height, auto_scale, image_1, image_2):
|
| 385 |
try:
|
| 386 |
+
if mode == "Preset" and preset in PRESET_RESOLUTION_MAP:
|
| 387 |
+
return _clamp_size(*PRESET_RESOLUTION_MAP[preset])
|
| 388 |
+
if mode == "Custom":
|
| 389 |
+
return _clamp_size(int(custom_width), int(custom_height))
|
| 390 |
+
if mode == "Auto":
|
| 391 |
+
return _auto_size(auto_scale, image_1, image_2)
|
| 392 |
+
except (TypeError, ValueError, OverflowError):
|
| 393 |
pass
|
| 394 |
+
return None, None
|
| 395 |
|
| 396 |
|
| 397 |
def _fit_size(width, height, max_pixels=FLUX_MAX_PIXELS, multiple=16):
|
|
|
|
| 407 |
def _load_image(img):
|
| 408 |
if not isinstance(img, Image.Image):
|
| 409 |
img = Image.open(img if isinstance(img, str) else img.name)
|
| 410 |
+
img = img.convert("RGB")
|
| 411 |
+
w, h = img.size
|
| 412 |
+
if w * h > LIMIT_INPUT_PIXELS:
|
| 413 |
+
scale = (LIMIT_INPUT_PIXELS / (w * h)) ** 0.5
|
| 414 |
+
img = img.resize((max(1, int(w * scale)), max(1, int(h * scale))), Image.LANCZOS)
|
| 415 |
+
return img
|
| 416 |
|
| 417 |
|
| 418 |
def _sanitize_common(seed, gpu_duration, output_format):
|
|
|
|
| 456 |
num_inference_steps,
|
| 457 |
height,
|
| 458 |
width,
|
| 459 |
+
output_format,
|
| 460 |
progress=gr.Progress(track_tqdm=True),
|
| 461 |
):
|
| 462 |
if randomize_seed:
|
|
|
|
| 545 |
else:
|
| 546 |
img.save(output_path)
|
| 547 |
output_paths.append(output_path)
|
|
|
|
| 548 |
log_generation(
|
| 549 |
path=output_path,
|
| 550 |
prompt=prompt,
|
|
|
|
| 559 |
inputs=input_paths,
|
| 560 |
)
|
| 561 |
has_output = bool(output_paths)
|
| 562 |
+
return output_paths, seed, gr.update(visible=has_output), gr.update(visible=has_output)
|
| 563 |
|
| 564 |
|
| 565 |
+
def _make_gpu_variant(duration_seconds, size):
|
| 566 |
+
def _infer_gpu_call(*args, **kwargs):
|
| 567 |
+
return _generate_core(*args, **kwargs)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 568 |
|
| 569 |
+
_infer_gpu_call.__name__ = f"_infer_impl_gpu_{size}_{duration_seconds}s"
|
| 570 |
+
_infer_gpu_call.__qualname__ = _infer_gpu_call.__name__
|
| 571 |
+
return spaces.GPU(duration=duration_seconds, size=size)(_infer_gpu_call)
|
| 572 |
|
| 573 |
|
| 574 |
+
_gpu_variants = {(s, d): _make_gpu_variant(d, s) for s in GPU_SIZE_OPTIONS for d in _GPU_DURATION_STEPS}
|
| 575 |
|
| 576 |
|
| 577 |
+
def _pick_gpu_variant(gpu_duration, gpu_size=GPU_SIZE_DEFAULT):
|
| 578 |
requested = _to_int(gpu_duration, GPU_DURATION_MIN, GPU_DURATION_MAX, GPU_DURATION_DEFAULT)
|
| 579 |
chosen = next((d for d in _GPU_DURATION_STEPS if d >= requested), _GPU_DURATION_STEPS[-1])
|
| 580 |
size = gpu_size if gpu_size in GPU_SIZE_OPTIONS else GPU_SIZE_DEFAULT
|
| 581 |
+
gpu_fn = _gpu_variants[(size, chosen)]
|
| 582 |
|
| 583 |
+
def _call(model_choice, image_1, image_2, prompt, *args, output_format="PNG", **kwargs):
|
| 584 |
+
res = gpu_fn(model_choice, image_1, image_2, prompt, *args, output_format=output_format, **kwargs)
|
| 585 |
+
return _finalize(res, image_1, image_2, prompt, output_format)
|
|
|
|
| 586 |
|
| 587 |
return _call
|
| 588 |
|
|
|
|
| 681 |
ok, key, msg = rate_acquire(request)
|
| 682 |
if not ok:
|
| 683 |
show_rate_message(msg)
|
| 684 |
+
return gr.update(), seed, gr.update(), gr.update()
|
| 685 |
refund = False
|
| 686 |
try:
|
| 687 |
return run()
|
|
|
|
| 692 |
rate_release(key, refund=refund)
|
| 693 |
|
| 694 |
|
| 695 |
+
def infer_qwen_only(
|
| 696 |
image_1,
|
| 697 |
image_2,
|
| 698 |
prompt,
|
|
|
|
| 702 |
num_inference_steps,
|
| 703 |
gpu_duration,
|
| 704 |
gpu_size,
|
| 705 |
+
resolution_mode,
|
| 706 |
+
resolution_preset,
|
| 707 |
+
custom_width,
|
| 708 |
+
custom_height,
|
| 709 |
+
auto_scale,
|
| 710 |
output_format,
|
| 711 |
age_confirmed=False,
|
| 712 |
progress=gr.Progress(track_tqdm=True),
|
|
|
|
| 716 |
raise gr.Error("Please tick the box to confirm you are 18 or older.")
|
| 717 |
|
| 718 |
if not (isinstance(prompt, str) and prompt.strip()):
|
| 719 |
+
return gr.update(), seed, gr.update(), gr.update()
|
| 720 |
|
| 721 |
def run():
|
| 722 |
clean_seed, duration, fmt = _sanitize_common(seed, gpu_duration, output_format)
|
| 723 |
+
width, height = _resolve_size(resolution_mode, resolution_preset, custom_width, custom_height, auto_scale, image_1, image_2)
|
| 724 |
clean_prompt = prompt if isinstance(prompt, str) else ""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 725 |
try:
|
| 726 |
_screen_inputs(request, clean_prompt, image_1, image_2)
|
| 727 |
+
return _screen_outputs(_pick_gpu_variant(duration, gpu_size)(
|
| 728 |
+
MODEL_QWEN,
|
| 729 |
+
image_1,
|
| 730 |
+
image_2,
|
| 731 |
+
clean_prompt,
|
| 732 |
+
seed=clean_seed,
|
| 733 |
+
randomize_seed=bool(randomize_seed),
|
| 734 |
+
true_guidance_scale=_to_float(true_guidance_scale, LIMIT_GUIDANCE_MIN, LIMIT_GUIDANCE_MAX, 1.0),
|
| 735 |
+
num_inference_steps=_to_int(num_inference_steps, 1, LIMIT_STEPS_MAX, 4),
|
| 736 |
+
height=height,
|
| 737 |
+
width=width,
|
| 738 |
+
output_format=fmt,
|
| 739 |
+
progress=progress,
|
| 740 |
+
), request)
|
| 741 |
+
except gr.Error as exc:
|
| 742 |
+
if not _is_age_block(exc):
|
| 743 |
+
raise
|
| 744 |
+
return [_block_image()], seed, gr.update(visible=False), gr.update(visible=False)
|
| 745 |
+
|
| 746 |
+
return _run_limited(request, seed, run)
|
| 747 |
+
|
| 748 |
+
|
| 749 |
+
def refine_with_flux(
|
| 750 |
+
output_images,
|
| 751 |
+
seed,
|
| 752 |
+
randomize_seed,
|
| 753 |
+
gpu_duration,
|
| 754 |
+
gpu_size,
|
| 755 |
+
output_format,
|
| 756 |
+
progress=gr.Progress(track_tqdm=True),
|
| 757 |
+
request: gr.Request = None,
|
| 758 |
+
):
|
| 759 |
+
if not output_images:
|
| 760 |
+
return gr.update(), seed, gr.update(visible=False), gr.update(visible=False)
|
| 761 |
+
|
| 762 |
+
def run():
|
| 763 |
+
clean_seed, duration, fmt = _sanitize_common(seed, gpu_duration, output_format)
|
| 764 |
+
path = _first_path(output_images)
|
| 765 |
+
try:
|
| 766 |
+
with Image.open(path) as im:
|
| 767 |
+
width, height = _clamp_size(*im.size, multiple=16)
|
| 768 |
+
except Exception:
|
| 769 |
+
width = height = None
|
| 770 |
+
try:
|
| 771 |
+
_screen_inputs(request, "", path)
|
| 772 |
+
return _screen_outputs(_pick_gpu_variant(duration, gpu_size)(
|
| 773 |
+
MODEL_FLUX,
|
| 774 |
+
path,
|
| 775 |
+
None,
|
| 776 |
+
FLUX_REFINE_PROMPT,
|
| 777 |
+
seed=clean_seed,
|
| 778 |
+
randomize_seed=bool(randomize_seed),
|
| 779 |
+
true_guidance_scale=FLUX_REFINE_GUIDANCE_SCALE,
|
| 780 |
+
num_inference_steps=FLUX_NUM_INFERENCE_STEPS,
|
| 781 |
+
height=height,
|
| 782 |
+
width=width,
|
| 783 |
+
output_format=fmt,
|
| 784 |
+
progress=progress,
|
| 785 |
+
), request)
|
| 786 |
except gr.Error as exc:
|
| 787 |
if not _is_age_block(exc):
|
| 788 |
raise
|
| 789 |
+
return [_block_image()], seed, gr.update(visible=False), gr.update()
|
| 790 |
|
| 791 |
return _run_limited(request, seed, run)
|
| 792 |
|
|
|
|
| 798 |
|
| 799 |
|
| 800 |
def _sync_result_followups(output_images):
|
| 801 |
+
visible = bool(output_images) and not _is_block_image(output_images)
|
| 802 |
+
return gr.update(visible=visible), gr.update(visible=visible)
|
| 803 |
+
|
| 804 |
+
|
| 805 |
+
def _sync_flux_result_followups(output_images):
|
| 806 |
return gr.update(visible=bool(output_images) and not _is_block_image(output_images))
|
| 807 |
|
| 808 |
|
|
|
|
| 820 |
}
|
| 821 |
#logo-title {
|
| 822 |
text-align: center;
|
| 823 |
+
display: flex;
|
| 824 |
+
flex-direction: column;
|
| 825 |
+
align-items: center;
|
| 826 |
+
gap: 14px;
|
| 827 |
+
margin-bottom: 10px;
|
| 828 |
+
}
|
| 829 |
+
#logo-title h1 {
|
| 830 |
+
margin: 0 !important;
|
| 831 |
+
padding: 0 !important;
|
| 832 |
+
line-height: 1.15 !important;
|
| 833 |
+
}
|
| 834 |
+
/* Italic + background-clip:text clips the slanted tail of the last glyph; extra right padding
|
| 835 |
+
gives it room, and the negative margin keeps the title visually centered. */
|
| 836 |
+
#logo-title h1#ast-main-title,
|
| 837 |
+
#logo-title h1#ast-nfaa-title {
|
| 838 |
+
padding-right: 0.3em !important;
|
| 839 |
+
margin-right: -0.3em !important;
|
| 840 |
}
|
| 841 |
|
| 842 |
@keyframes ast-title-flash {
|
|
|
|
| 858 |
@media (max-width: 480px) {
|
| 859 |
#ast-main-title { font-size: 5.8vw !important; }
|
| 860 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 861 |
#ast-nfaa-title {
|
| 862 |
font-style: italic;
|
| 863 |
display: inline-block;
|
|
|
|
| 877 |
#ast-notice {
|
| 878 |
width: 100%;
|
| 879 |
max-width: 560px;
|
| 880 |
+
margin: 0 auto !important;
|
| 881 |
padding: 1px 0 1px 14px !important;
|
| 882 |
border-left: 2px solid transparent;
|
| 883 |
border-image: linear-gradient(180deg, #FF3CAC, #7B2FF7, #00E5FF) 1;
|
|
|
|
| 926 |
background-color: #FF1010 !important;
|
| 927 |
border-color: #FF1010 !important;
|
| 928 |
}
|
| 929 |
+
/* Force a 2-column grid for the preset radio (Gradio's default flex-wrap stacks into one column on mobile). */
|
| 930 |
+
.preset-grid > .wrap,
|
| 931 |
+
.preset-grid .wrap {
|
| 932 |
+
display: grid !important;
|
| 933 |
+
grid-template-columns: 1fr 1fr !important;
|
| 934 |
+
gap: 8px !important;
|
| 935 |
+
}
|
| 936 |
+
#adv-settings:has(#res-mode input[value="Preset"]:checked) #res-custom-height,
|
| 937 |
+
#adv-settings:has(#res-mode input[value="Preset"]:checked) #res-custom-width,
|
| 938 |
+
#adv-settings:has(#res-mode input[value="Preset"]:checked) #res-auto-scale,
|
| 939 |
+
#adv-settings:has(#res-mode input[value="Custom"]:checked) #res-preset,
|
| 940 |
+
#adv-settings:has(#res-mode input[value="Custom"]:checked) #res-auto-scale,
|
| 941 |
+
#adv-settings:has(#res-mode input[value="Auto"]:checked) #res-preset,
|
| 942 |
+
#adv-settings:has(#res-mode input[value="Auto"]:checked) #res-custom-height,
|
| 943 |
+
#adv-settings:has(#res-mode input[value="Auto"]:checked) #res-custom-width {
|
| 944 |
+
display: none !important;
|
| 945 |
+
}
|
| 946 |
+
/* Seed is an output component: hide Gradio's progress/status overlay so it never covers the number box. */
|
| 947 |
#seed-slider .wrap.generating,
|
| 948 |
#seed-slider .wrap.pending,
|
| 949 |
#seed-slider .progress-text,
|
| 950 |
#seed-slider .progress-level {
|
| 951 |
display: none !important;
|
| 952 |
}
|
| 953 |
+
#flux-seed-slider .wrap.generating,
|
| 954 |
+
#flux-seed-slider .wrap.pending,
|
| 955 |
+
#flux-seed-slider .progress-text,
|
| 956 |
+
#flux-seed-slider .progress-level {
|
| 957 |
+
display: none !important;
|
| 958 |
+
}
|
| 959 |
+
.preset-grid label {
|
| 960 |
+
width: 100% !important;
|
| 961 |
+
margin: 0 !important;
|
| 962 |
+
box-sizing: border-box !important;
|
| 963 |
+
}
|
| 964 |
+
#res-mode .wrap {
|
| 965 |
+
display: grid !important;
|
| 966 |
+
grid-template-columns: repeat(3, 1fr) !important;
|
| 967 |
+
gap: 8px !important;
|
| 968 |
+
}
|
| 969 |
+
#res-mode label {
|
| 970 |
+
width: 100% !important;
|
| 971 |
+
margin: 0 !important;
|
| 972 |
+
box-sizing: border-box !important;
|
| 973 |
+
justify-content: center !important;
|
| 974 |
+
}
|
| 975 |
+
#output-format .wrap {
|
| 976 |
+
display: grid !important;
|
| 977 |
+
grid-template-columns: repeat(3, 1fr) !important;
|
| 978 |
+
gap: 8px !important;
|
| 979 |
+
}
|
| 980 |
+
#output-format label {
|
| 981 |
+
width: 100% !important;
|
| 982 |
+
margin: 0 !important;
|
| 983 |
+
box-sizing: border-box !important;
|
| 984 |
+
justify-content: center !important;
|
| 985 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 986 |
#gpu-size .wrap {
|
| 987 |
display: grid !important;
|
| 988 |
grid-template-columns: repeat(2, 1fr) !important;
|
|
|
|
| 1015 |
line-height: 1.5 !important;
|
| 1016 |
opacity: 0.7;
|
| 1017 |
}
|
| 1018 |
+
#res-auto-scale .wrap {
|
| 1019 |
+
display: grid !important;
|
| 1020 |
+
grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
|
| 1021 |
+
gap: 6px !important;
|
| 1022 |
+
}
|
| 1023 |
+
#res-auto-scale label {
|
| 1024 |
+
width: 100% !important;
|
| 1025 |
+
min-width: 0 !important;
|
| 1026 |
+
margin: 0 !important;
|
| 1027 |
+
padding-left: 8px !important;
|
| 1028 |
+
padding-right: 8px !important;
|
| 1029 |
+
box-sizing: border-box !important;
|
| 1030 |
+
justify-content: center !important;
|
| 1031 |
+
}
|
| 1032 |
"""
|
| 1033 |
|
| 1034 |
+
_div_a = f'.preset-grid label:has(input[value="{PRESET_WIDE[0]}"])'
|
| 1035 |
+
_div_b = f'.preset-grid label:has(input[value="{PRESET_WIDE[1]}"])'
|
| 1036 |
+
css += (
|
| 1037 |
+
f"{_div_a}, {_div_b} {{ position: relative !important; margin-top: 12px !important; overflow: visible !important; }}\n"
|
| 1038 |
+
f"{_div_a}::before, {_div_b}::before {{ content: \"\"; position: absolute; top: -10px; height: 1px; "
|
| 1039 |
+
f"background: rgba(128, 128, 128, 0.55); pointer-events: none; }}\n"
|
| 1040 |
+
f"{_div_a}::before {{ left: 0; right: -4px; }}\n"
|
| 1041 |
+
f"{_div_b}::before {{ left: -4px; right: 0; }}\n"
|
| 1042 |
+
)
|
| 1043 |
+
|
| 1044 |
_theme_dir = os.path.dirname(os.path.abspath(__file__))
|
| 1045 |
_theme_pick = os.environ.get("UI_THEME", "")
|
| 1046 |
if _theme_pick == "off":
|
|
|
|
| 1061 |
gr.HTML(
|
| 1062 |
'<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;600&display=swap">'
|
| 1063 |
'<div id="logo-title">'
|
| 1064 |
+
'<h1 id="ast-main-title">Qwen 2511 + FLUX.2</h1>'
|
|
|
|
| 1065 |
'<h1 id="ast-nfaa-title">Not For All Audiences</h1>'
|
| 1066 |
'<div id="ast-notice">'
|
| 1067 |
'<p><b>Adults only:</b> Zero tolerance for any content involving minors. '
|
|
|
|
| 1077 |
with gr.Column():
|
| 1078 |
with gr.Row():
|
| 1079 |
image_1 = gr.Image(label="Image 1", type="filepath", interactive=True)
|
| 1080 |
+
with gr.Column():
|
| 1081 |
+
image_2 = gr.Image(label="Image 2 (optional)", type="filepath", interactive=True)
|
| 1082 |
|
| 1083 |
prompt = gr.Text(
|
| 1084 |
label="Prompt 🪄",
|
|
|
|
| 1093 |
)
|
| 1094 |
|
| 1095 |
with gr.Group():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1096 |
output_format = gr.Radio(
|
| 1097 |
label="Output Format",
|
|
|
|
| 1098 |
choices=["PNG", "JPG", "WebP"],
|
| 1099 |
value="PNG",
|
| 1100 |
elem_id="output-format",
|
|
|
|
| 1139 |
elem_id="gpu-size-note",
|
| 1140 |
)
|
| 1141 |
|
| 1142 |
+
with gr.Group():
|
| 1143 |
+
resolution_mode = gr.Radio(
|
| 1144 |
+
label="Resolution mode",
|
| 1145 |
+
choices=["Auto", "Preset", "Custom"],
|
| 1146 |
+
value="Auto",
|
| 1147 |
+
elem_id="res-mode",
|
| 1148 |
+
)
|
| 1149 |
+
resolution_preset = gr.Radio(
|
| 1150 |
+
label="Resolution",
|
| 1151 |
+
choices=PRESET_ALL,
|
| 1152 |
+
value=PRESET_DEFAULT,
|
| 1153 |
+
elem_classes=["preset-grid"],
|
| 1154 |
+
elem_id="res-preset",
|
| 1155 |
+
)
|
| 1156 |
+
custom_height = gr.Slider(
|
| 1157 |
+
label="Height", minimum=256, maximum=2048, step=8, value=256,
|
| 1158 |
+
buttons=[], elem_id="res-custom-height",
|
| 1159 |
+
)
|
| 1160 |
+
custom_width = gr.Slider(
|
| 1161 |
+
label="Width", minimum=256, maximum=2048, step=8, value=256,
|
| 1162 |
+
buttons=[], elem_id="res-custom-width",
|
| 1163 |
+
)
|
| 1164 |
+
auto_scale = gr.Radio(
|
| 1165 |
+
label="Auto resolution",
|
| 1166 |
+
choices=AUTO_SCALES,
|
| 1167 |
+
value="1x",
|
| 1168 |
+
elem_id="res-auto-scale",
|
| 1169 |
+
)
|
| 1170 |
|
| 1171 |
+
with gr.Column():
|
| 1172 |
+
result = gr.Gallery(label="Result", show_label=False, type="filepath", interactive=True, elem_id="result-gallery")
|
| 1173 |
+
with gr.Column(visible=False) as refine_group:
|
| 1174 |
+
with gr.Row():
|
| 1175 |
+
use_output_btn = gr.Button("↗️ Use as input", variant="secondary", size="sm", visible=False)
|
| 1176 |
+
refine_with_flux_btn = gr.Button(
|
| 1177 |
+
"Refine with FLUX.2-klein-9B", variant="primary", size="lg",
|
| 1178 |
+
)
|
| 1179 |
+
flux_seed = gr.Slider(label="FLUX seed", minimum=0, maximum=MAX_SEED, step=1, value=42, elem_id="flux-seed-slider")
|
| 1180 |
+
flux_randomize_seed = gr.Checkbox(label="Randomize FLUX seed", value=False)
|
| 1181 |
+
flux_result = gr.Gallery(label="FLUX Result", show_label=True, type="filepath", interactive=True, elem_id="flux-result-gallery")
|
| 1182 |
+
with gr.Row():
|
| 1183 |
+
use_flux_output_btn = gr.Button(
|
| 1184 |
+
"↗️ Use FLUX output as input", variant="secondary", size="sm", visible=False,
|
| 1185 |
+
)
|
| 1186 |
|
| 1187 |
with gr.Tab("🔒", visible=False) as admin_tab:
|
| 1188 |
with gr.Group(visible=True) as admin_lock_group:
|
|
|
|
| 1239 |
**_PRIVATE,
|
| 1240 |
)
|
| 1241 |
|
| 1242 |
+
gr.on(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1243 |
triggers=[run_button.click, prompt.submit],
|
| 1244 |
+
fn=infer_qwen_only,
|
| 1245 |
inputs=[
|
| 1246 |
image_1,
|
| 1247 |
image_2,
|
|
|
|
| 1252 |
num_inference_steps,
|
| 1253 |
gpu_duration,
|
| 1254 |
gpu_size,
|
| 1255 |
+
resolution_mode,
|
| 1256 |
+
resolution_preset,
|
| 1257 |
+
custom_width,
|
| 1258 |
+
custom_height,
|
| 1259 |
+
auto_scale,
|
| 1260 |
output_format,
|
| 1261 |
age_confirm,
|
| 1262 |
],
|
| 1263 |
+
outputs=[result, seed, use_output_btn, refine_group],
|
| 1264 |
**({"show_progress_on": [result]} if _SHOW_PROGRESS_ON else {}),
|
| 1265 |
+
).then(
|
|
|
|
| 1266 |
fn=_sync_result_followups,
|
| 1267 |
inputs=result,
|
| 1268 |
+
outputs=[use_output_btn, refine_group],
|
| 1269 |
show_progress="hidden",
|
| 1270 |
)
|
| 1271 |
|
| 1272 |
use_output_btn.click(fn=use_output_as_input, inputs=[result], outputs=[image_1])
|
| 1273 |
+
|
| 1274 |
+
refine_with_flux_btn.click(
|
| 1275 |
+
fn=refine_with_flux,
|
| 1276 |
+
inputs=[result, flux_seed, flux_randomize_seed, gpu_duration, gpu_size, output_format],
|
| 1277 |
+
outputs=[flux_result, flux_seed, use_flux_output_btn, refine_group],
|
| 1278 |
+
**({"show_progress_on": [flux_result]} if _SHOW_PROGRESS_ON else {}),
|
| 1279 |
+
).then(
|
| 1280 |
+
fn=_sync_result_followups,
|
| 1281 |
+
inputs=result,
|
| 1282 |
+
outputs=[use_output_btn, refine_group],
|
| 1283 |
+
show_progress="hidden",
|
| 1284 |
+
).then(
|
| 1285 |
+
fn=_sync_flux_result_followups,
|
| 1286 |
+
inputs=flux_result,
|
| 1287 |
+
outputs=use_flux_output_btn,
|
| 1288 |
+
show_progress="hidden",
|
| 1289 |
+
)
|
| 1290 |
+
|
| 1291 |
+
use_flux_output_btn.click(fn=use_output_as_input, inputs=[flux_result], outputs=[image_1])
|
| 1292 |
+
|
| 1293 |
+
flux_result.change(
|
| 1294 |
+
fn=_sync_flux_result_followups,
|
| 1295 |
+
inputs=flux_result,
|
| 1296 |
+
outputs=use_flux_output_btn,
|
| 1297 |
+
show_progress="hidden",
|
| 1298 |
+
)
|
| 1299 |
result.change(
|
| 1300 |
fn=_sync_result_followups,
|
| 1301 |
inputs=result,
|
| 1302 |
+
outputs=[use_output_btn, refine_group],
|
| 1303 |
show_progress="hidden",
|
| 1304 |
)
|
| 1305 |
|
|
|
|
| 1321 |
**_PRIVATE,
|
| 1322 |
)
|
| 1323 |
|
| 1324 |
+
result.upload(fn=lambda: None, inputs=None, outputs=result, show_progress="hidden", **_PRIVATE)
|
| 1325 |
+
flux_result.upload(fn=lambda: None, inputs=None, outputs=flux_result, show_progress="hidden", **_PRIVATE)
|
| 1326 |
+
demo.load(
|
| 1327 |
+
fn=None, inputs=None, outputs=None,
|
| 1328 |
+
js="""() => {
|
| 1329 |
+
const ids = ["result-gallery", "flux-result-gallery"];
|
| 1330 |
+
const inGallery = (t) => t && t.closest && ids.some((id) => t.closest("#" + id));
|
| 1331 |
+
const lock = () => ids.forEach((id) => {
|
| 1332 |
+
const root = document.getElementById(id);
|
| 1333 |
+
if (!root) return;
|
| 1334 |
+
root.querySelectorAll("input[type=file]").forEach((i) => {
|
| 1335 |
+
i.disabled = true;
|
| 1336 |
+
const wrap = i.closest("button");
|
| 1337 |
+
if (wrap) wrap.style.visibility = "hidden";
|
| 1338 |
+
});
|
| 1339 |
+
});
|
| 1340 |
+
["dragenter", "dragover", "drop"].forEach((ev) =>
|
| 1341 |
+
document.addEventListener(ev, (e) => {
|
| 1342 |
+
if (inGallery(e.target)) { e.preventDefault(); e.stopPropagation(); }
|
| 1343 |
+
}, true));
|
| 1344 |
+
new MutationObserver(lock).observe(document.body, { childList: true, subtree: true });
|
| 1345 |
+
lock();
|
| 1346 |
+
}""",
|
| 1347 |
+
**_PRIVATE,
|
| 1348 |
+
)
|
| 1349 |
+
|
| 1350 |
+
resolution_mode.change(
|
| 1351 |
+
fn=None, inputs=None, outputs=None,
|
| 1352 |
+
js="""() => {
|
| 1353 |
+
const nudge = () => {
|
| 1354 |
+
window.dispatchEvent(new Event("resize"));
|
| 1355 |
+
try { if (window.parentIFrame && window.parentIFrame.size) window.parentIFrame.size(); } catch (e) {}
|
| 1356 |
+
};
|
| 1357 |
+
[0, 100, 300, 800].forEach((ms) => setTimeout(nudge, ms));
|
| 1358 |
+
}""",
|
| 1359 |
+
show_progress="hidden",
|
| 1360 |
+
**_PRIVATE,
|
| 1361 |
+
)
|
| 1362 |
+
|
| 1363 |
demo.load(fn=mark_browser_session, inputs=None, outputs=None, **_PRIVATE)
|
| 1364 |
demo.load(fn=check_admin_tab_visibility, inputs=None, outputs=[admin_tab], **_PRIVATE)
|
| 1365 |
|