WindowSeat Reflection Removal
Scrub the tourist out of the glass
- model
- image
- image
You know the shot: a shop window, a museum case, a car windshield, and right in the middle of it, the photographer reflecting back like a ghost. That's the problem this node exists to kill. WindowSeat Reflection Removal takes an image plus a loaded WindowSeat model and returns a clean, reflection-free version - the actual workhorse of the pack, sitting downstream of the WindowSeat Model Loader.
It's from Huawei Bayerlab's WindowSeat project (arXiv 2512.05000), ported to ComfyUI by toshas, one of the paper's authors. Real restoration, not a Photoshop slider - the kind of thing that used to be a paid cleanup job.
How it actually works
This is the interesting part, and it's why WindowSeat runs on a single GPU at all. Instead of iterating through dozens of denoising steps like normal image-editing diffusion, the model is a Qwen-Image-Edit-2509 transformer fine-tuned with a LoRA to predict the reflection-free image in a single flow step at a fixed timestep. That "efficient adaptation" in the paper title isn't marketing - one transformer pass, VAE encode on one side, decode on the other, done.
Large photos are handled by tiling: the node computes a grid of square tiles over your image, resizes each to the model's 768px processing resolution, pushes tiles through the VAE → transformer → VAE pipeline in small batches, then stitches the results back together with triangular-window blending so the seams don't show. Feed it a 4K window shot and it just works without needing a 48GB card.
The inputs that matter
Two are required: model (from the loader) and image (from a Load Image node). Of the six optional ones, a beginner actually touches maybe three:
tile_batch_size(default 2, range 1–4) - the VRAM dial. If you're OOMing, drop this to 1 first.tiling_size(default 768, 512–1536) - base tile size. Only used whenuse_short_edge_tileis off; smaller = less VRAM per tile.use_short_edge_tile(default on) - sizes tiles off your image's short edge instead oftiling_size. Leave it on; the author's default is a good default.
The rest - max_tiles_w, max_tiles_h (both 1–8), and min_overlap (16–256) - govern how the grid is laid out. More tiles means smoother stitching on huge images but more passes.
Where people get burned
- Out of memory: the README says 24GB VRAM, and it means it. Fixes, in order:
tile_batch_sizeto 1, smallertiling_size, then smaller input images. - "Infeasible tile constraints" error: this one comes straight from the code, and it's self-explanatory once you see it - raise
max_tiles_w/max_tiles_hor lowermin_overlap. It happens when your tile settings can't mathematically cover the image. - Model not loading: that's the loader's problem, not this node's - check HF access and
huggingface-cli login. - Slow first run: that's the 41GB model download, not the removal. Once the model is cached, this node is the fast half.
Wiring it up
Install the pack via ComfyUI Manager (search "WindowSeat") or git clone + pip install -r requirements.txt into custom_nodes, and note the Manager security-policy workaround in the README. Then: Load Image → WindowSeat Reflection Removal → Preview Image or Save Image, with the loader feeding the model input. The output is a plain IMAGE, so everything downstream - save, upscale, compare - works as normal. If the result is close but soft, running it through an upscaler afterward is a perfectly normal ComfyUI move.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| model | WINDOWSEAT_MODEL | — | |
| image | IMAGE | — | |
| use_short_edge_tileopt | BOOLEAN | true | — |
| tiling_sizeopt | INT | 768512–1536 | — |
| max_tiles_wopt | INT | 41–8 | — |
| max_tiles_hopt | INT | 41–8 | — |
| min_overlapopt | INT | 6416–256 | — |
| tile_batch_sizeopt | INT | 21–4 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |