"""NL-Diffusion-Image Gradio demo for Hugging Face Spaces (and local testing). Local test with private Hub model: conda activate lavida export HF_TOKEN=hf_... export MODEL_ID=nvidia/NL-Diffusion-Image python app.py Post-generation NSFW guard (on by default — opt out with ENABLE_IMAGE_GUARD=0): export ENABLE_IMAGE_GUARD=0 python app.py """ from __future__ import annotations import gc import os import random import tempfile import time from contextlib import contextmanager from pathlib import Path from typing import Any ASSETS_DIR = Path(__file__).resolve().parent / "assets" def _asset(name: str) -> str: return str(ASSETS_DIR / name) import gradio as gr import imageio.v3 as iio import torch from PIL import ImageDraw from transformers import AutoModelForCausalLM, PreTrainedTokenizerFast from image_guard import ( DEFAULT_IMAGE_GUARD_MODEL_ID, DEFAULT_IMAGE_GUARD_THRESHOLD, ImageGuard, ) try: import spaces except ImportError: class _SpacesStub: @staticmethod def GPU(*args, **kwargs): def decorator(fn): return fn if args and callable(args[0]): return args[0] return decorator spaces = _SpacesStub() # The released model drives its denoising loop with a tqdm bar over a # length-less iterable (`enumerate(...)` plus a separate `total=`). Gradio's # track_tqdm ignores that explicit total and the bar has no description, so the # UI shows a bogus "Downloading (incomplete total...)" indicator. This shim # backfills the missing length/description onto the tracked bar, but only while # a generation is in flight (so download bars during model load are untouched). _DENOISE_PROGRESS = {"active": False, "total": None, "desc": "Generating image"} def _install_progress_shim() -> None: try: from gradio import helpers as _gr_helpers except Exception: return if getattr(_gr_helpers.Progress.tqdm, "_denoise_shim", False): return _orig_tqdm = _gr_helpers.Progress.tqdm def _patched_tqdm(self, *args, **kwargs): out = _orig_tqdm(self, *args, **kwargs) try: if _DENOISE_PROGRESS["active"] and getattr(self, "iterables", None): ti = self.iterables[-1] if getattr(ti, "length", None) in (None, 0) and _DENOISE_PROGRESS["total"]: ti.length = _DENOISE_PROGRESS["total"] if not getattr(ti, "desc", None): ti.desc = _DENOISE_PROGRESS["desc"] except Exception: pass return out _patched_tqdm._denoise_shim = True _gr_helpers.Progress.tqdm = _patched_tqdm _install_progress_shim() @contextmanager def _suppress_tqdm_tracking(): """Stop track_tqdm from capturing tqdm bars (e.g. Hugging Face download bars during model loading), so only our explicit status message shows.""" try: from gradio.context import LocalContext except Exception: yield return token = LocalContext.progress.set(None) try: yield finally: try: LocalContext.progress.reset(token) except Exception: pass os.environ.setdefault("DEBUG_FIX_PADDING", "1") os.environ.setdefault("NOT_ALWASY_DO_2DPOOL", "1") if "CUDA_HOME" not in os.environ: _local_cuda = "/lustre/fsw/portfolios/llmservice/users/gheinrich/cuda/cuda_12.4" if os.path.isdir(_local_cuda): os.environ["CUDA_HOME"] = _local_cuda def resolve_hf_token() -> str | None: """Return an HF token from any of the common env var names (or None).""" for var in ("HF_TOKEN", "HUGGING_FACE_HUB_TOKEN", "HUGGINGFACEHUB_API_TOKEN"): value = os.getenv(var) if value: return value.strip() return None HF_TOKEN = resolve_hf_token() def _log_token_status() -> None: if HF_TOKEN: masked = f"{HF_TOKEN[:4]}…{HF_TOKEN[-2:]}" if len(HF_TOKEN) > 6 else "set" print(f"HF token detected ({masked}).", flush=True) else: print( "WARNING: no HF token found (HF_TOKEN / HUGGING_FACE_HUB_TOKEN). " "Private models will fail to load with a 404. " "Add an HF_TOKEN secret in the Space Settings with read access.", flush=True, ) _log_token_status() MODEL_ID = os.getenv("MODEL_ID", "nvidia/NL-Diffusion-Image") DEVICE = os.getenv("DEVICE", "cuda") IMAGE_GUARD_MODEL_ID = os.getenv("IMAGE_GUARD_MODEL_ID", DEFAULT_IMAGE_GUARD_MODEL_ID) IMAGE_GUARD_THRESHOLD = float( os.getenv("IMAGE_GUARD_THRESHOLD", str(DEFAULT_IMAGE_GUARD_THRESHOLD)) ) IMAGE_GUARD_OFFLOAD_T2I = os.getenv("IMAGE_GUARD_OFFLOAD_T2I", "0") == "1" # Opt-out: guard runs by default; set ENABLE_IMAGE_GUARD=0 or uncheck the UI box to disable. DEFAULT_ENABLE_IMAGE_GUARD = os.getenv("ENABLE_IMAGE_GUARD", "1") == "1" # Pre-generation prompt safety check (input filter), same content-safety model. DEFAULT_ENABLE_PROMPT_GUARD = os.getenv("ENABLE_PROMPT_GUARD", "1") == "1" GUARD_ACCESS_HELP = ( "https://huggingface.co/nvidia/Nemotron-3.5-Content-Safety" ) def _guard_unavailable_message(exc: Exception) -> str: text = str(exc).lower() if "gated repo" in text or "403" in text or "authorized list" in text: return ( "NSFW filter is enabled but Nemotron 3.5 Content Safety is not accessible. " f"See {GUARD_ACCESS_HELP}, ensure HF_TOKEN has read access, " "or uncheck 'NSFW output filter' to opt out." ) return f"NSFW filter is enabled but Nemotron 3.5 Content Safety failed: {exc}" def _report_guard_failure(message: str) -> None: """Surface guard failures in the Gradio UI without breaking output components.""" gr.Warning(message) print(f"GUARD ERROR: {message}", flush=True) # Defaults aligned with nemotron-diffusion-omni/gradio_t2i_demo.py DEFAULT_PROMPT = ( "A full-body shot of hyper-realistic female cyborg, human facial skin seamlessly integrated " "with a glossy white mechanical head shell. Features a realistic human ear, blue eyes. bright, " "outdoor, background with blue sky, illuminated by striking bright white studio lighting, " "casting soft shadows. Cyberpunk aesthetic, high-tech minimalism, shot on 85mm lens, " "photorealistic, Unreal Engine 5 render, intricately detailed, 8k resolution, high dynamic " "range, chest with whit armor plate, cute, beautiful, sexy, glossy surface, reflective, " "Artstation, pixiv, no hair, 3D render, stylized eyesz" ) DEFAULT_MICRO_COND = ( "ORIGINAL WIDTH : 1024; ORIGINAL HEIGHT : 1024; TOP : 0; LEFT : 0; " "SCORE : 6.520; HPS: 3.220" ) EXAMPLE_PROMPTS = [ "A photorealistic portrait of an astronaut riding a horse on the moon, " "golden hour lighting, 85mm lens, ultra detailed, sharp focus", "A cozy bookstore cafe interior, warm lighting, hanging plants, wooden shelves, " "cinematic, highly detailed", "A majestic snow leopard standing on a rocky cliff, national geographic " "photography, crisp fur detail, soft bokeh background", "A futuristic city skyline at dusk, neon signs, wet streets with reflections, " "cyberpunk aesthetic, 8k, cinematic lighting", "A steaming bowl of ramen with a soft-boiled egg, studio food photography, " "shallow depth of field, rich colors", "An oil painting of a lighthouse on a stormy coast, dramatic clouds, crashing " "waves, impressionist style", ] CITATION_BIBTEX = """@article{li2026nemotron, title = {Nemotron-Labs-Diffusion-Image: Advancing Masked Discrete Diffusion for High-Resolution Image Synthesis}, author = {Li, Shufan and Heinrich, Greg and Ye, Hanrong and Fu, Yonggan and Grover, Aditya and Kautz, Jan and Molchanov, Pavlo}, journal = {arXiv preprint arXiv:2606.29814}, year = {2026} }""" DEFAULT_GENERATION_CONFIG: dict[str, Any] = { "guidance_scale": 5.0, "n_steps": 64, "shift": 5, "alg_temp": 1.0, "dynamic_temperature": False, "min_temperature": 0.01, "schedule_temp": "linear", "temperature": 0.86, "confidence_policy": "mmada", "micro_cond": DEFAULT_MICRO_COND, "edit_threshold": 0.6, "is_legacy": False, } def n_tokens_from_resolution(image_resolution: int) -> int: return (image_resolution // 16) * (image_resolution // 16) def process_gif(image_list): if not image_list: return None with tempfile.NamedTemporaryFile(suffix=".gif", delete=False) as tmp_file: gif_path = tmp_file.name frames = [] total_frames = len(image_list) for i, img in enumerate(image_list): frame = img.resize((400, 400)) draw = ImageDraw.Draw(frame) text = f"Frame: {i + 1} / {total_frames}" x, y = 15, 15 for dx, dy in [(-1, -1), (1, -1), (-1, 1), (1, 1)]: draw.text((x + dx, y + dy), text, fill="black") draw.text((x, y), text, fill="white") frames.append(frame) duration = [1000 / 20] * len(frames) duration[-1] = 2000 iio.imwrite(gif_path, frames, extension=".gif", duration=duration, loop=0) return gif_path def process_webp(pil_image): with tempfile.NamedTemporaryFile(suffix=".webp", delete=False) as tmp_file: webp_path = tmp_file.name pil_image.save(webp_path, "webp", quality=95) return webp_path def load_release_model_and_tokenizer(model_id: str, device: str): hf_token = HF_TOKEN if hf_token is None and not os.path.isdir(model_id): raise RuntimeError( f"Cannot load '{model_id}': no HF token found. " "This is a private repo — add an HF_TOKEN secret in the Space Settings " "(Settings → Variables and secrets) using a token with read access to " f"{model_id}." ) tokenizer = PreTrainedTokenizerFast.from_pretrained(model_id, token=hf_token) if tokenizer.pad_token_id is None: tokenizer.pad_token_id = tokenizer.eos_token_id tokenizer.pad_token = tokenizer.eos_token model = AutoModelForCausalLM.from_pretrained( model_id, trust_remote_code=True, torch_dtype=torch.bfloat16, low_cpu_mem_usage=False, token=hf_token, ) model.to(device) model.eval() model.requires_grad_(False) model.config.dlm_paradigm = "bidirectional" return tokenizer, model def _format_guard_meta(result) -> str: return ( f"guard={result.model_id} | label={result.label} | " f"unsafe_score={result.score:.3f} | guard_time={result.inference_seconds:.2f}s" ) class T2IEngine: def __init__(self, model_id: str, device: str = "cuda") -> None: self.model_id = model_id self.device = device self._tokenizer = None self._model = None self._image_guard: ImageGuard | None = None def _lazy_load(self) -> None: if self._model is not None and self._tokenizer is not None: return print(f"Loading model from {self.model_id} ...", flush=True) self._tokenizer, self._model = load_release_model_and_tokenizer( self.model_id, self.device ) print("Model loaded.", flush=True) def _get_image_guard(self) -> ImageGuard: if self._image_guard is None: print(f"Loading image guard from {IMAGE_GUARD_MODEL_ID} ...", flush=True) self._image_guard = ImageGuard( model_id=IMAGE_GUARD_MODEL_ID, threshold=IMAGE_GUARD_THRESHOLD, device=self.device, hf_token=HF_TOKEN, ) return self._image_guard def _offload_t2i_to_cpu(self) -> None: if self._model is not None: self._model.to("cpu") gc.collect() if torch.cuda.is_available(): torch.cuda.empty_cache() def _reload_t2i_to_device(self) -> None: if self._model is not None: self._model.to(self.device) def _moderate_output( self, result, return_animation: bool, enable_image_guard: bool, ) -> tuple[bool, str]: """Run post-generation moderation. Returns (ok, meta_suffix_or_error_message).""" if not enable_image_guard: return True, "" if IMAGE_GUARD_OFFLOAD_T2I: self._offload_t2i_to_cpu() try: with _suppress_tqdm_tracking(): guard = self._get_image_guard() frames = result if return_animation else [result] guard_parts = [] for frame_idx, frame in enumerate(frames): check = guard.check_image(frame) guard_parts.append(_format_guard_meta(check)) if not check.passed: message = ( "Generated image blocked by NSFW filter " f"(frame {frame_idx + 1}/{len(frames)}, " f"unsafe_score={check.score:.3f}, threshold={IMAGE_GUARD_THRESHOLD})." ) _report_guard_failure(message) return False, message return True, " | " + guard_parts[0] if guard_parts else "" except Exception as exc: message = _guard_unavailable_message(exc) _report_guard_failure(message) return False, message finally: if IMAGE_GUARD_OFFLOAD_T2I: self._reload_t2i_to_device() def _moderate_prompt(self, prompt: str) -> tuple[bool, str]: """Run pre-generation prompt moderation. Returns (ok, meta_or_error).""" try: with _suppress_tqdm_tracking(): guard = self._get_image_guard() check = guard.check_text(prompt) if not check.passed: message = ( "Prompt blocked by content-safety filter " f"(label={check.label})." ) _report_guard_failure(message) return False, message return True, "prompt_" + _format_guard_meta(check) except Exception as exc: message = _guard_unavailable_message(exc) _report_guard_failure(message) return False, message def generate( self, prompt: str, image_resolution: int, guidance_scale: float, temperature: float, n_steps: int, shift: int, confidence_policy: str, schedule_temp: str, alg_temp: float, dynamic_temperature: bool, min_temperature: float, edit_threshold: float, seed: int, micro_cond: str, return_animation: bool, enable_image_guard: bool = DEFAULT_ENABLE_IMAGE_GUARD, enable_prompt_guard: bool = DEFAULT_ENABLE_PROMPT_GUARD, progress: gr.Progress | None = None, ): prompt_guard_meta = "" if enable_prompt_guard: if progress is not None: progress(0.0, desc="Checking prompt…") prompt_ok, prompt_guard_meta = self._moderate_prompt(prompt) if not prompt_ok: return None, f"ERROR: {prompt_guard_meta}" if progress is not None and self._model is None: progress(0.0, desc="Loading model (first run, this can take 1-2 min)…") with _suppress_tqdm_tracking(): self._lazy_load() gen_cfg = dict(DEFAULT_GENERATION_CONFIG) gen_cfg.update( micro_cond=micro_cond, guidance_scale=guidance_scale, temperature=temperature, edit_threshold=edit_threshold, n_steps=int(n_steps), shift=int(shift), confidence_policy=confidence_policy, schedule_temp=schedule_temp, alg_temp=alg_temp, dynamic_temperature=dynamic_temperature, min_temperature=min_temperature, ) if seed < 0: seed = int(torch.seed() % (2**31 - 1)) torch.manual_seed(int(seed)) n_tokens = n_tokens_from_resolution(int(image_resolution)) if progress is not None: progress(0.0, desc="Generating…") _DENOISE_PROGRESS["total"] = int(n_steps) _DENOISE_PROGRESS["active"] = progress is not None t0 = time.time() try: with torch.no_grad(): with torch.inference_mode(): result = self._model.text_to_image( prompt, tokenizer=self._tokenizer, **gen_cfg, image_resolution=int(image_resolution), n_tokens=n_tokens, disable_tqdm=progress is None, return_intermediate_steps=return_animation, ) finally: _DENOISE_PROGRESS["active"] = False latency = time.time() - t0 meta = ( f"model={self.model_id} | seed={seed} | res={image_resolution} | " f"n_tokens={n_tokens} | steps={n_steps} | " f"cfg={guidance_scale:.2f} | temp={temperature:.3f} | " f"shift={shift} | alg_temp={alg_temp:.2f} | " f"dyn_temp={dynamic_temperature} | min_temp={min_temperature:.3f} | " f"sch_temp={schedule_temp} | conf={confidence_policy} | " f"edit_threshold={edit_threshold:.3f} | gen_time={latency:.2f}s" ) if prompt_guard_meta: meta += " | " + prompt_guard_meta if progress is not None and enable_image_guard: progress(1.0, desc="Running safety filter…") guard_ok, guard_meta = self._moderate_output( result, return_animation, enable_image_guard ) if not guard_ok: return None, f"ERROR: {guard_meta}\n\n{meta}" meta += guard_meta if return_animation: return process_gif(result), meta return process_webp(result), meta engine = T2IEngine(model_id=MODEL_ID, device=DEVICE) @spaces.GPU(duration=240) def generate( prompt: str, image_resolution: int, guidance_scale: float, temperature: float, n_steps: int, shift: int, confidence_policy: str, schedule_temp: str, alg_temp: float, dynamic_temperature: bool, min_temperature: float, edit_threshold: float, seed: int, micro_cond: str, return_animation: bool, progress: gr.Progress = gr.Progress(track_tqdm=True), ): return engine.generate( prompt, image_resolution, guidance_scale, temperature, n_steps, shift, confidence_policy, schedule_temp, alg_temp, dynamic_temperature, min_temperature, edit_threshold, seed, micro_cond, return_animation, enable_image_guard=DEFAULT_ENABLE_IMAGE_GUARD, enable_prompt_guard=DEFAULT_ENABLE_PROMPT_GUARD, progress=progress, ) def make_theme() -> gr.themes.Base: nvidia_green = gr.themes.Color( c50="#f3f9e6", c100="#e3f1c2", c200="#cfe88f", c300="#b6dc56", c400="#97c61f", c500="#76b900", c600="#69a600", c700="#548400", c800="#3f6300", c900="#2a4200", c950="#1a2900", ) return gr.themes.Soft( primary_hue=nvidia_green, secondary_hue=nvidia_green, font=[ gr.themes.GoogleFont("Inter"), "ui-sans-serif", "system-ui", "sans-serif", ], font_mono=[ gr.themes.GoogleFont("JetBrains Mono"), "ui-monospace", "monospace", ], ) # Gradio 6.0 moved `theme` from Blocks(...) to launch(...). _GRADIO_MAJOR = int(gr.__version__.split(".")[0]) def build_demo() -> gr.Blocks: theme = make_theme() blocks_kwargs: dict[str, Any] = {"title": "Nemotron Labs Diffusion Image"} if _GRADIO_MAJOR < 6: blocks_kwargs["theme"] = theme with gr.Blocks(**blocks_kwargs) as demo: gr.Markdown( "# Nemotron Labs Diffusion Image\n\n" "NL-Diffusion-Image generates high-resolution images via **masked discrete diffusion** " "over tokenized image patches. Each image is encoded into discrete tokens " "(131K codebook), and generation proceeds through iterative parallel unmasking—similar " "to diffusion LLMs. The model is fine-tuned from " "[Nemotron-Labs-Diffusion](https://huggingface.co/nvidia/Nemotron-Labs-Diffusion-8B) " "with two key additions:\n\n" "- **Token editing** — revise already-unmasked tokens during inference.\n" "- **Grouped Cross-Entropy (GCE)** — efficient training with large vocabularies.\n\n" "This aligns image generation with LLM training and inference infrastructure, " "making it highly scalable.\n\n" "📄 [Paper (arXiv:2606.29814)](https://arxiv.org/abs/2606.29814) · " "🤗 [Model](https://huggingface.co/nvidia/NL-Diffusion-Image)" ) gr.Markdown( "| GenEval | DPG | HPSv3 | Speed vs EMU3.5 |\n" "|:---:|:---:|:---:|:---:|\n" "| **0.90** | **86.9** | **10.76** | **42.4× faster** |" ) gr.Markdown("## Generate an image") with gr.Row(): with gr.Column(scale=2): prompt = gr.Textbox(label="Prompt", lines=4, value=DEFAULT_PROMPT) with gr.Row(): image_resolution = gr.Dropdown( choices=[256, 512, 1024], value=1024, label="Image Resolution", ) n_steps = gr.Slider( minimum=1, maximum=128, value=64, step=1, label="Diffusion Steps" ) with gr.Row(): guidance_scale = gr.Slider( minimum=1.0, maximum=10.0, value=5.0, step=0.1, label="Guidance Scale" ) temperature = gr.Slider( minimum=0.05, maximum=1.5, value=0.86, step=0.01, label="Temperature" ) with gr.Row(): seed = gr.Number( label="Seed (-1 for random)", value=42, precision=0, scale=3 ) randomize_seed_btn = gr.Button("🎲 Randomize", scale=1) gr.Markdown( "Safety filters (Nemotron 3.5 Content Safety) run on both the " "prompt and the generated image and cannot be disabled." ) generate_btn = gr.Button("Generate", variant="primary") with gr.Accordion("Advanced settings", open=False): micro_cond = gr.Textbox( label="Micro Cond", lines=2, value=DEFAULT_MICRO_COND ) with gr.Row(): shift = gr.Slider( minimum=0, maximum=16, value=5, step=1, label="Shift" ) confidence_policy = gr.Dropdown( choices=["mask_git", "mmada", "stratified"], value="mmada", label="Confidence Policy", ) with gr.Row(): schedule_temp = gr.Dropdown( choices=["linear", "cosine2", "shift", "exp"], value="linear", label="Schedule Temp", ) alg_temp = gr.Slider( minimum=0.1, maximum=3.0, value=1.0, step=0.1, label="Alg Temp" ) dynamic_temperature = gr.Checkbox(label="Dynamic Temp", value=False) with gr.Row(): min_temperature = gr.Slider( minimum=0.0, maximum=1.0, value=0.01, step=0.01, label="Min Temp" ) edit_threshold = gr.Slider( minimum=-1.0, maximum=1.0, value=0.6, step=0.01, label="Edit Threshold", ) return_animation = gr.Checkbox( label="Return Animation (resized to 400x400 for preview)", value=False, ) with gr.Column(scale=3): output_image = gr.Image(label="Generated Image", type="filepath") output_meta = gr.Textbox(label="Generation Info", lines=6) gr.Examples(examples=EXAMPLE_PROMPTS, inputs=[prompt], label="Example prompts") randomize_seed_btn.click( fn=lambda: random.randint(0, 2**31 - 1), inputs=None, outputs=seed ) generate_btn.click( fn=generate, inputs=[ prompt, image_resolution, guidance_scale, temperature, n_steps, shift, confidence_policy, schedule_temp, alg_temp, dynamic_temperature, min_temperature, edit_threshold, seed, micro_cond, return_animation, ], outputs=[output_image, output_meta], ) with gr.Accordion("About the model", open=True): gr.Markdown( "_Masked Discrete Diffusion · Text-to-Image Synthesis · Token Editing · " "Grouped Cross-Entropy (GCE) · High-Resolution Image Generation_" ) gr.Markdown("### Sample outputs") gr.Gallery( value=[ _asset("demo_1.gif"), _asset("demo_2.gif"), _asset("demo_3.gif"), ], columns=3, height="auto", object_fit="contain", show_label=False, ) gr.Markdown( "### Generation speed\n\n" "Side-by-side at 1024×1024. **Left:** NL-Diffusion-Image. " "**Right:** EMU3.5 (autoregressive). NL-Diffusion-Image is **42.4× faster** " "while scoring higher on GenEval." ) gr.Image( value=_asset("speed_comparison.gif"), show_label=False, interactive=False, ) with gr.Row(equal_height=True): with gr.Column(scale=1): gr.Markdown( "### Architecture\n\n" "16×16 image patches are encoded with a pretrained discrete tokenizer " "from EMU3.5 (128K codebook). The Nemotron-Labs-Diffusion vocabulary is " "expanded with randomly initialized embeddings and fine-tuned on " "image/caption pairs." ) with gr.Column(scale=1): gr.Image( value=_asset("architecture.png"), show_label=False, interactive=False, ) with gr.Row(equal_height=True): with gr.Column(scale=1): gr.Markdown( "### Benchmarks\n\n" "State-of-the-art among discrete image generators at 1024px text-to-image, " "surpassing prior masked image generators on quality while remaining " "dramatically faster than autoregressive baselines." ) with gr.Column(scale=1): gr.Image( value=_asset("benchmarks.png"), show_label=False, interactive=False, ) gr.Markdown("### Key findings") with gr.Row(equal_height=True): with gr.Column(scale=1): gr.Markdown( "**Token editing for self-correction**\n\n" "Token editing lets the model iteratively refine outputs during inference, " "correcting artifacts and improving texture detail." ) with gr.Column(scale=1): gr.Image( value=_asset("token_editing.png"), show_label=False, interactive=False, ) with gr.Row(equal_height=True): with gr.Column(scale=1): gr.Markdown( "**Grouped Cross-Entropy (GCE)**\n\n" "GCE alleviates codebook sparsity by supervising semantically close " "non-top-1 tokens in embedding space.\n\n" "A fused GCE operator cuts peak VRAM from 25.2 GB to 16.1 GB and latency " "from 44.14 ms to 20.04 ms versus an eager implementation." ) with gr.Column(scale=1): gr.Image( value=_asset("gce_objective.png"), show_label=False, interactive=False, ) with gr.Row(equal_height=True): with gr.Column(scale=1): gr.Markdown( "**Few-step generation**\n\n" "Unlike continuous flow-matching models that predict blurry mean fields at " "low step counts, NL-Diffusion-Image produces reasonable quality in as few " "as 4 steps without distillation." ) with gr.Column(scale=1): gr.Image( value=_asset("few_step_generation.png"), show_label=False, interactive=False, ) gr.Markdown( "**Future work:** extend the model to unified vision generation and understanding." ) gr.Markdown("### Citation") gr.Code(value=CITATION_BIBTEX, language=None, label="BibTeX") return demo demo = build_demo() if __name__ == "__main__": launch_kwargs: dict[str, Any] = { "server_name": os.getenv("HOST", "0.0.0.0"), "server_port": int(os.getenv("PORT", "7860")), } if _GRADIO_MAJOR >= 6: launch_kwargs["theme"] = make_theme() demo.queue(default_concurrency_limit=1).launch(**launch_kwargs)