import os os.environ.setdefault("HF_HOME", "/tmp/.cache/huggingface") os.environ.setdefault("HF_MODULES_CACHE", "/tmp/hf_modules") os.environ.setdefault("MPLCONFIGDIR", "/tmp/matplotlib") os.environ.setdefault("GRADIO_ANALYTICS_ENABLED", "False") os.environ.setdefault("HF_HUB_DISABLE_SYMLINKS_WARNING", "1") os.environ.setdefault("TOKENIZERS_PARALLELISM", "false") import spaces import time from pathlib import Path from typing import Optional, Tuple import gradio as gr from gradio.themes.utils import colors, sizes from gradio_imageslider import ImageSlider from huggingface_hub import snapshot_download from PIL import Image, ImageOps import torch from diffusers.pipelines import FluxPipeline from src.flux.condition import Condition from src.flux.generate import generate, seed_everything from tools.color_fix import adain_color_fix _IMAGE_SLIDER_GET_CONFIG = ImageSlider.get_config def _imageslider_get_config_with_buttons(self, cls=None): # gradio_imageslider 0.0.20's Gradio 6 front-end expects this prop. config = _IMAGE_SLIDER_GET_CONFIG(self, cls) config.setdefault("buttons", ["download", "fullscreen"]) return config ImageSlider.get_config = _imageslider_get_config_with_buttons FLUX_MODEL_ID = os.environ.get("FLUX_MODEL_ID", "black-forest-labs/FLUX.1-dev") ASASR_MODEL_ID = os.environ.get("ASASR_MODEL_ID", "wafer-bob/ASASR") SR_LORA_NAME = "sr_lora/pytorch_lora_weights_v2.safetensors" DPO_LORA_NAME = "dpo_lora/adapter_model.safetensors" TARGET_RESOLUTION = 512 LR_RESOLUTION = TARGET_RESOLUTION // 4 NUM_INFERENCE_STEPS = 28 GUIDANCE_SCALE = 3.5 EXAMPLES_DIR = Path("examples") EXAMPLE_NAMES = ( "portrait.png", "landscape.png", "text.png", "architecture.png", "wildlife.png", "texture.png", ) EXAMPLE_FILES = [ str(EXAMPLES_DIR / name) for name in EXAMPLE_NAMES if (EXAMPLES_DIR / name).is_file() ] PIPELINE: Optional[FluxPipeline] = None FLUX_LOCAL_DIR: Optional[str] = None ASASR_LOCAL_DIR: Optional[str] = None PIPELINE_LOAD_SECONDS: Optional[float] = None LAST_INFERENCE_SECONDS: Optional[float] = None STARTUP_NOTE = "Assets will be resolved on the first request." def _token() -> Optional[str]: return os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN") def _require_token() -> str: token = _token() if not token: raise gr.Error( "HF_TOKEN is not set. Add it as a Space secret with access to " "black-forest-labs/FLUX.1-dev." ) return token def _prepare_assets() -> Tuple[str, str]: global FLUX_LOCAL_DIR, ASASR_LOCAL_DIR, STARTUP_NOTE if FLUX_LOCAL_DIR and ASASR_LOCAL_DIR: return FLUX_LOCAL_DIR, ASASR_LOCAL_DIR token = _require_token() start = time.perf_counter() print("[ASASR] Resolving FLUX.1-dev and ASASR LoRA assets...") FLUX_LOCAL_DIR = snapshot_download( repo_id=FLUX_MODEL_ID, token=token, ignore_patterns=[ "*.bin", "*.onnx", "*.msgpack", "examples/*", "ae.safetensors", "dev_grid.jpg", "flux1-dev.safetensors", ], max_workers=8, ) ASASR_LOCAL_DIR = snapshot_download( repo_id=ASASR_MODEL_ID, token=token, allow_patterns=[ SR_LORA_NAME, DPO_LORA_NAME, "dpo_lora/adapter_config.json", ], max_workers=4, ) elapsed = time.perf_counter() - start STARTUP_NOTE = f"Model assets resolved in {elapsed:.1f}s." print(f"[ASASR] Asset resolution complete in {elapsed:.1f}s.") return FLUX_LOCAL_DIR, ASASR_LOCAL_DIR def _startup_prefetch() -> None: global STARTUP_NOTE if os.environ.get("ASASR_PREFETCH", "1") != "1": STARTUP_NOTE = "Startup prefetch is disabled." print("[ASASR] Startup prefetch disabled.") return if not _token(): STARTUP_NOTE = "HF_TOKEN is missing; assets will be resolved after the secret is set." print("[ASASR] HF_TOKEN missing; startup prefetch deferred.") return try: _prepare_assets() except Exception as exc: STARTUP_NOTE = f"Startup prefetch deferred: {type(exc).__name__}: {exc}" print(f"[ASASR] Startup prefetch deferred: {type(exc).__name__}: {exc}") def _get_pipeline() -> FluxPipeline: global PIPELINE, PIPELINE_LOAD_SECONDS if PIPELINE is not None: return PIPELINE flux_dir, asasr_dir = _prepare_assets() sr_path = Path(asasr_dir) / SR_LORA_NAME dpo_path = Path(asasr_dir) / DPO_LORA_NAME start = time.perf_counter() print("[ASASR] Loading FLUX.1-dev pipeline and dual LoRAs onto cuda...") pipe = FluxPipeline.from_pretrained( flux_dir, torch_dtype=torch.bfloat16, local_files_only=True, ).to("cuda") pipe.load_lora_weights( sr_path.parent.as_posix(), weight_name=sr_path.name, adapter_name="sr", ) pipe.load_lora_weights( dpo_path.parent.as_posix(), weight_name=dpo_path.name, adapter_name="dpo", ) pipe.set_adapters(["sr", "dpo"], adapter_weights=[1.0, 1.0]) pipe.set_progress_bar_config(disable=True) PIPELINE = pipe PIPELINE_LOAD_SECONDS = time.perf_counter() - start print(f"[ASASR] Pipeline ready in {PIPELINE_LOAD_SECONDS:.1f}s.") return PIPELINE def _center_square(image: Image.Image) -> Image.Image: image = ImageOps.exif_transpose(image).convert("RGB") width, height = image.size side = min(width, height) left = (width - side) // 2 top = (height - side) // 2 return image.crop((left, top, left + side, top + side)) def _prepare_lr_image(image: Image.Image) -> Image.Image: return _center_square(image).resize( (LR_RESOLUTION, LR_RESOLUTION), Image.Resampling.LANCZOS, ) def _prepare_condition_image(lr_image: Image.Image) -> Image.Image: return lr_image.resize( (TARGET_RESOLUTION, TARGET_RESOLUTION), Image.Resampling.BICUBIC, ) def _prepare_slider_lr_image(lr_image: Image.Image) -> Image.Image: return lr_image.resize( (TARGET_RESOLUTION, TARGET_RESOLUTION), Image.Resampling.NEAREST, ) def _initial_slider_value() -> Tuple[Image.Image, Image.Image]: preview = Image.new("RGB", (TARGET_RESOLUTION, TARGET_RESOLUTION), "#f8fafc") return preview, preview.copy() def _gpu_duration(*args, **kwargs) -> int: value = os.environ.get("ASASR_GPU_DURATION", "45") try: duration = int(value) except ValueError: duration = 240 return max(30, min(duration, 300)) @spaces.GPU(duration=1) def _zerogpu_probe() -> str: return "ready" @spaces.GPU(duration=_gpu_duration) def super_resolve( input_image: Image.Image, progress: gr.Progress = gr.Progress(track_tqdm=True), ): global LAST_INFERENCE_SECONDS if input_image is None: raise gr.Error("Upload or choose a 128 x 128 low-resolution image first.") pipe = _get_pipeline() lr_image = _prepare_lr_image(input_image) condition_image = _prepare_condition_image(lr_image) slider_lr_image = _prepare_slider_lr_image(lr_image) condition = Condition("sr", condition_image) seed_everything(42) start = time.perf_counter() result = generate( pipe, prompt="", conditions=[condition], default_lora=True, height=TARGET_RESOLUTION, width=TARGET_RESOLUTION, num_inference_steps=NUM_INFERENCE_STEPS, guidance_scale=GUIDANCE_SCALE, ).images[0] result = adain_color_fix(result, condition_image).convert("RGB") LAST_INFERENCE_SECONDS = time.perf_counter() - start load_text = ( f"Model load: {PIPELINE_LOAD_SECONDS:.1f}s. " if PIPELINE_LOAD_SECONDS is not None else "" ) status = ( f"Input size: {lr_image.width} x {lr_image.height}. " f"Output size: {result.width} x {result.height}. " f"{load_text}Inference: {LAST_INFERENCE_SECONDS:.1f}s." ) print(f"[ASASR] {status}") return (slider_lr_image, result), status _startup_prefetch() PAPER_URL = "https://arxiv.org/abs/2605.23264" GITHUB_URL = "https://github.com/wafer-bob/ASASR" MODEL_URL = "https://huggingface.co/wafer-bob/ASASR" DESCRIPTION_MD = ( "ASASR turns a low-resolution image into a faithful **512 × 512** reconstruction " "using a **FLUX.1-dev** backbone with **dual-LoRA inference** — a base SR LoRA plus an " "AS-DPO alignment LoRA. Upload a roughly **128 × 128** image or choose an example, " "then run the 28-step x4 sampler and scrub the slider to compare input and output." ) HERO_HTML = f"""
ICML 2026 FLUX.1-dev · Dual-LoRA · x4 SR

Coloring the Noise: Adversarial Sobolev Alignment
for Faithful Image Super-Resolution

Hongbo Wang · Huaibo Huang · Pin Wang · Jinhua Hao · Chao Zhou · Ran He

""" FOOTER_HTML = """ """ CSS = """ #asasr-root { max-width: 1180px; margin: 0 auto; } /* ---------- Hero ---------- */ .asasr-hero { text-align: center; padding: 2.4rem 1rem 1.4rem; } .asasr-badges { display: flex; gap: .55rem; justify-content: center; flex-wrap: wrap; margin-bottom: 1.1rem; } .asasr-badge { display: inline-flex; align-items: center; gap: .4rem; padding: .32rem .8rem; border-radius: 999px; font-size: .72rem; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; } .asasr-badge--icml { background: linear-gradient(135deg, #6366f1, #8b5cf6); color: #fff; box-shadow: 0 4px 14px rgba(99,102,241,.35); } .asasr-badge--soft { background: rgba(99,102,241,.10); color: #4338ca; border: 1px solid rgba(99,102,241,.25); } .asasr-title { font-size: clamp(1.55rem, 3.6vw, 2.55rem); line-height: 1.12; font-weight: 800; margin: 0 auto; max-width: 940px; letter-spacing: -0.025em; color: var(--body-text-color); } .asasr-title--sub { color: var(--body-text-color-subdued); font-weight: 600; } .asasr-authors { color: var(--body-text-color-subdued); margin: .85rem 0 1.25rem; font-size: .98rem; } .asasr-links { display: inline-flex; gap: .55rem; flex-wrap: wrap; justify-content: center; } .asasr-link { display: inline-flex; align-items: center; gap: .45rem; padding: .5rem 1.05rem; border-radius: 999px; text-decoration: none; font-weight: 600; font-size: .9rem; border: 1px solid var(--border-color-primary); color: var(--body-text-color); background: var(--background-fill-primary); transition: all .15s ease; } .asasr-link:hover { border-color: #6366f1; color: #4338ca; background: rgba(99,102,241,.08); } .asasr-link--primary { background: #4338ca; color: #fff; border-color: #4338ca; box-shadow: 0 4px 14px rgba(67,56,202,.3); } .asasr-link--primary:hover { background: #3730a3; color: #fff; border-color: #3730a3; } /* ---------- Description ---------- */ .asasr-desc { max-width: 760px; margin: 0 auto 1.6rem; text-align: center; color: var(--body-text-color-subdued); font-size: 1.02rem; line-height: 1.55; } .asasr-desc strong { color: var(--body-text-color); } /* ---------- Section labels ---------- */ .asasr-section-label { display: flex; align-items: center; gap: .6rem; margin: .2rem 0 .9rem; font-size: .78rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase; color: var(--body-text-color-subdued); } .asasr-section-label::after { content: ""; flex: 1; height: 1px; background: var(--border-color-primary); } /* ---------- Interface panels ---------- */ .asasr-panel { border: 1px solid var(--border-color-primary); border-radius: 16px; padding: .9rem .9rem 1.1rem; background: var(--background-fill-primary); } .asasr-panel--input { display: flex; flex-direction: column; gap: .8rem; } /* Input image frame */ .asasr-input-image { border-radius: 14px !important; border: 1px solid var(--border-color-primary) !important; overflow: hidden; } .asasr-input-image .image-frame, .asasr-input-image img { border-radius: 14px !important; } /* The centerpiece slider gets a subtle accent ring */ .asasr-slider-wrap { position: relative; border-radius: 18px; padding: .55rem; background: linear-gradient(180deg, rgba(99,102,241,.10), rgba(139,92,246,.05)); border: 1px solid rgba(99,102,241,.18); } .asasr-slider-wrap .component-wrapper { border: none !important; } /* Run button */ .asasr-run { width: 100%; height: 52px !important; font-size: 1rem !important; font-weight: 700 !important; border-radius: 12px !important; letter-spacing: .01em; } /* Status line */ .asasr-status textarea { font-family: var(--font-mono, ui-monospace, Menlo, monospace); font-size: .82rem !important; color: var(--body-text-color-subdued) !important; } /* ---------- Examples ---------- */ #asasr-examples, #asasr-examples .examples, #asasr-examples .table-wrap, #asasr-examples .table, #asasr-examples table, #asasr-examples tbody, #asasr-examples tr { max-height: none !important; overflow: hidden !important; } #asasr-examples img { width: 50px !important; height: 50px !important; min-width: 50px !important; min-height: 50px !important; max-width: 50px !important; max-height: 50px !important; object-fit: cover !important; border-radius: 8px !important; } #asasr-examples button, #asasr-examples .example, #asasr-examples td { width: 58px !important; height: 58px !important; min-width: 58px !important; max-width: 58px !important; padding: 4px !important; overflow: hidden !important; } #asasr-examples, #asasr-examples * { scrollbar-width: none !important; } #asasr-examples::-webkit-scrollbar, #asasr-examples *::-webkit-scrollbar { display: none !important; } /* ---------- Footer ---------- */ .asasr-footer { margin-top: 1.8rem; padding-top: 1.2rem; border-top: 1px solid var(--border-color-primary); } .asasr-license { font-size: .82rem; color: var(--body-text-color-subdued); line-height: 1.5; } .asasr-license strong { color: var(--body-text-color); } .asasr-citation { margin-top: 1rem; border: 1px solid var(--border-color-primary); border-radius: 12px; background: var(--background-fill-secondary); overflow: hidden; } .asasr-citation summary { cursor: pointer; padding: .65rem 1rem; font-weight: 600; font-size: .9rem; list-style: none; display: flex; align-items: center; gap: .5rem; } .asasr-citation summary::before { content: "▸"; color: var(--body-text-color-subdued); transition: transform .15s ease; } .asasr-citation[open] summary::before { transform: rotate(90deg); } .asasr-citation summary::-webkit-details-marker { display: none; } .asasr-citation pre { margin: 0; padding: 1rem 1.1rem; font-family: var(--font-mono, ui-monospace, Menlo, monospace); font-size: .82rem; line-height: 1.5; overflow-x: auto; background: transparent; border-top: 1px solid var(--border-color-primary); } """ THEME = gr.themes.Soft( primary_hue=colors.indigo, secondary_hue=colors.violet, neutral_hue=colors.slate, radius_size=sizes.radius_lg, text_size=sizes.text_lg, ) with gr.Blocks( title="ASASR · Faithful x4 Image Super-Resolution", elem_id="asasr-root", ) as demo: gr.HTML(HERO_HTML, container=False, padding=False, apply_default_css=False) gr.Markdown(DESCRIPTION_MD, elem_classes=["asasr-desc"]) with gr.Row(equal_height=False): with gr.Column(scale=1, min_width=320): gr.HTML('
Input
') with gr.Column(elem_classes=["asasr-panel", "asasr-panel--input"]): input_image = gr.Image( label="Low-resolution input (≈128×128)", type="pil", height=360, elem_classes=["asasr-input-image"], ) run_button = gr.Button( "✦ Super-Resolve", variant="primary", elem_classes=["asasr-run"], ) with gr.Column(scale=2, min_width=480): gr.HTML('
Result · drag to compare
') with gr.Column(elem_classes=["asasr-slider-wrap"]): comparison_slider = ImageSlider( label="Input LR | ASASR HR", value=_initial_slider_value(), type="pil", height=560, position=0.5, interactive=False, slider_color="#4338ca", ) status = gr.Textbox( label="Runtime", value=STARTUP_NOTE, interactive=False, elem_classes=["asasr-status"], ) gr.HTML('
Examples
') gr.Examples( examples=[[path] for path in EXAMPLE_FILES], inputs=input_image, outputs=[comparison_slider, status], fn=super_resolve, cache_examples=True, cache_mode="lazy", examples_per_page=6, label="Example inputs", elem_id="asasr-examples", run_on_click=True, ) gr.HTML(FOOTER_HTML, container=False, padding=False, apply_default_css=False) run_button.click( fn=super_resolve, inputs=input_image, outputs=[comparison_slider, status], api_name="super_resolve", ) if __name__ == "__main__": demo.launch(theme=THEME, css=CSS)