import base64 import os from pathlib import Path os.system("pip freeze") import spaces import tempfile import shutil import gradio as gr import torch as torch from gradio_dualvision import DualVisionApp from huggingface_hub import login from PIL import Image from windowseat_inference import load_network, run_inference uri_base = "Qwen/Qwen-Image-Edit-2509" uri_lora = "huawei-bayerlab/windowseat-reflection-removal-v1-0" if "HF_TOKEN_LOGIN" in os.environ: login(token=os.environ["HF_TOKEN_LOGIN"]) device = torch.device("cuda" if torch.cuda.is_available() else "cpu") dtype = torch.bfloat16 if torch.cuda.is_available() else torch.float32 vae, transformer, embeds_dict, processing_resolution = load_network(uri_base, uri_lora, device) # # As of transformers==4.57.1 , xformers is not supported in QwenImageTransformer2DModel # try: # transformer.enable_xformers_memory_efficient_attention() # print("xformers enabled") # except: # print("xformers not enabled") SHIELDS_DIR = Path(__file__).parent / "assets" / "shields" LINKS = [ ("website", "Website", "https://hf.co/spaces/huawei-bayerlab/windowseat-reflection-removal-web"), ("paper", "Paper", "https://arxiv.org/abs/2512.05000"), ("code", "Code", "https://github.com/huawei-bayerlab/windowseat-reflection-removal"), ("weights", "Weights", "https://hf.co/huawei-bayerlab/windowseat-reflection-removal-v1-0"), ("follow", "Follow", "https://twitter.com/antonobukhov1"), ] # Badges are the committed SVGs, shared with the README, model card, and website. Inlined # as data URIs so the HTML header needs no static file route. BADGES = { name: "data:image/svg+xml;base64," + base64.b64encode((SHIELDS_DIR / f"{name}.svg").read_bytes()).decode() for name, _, _ in LINKS } HEADER_CSS = """ .ws-header { max-width: 70vw; margin: 0 auto; display: grid; grid-template-columns: minmax(0, 6fr) minmax(0, 7fr); gap: 28px; align-items: center; color: var(--body-text-color); } .ws-identity { display: flex; flex-direction: column; gap: 6px; border-right: 1px solid var(--border-color-primary); padding-right: 28px; } .ws-titles { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 14px; } .ws-title { font-family: 'Instrument Serif', Georgia, serif; font-weight: 400; font-size: 36px; line-height: 40px; color: var(--body-text-color); } .ws-subtitle { font-size: 12px; line-height: 16px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--body-text-color-subdued); } .ws-links { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; margin-top: 8px; } .ws-link-group { display: inline-flex; gap: inherit; white-space: nowrap; } .ws-links a { display: inline-flex; line-height: 0; } .ws-links img { height: 22px; width: auto; transition: transform 0.15s ease; } .ws-links a:hover img { transform: translateY(-1px); } .ws-intro { margin: 0; font-size: 14px; line-height: 20px; } @media (max-width: 900px) { .ws-header { max-width: none; grid-template-columns: minmax(0, 1fr); gap: 10px; } .ws-identity { display: grid; grid-template-columns: max-content minmax(0, 1fr); column-gap: 16px; row-gap: 4px; align-items: center; border-right: 0; border-bottom: 1px solid var(--border-color-primary); padding: 0 0 10px 0; } .ws-titles { display: contents; } .ws-title { grid-column: 1; grid-row: 1; font-size: 30px; line-height: 34px; } .ws-links { grid-column: 2; grid-row: 1; justify-content: flex-end; gap: 6px 8px; margin: 0; } .ws-subtitle { grid-column: 1 / -1; grid-row: 2; } .ws-intro { font-size: 13px; line-height: 18px; } } @media (max-width: 529px) { .ws-identity { grid-template-columns: minmax(0, 1fr) max-content; } .ws-subtitle { grid-column: 1; font-size: 10px; letter-spacing: 0.12em; } .ws-links { grid-row: 1 / span 2; align-self: center; flex-direction: column; align-items: flex-end; gap: 6px; } .ws-link-group { gap: 6px; } .ws-links img { height: 20px; } } @media (max-width: 370px) { .ws-title { font-size: 26px; line-height: 30px; } } /* Footer links wrap as whole items; outside .contain, so plain rules only. */ footer.svelte-1byz9vf { flex-wrap: wrap; justify-content: center; gap: 2px 24px; } footer.svelte-1byz9vf a { white-space: nowrap; } footer.svelte-1byz9vf .divider { display: none; } /* Examples block as wide as the drop zone, tiles in a full rectangle (column counts that divide 12). */ .block:has(> .gallery.svelte-p5q82i) { max-width: 70vw; margin-left: auto; margin-right: auto; } .gallery.svelte-p5q82i.svelte-p5q82i.svelte-p5q82i { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: var(--spacing-lg); } .gallery-item .gallery.svelte-a9zvka { width: 100%; min-width: 0; max-width: none; height: auto; min-height: 0; aspect-ratio: 1; } .gallery.svelte-p5q82i .gallery-item img { width: 100%; min-width: 0; max-width: none; height: 100%; min-height: 0; object-fit: cover; } @media (max-width: 900px) { .block:has(> .gallery.svelte-p5q82i) { max-width: none; } .sliderrow .slider { max-width: none; width: 100%; } .slider .wrap.half-wrap { width: 100%; } } @media (max-width: 829px) { .gallery.svelte-p5q82i.svelte-p5q82i.svelte-p5q82i { grid-template-columns: repeat(4, minmax(0, 1fr)); } } @media (max-width: 529px) { .gallery.svelte-p5q82i.svelte-p5q82i.svelte-p5q82i { grid-template-columns: repeat(3, minmax(0, 1fr)); } } """ HEADER_HEAD = '' class WindowSeatApp(DualVisionApp): DEFAULT_SEED = 2025 def make_header(self): links = "".join( '' + "".join( f'' f'{label}' for name, label, href in group ) + "" for group in (LINKS[:3], LINKS[3:]) ) gr.HTML( f"""
WindowSeat
Image Reflection Removal

The authors' demo of Reflection Removal through Efficient Adaptation of Diffusion Transformers. Upload a photo through glass or pick an example below, wait for the result, then drag the slider to compare it with the input and zoom in for detail. If a quota limit appears, duplicate the space to continue.

""", padding=False, ) def build_user_components(self): return {} def process(self, image_in: Image.Image, **kwargs): input_temp_dir = tempfile.mkdtemp() output_temp_dir = tempfile.mkdtemp() try: input_image_path = os.path.join(input_temp_dir, "image.png") image_in.save(input_image_path) run_inference( vae, transformer, embeds_dict, processing_resolution, input_temp_dir, output_temp_dir, use_short_edge_tile=True, save_comparison=False, save_alternating=False, ) output_image_path = os.path.join(output_temp_dir, "image_windowseat_output.png") result_image = Image.open(output_image_path) result_image.load() out_modalities = { "Result": result_image, } out_settings = {} return out_modalities, out_settings finally: if os.path.exists(input_temp_dir): shutil.rmtree(input_temp_dir) if os.path.exists(output_temp_dir): shutil.rmtree(output_temp_dir) with WindowSeatApp( title="WindowSeat Reflection Removal", examples_path="example_images", examples_per_page=12, right_selector_visible=False, advanced_settings_visible=False, squeeze_canvas=True, spaces_zero_gpu_enabled=True, css=HEADER_CSS, head=HEADER_HEAD, ) as demo: demo.queue( api_open=False, ).launch( server_name="0.0.0.0", server_port=7860, ssr_mode=False, )