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''
for name, label, href in group
)
+ ""
for group in (LINKS[:3], LINKS[3:])
)
gr.HTML(
f"""
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.