Downloads Β· 30 days
5
5% of all-time downloads
remyxai/stitch-flux-modular
stitch-flux-modular is a machine learning model from remyxai. Use it for the machine learning task on the model card, and read the license before you ship it in a product. It is set up for diffusers. The card lists the license as other.
π€ Hub: remyxai/stitch-flux-modular Β· π Paper: arXiv:2509.26644 Β· π» Reference: ExplainableML/Stitch (unlicensed β see the clean-room note) Β· π¦ Monorepo: flux-recipes
Downloads Β· 30 days
5
5% of all-time downloads
All-time downloads
97
Public
Repo size
751 KB
Likes
0
Public
Click a slice to open those files.
.png751 KB Β· 94%
From the Hugging Face model README
π€ Hub: remyxai/stitch-flux-modular Β· π Paper: arXiv:2509.26644 Β· π» Reference: ExplainableML/Stitch (unlicensed β see the clean-room note) Β· π¦ Monorepo: flux-recipes
Put this object at this location β no fine-tuning, no detector, no extra weights β as a
Modular Diffusers
custom block. Implements Stitch (arXiv:2509.26644): you give
a global prompt plus a few (bounding box, sub-prompt) regions, and each object is generated
confined to its box, cut out, composited onto the background, and blended. In the paper this
lifts FLUX's GenEval-Position score from ~0.22 to ~0.70 with zero training.
Off-the-shelf FLUX routinely fails simple spatial requests β ask for "a red cube to the left of a blue sphere" and the objects swap, merge, or drift. Stitch fixes exactly that, and it is hard placement, unlike our regional-prompting block, which does soft per-region prompt routing (a base prompt plus per-region prompts in one pass, no boxes and no compositing). Use Stitch when you know where things should go.

import torch
from diffusers import ModularPipeline
pipe = ModularPipeline.from_pretrained("remyxai/stitch-flux-modular", trust_remote_code=True)
pipe.load_components(dtype=torch.bfloat16)
pipe.to("cuda")
img = pipe(
prompt="a red cube to the left of a blue sphere, on a plain studio backdrop",
regions=[
{"box": [0.05, 0.3, 0.45, 0.75], "prompt": "a red cube"}, # left
{"box": [0.55, 0.3, 0.95, 0.75], "prompt": "a blue sphere"}, # right
],
height=1024, width=1024,
).images[0]
img.save("stitch.png")
box is normalized [x0, y0, x1, y1] in [0, 1], origin top-left, y down. Pass
regions=None (or []) and the block is a bit-exact no-op against stock
FluxPipeline β no processor is installed and nothing is mutated.
Three phases, all training-free on the frozen FLUX weights:
region_bind_steps = S, default 4). For the first S steps the transformer runs
once per region, plus once for the background prompt β K+1 passes per step, all started from
the same noise so they stay registered token-for-token. In each object pass an additive
joint-attention mask confines generation to the box: inside-box image tokens can't attend
outside the box, outside-box tokens can't attend to the sub-prompt, and the sub-prompt can't
attend outside the box. The mask is added to the attention logits before softmax; Q/K/V
projections are untouched. The background pass runs unmasked.cutout_head, default block 14 / head 20 for FLUX.1-dev), averages
it over the non-pad text tokens, sorts descending, and keeps tokens until their cumulative mass
reaches cutout_eta = 0.65 β then 2-D max-pools that token mask with cutout_kernel = 5 to close
holes. Each object's foreground latent tokens are written into the background latent at the box.Two implementation notes that matter on FLUX specifically: the joint attention matrix is
(n_txt + n_img)-square with text first, so the box mask is built on the image block only and
every boxβtoken index is offset by n_txt; and packed FLUX latents are (B, seq, 64), so
box mapping, foreground selection and compositing all index tokens on dim 1.
The attention processors are swapped for the duration of the call and restored in finally;
with regions empty none are installed at all.
Compute: Phase A costs K+1 transformer passes per step for S steps (K = number of regions). At K β€ 3 this is comfortable on a single A100; the passes are independent, so a VRAM-tight setup can batch them instead of running them sequentially. A 2-region 50-step run is β 10 steps Γ 3 passes + 40 steps Γ 1 pass β 1.4Γ a stock 50-step generation.
| arg | default | meaning |
|---|---|---|
prompt | β | global prompt for the whole scene (required) |
regions | None | list of {"box": [x0,y0,x1,y1], "prompt": str} (normalized); None/[] β stock FLUX |
background_prompt | "" | background pass's prompt; "" β derived from prompt |
region_bind_steps | 4 | S: Region-Binding steps β fewer than the paper's 10; the refine de-boxifies |
num_inference_steps | 50 | T: total steps |
cutout_eta | 0.65 | cumulative attention mass kept by the Cutout threshold |
cutout_kernel | 5 | ΞΊ: 2-D max-pool kernel that solidifies the foreground mask |
cutout_head | (14, 20) | (block, head) the Cutout reads β paper-reported for FLUX.1-dev; re-pick via head_probe.ipynb if your build's indexing differs |
guidance_scale | 3.5 | FLUX guidance |
height / width | 1024 | canvas size (multiple of 16) |
Tuning (defaults tuned on FLUX): region_bind_steps=4 + cutout_eta=0.65 were chosen because the
paper's 10 / 0.95 pasted the whole box-fill and produced rectangular slabs on FLUX. Sweet spot is
2β3 objects (4+ may show minor per-region duplication). If objects come out boxy or duplicated,
lower cutout_eta or region_bind_steps further (fewer confined steps let the refine de-boxify); if
an object drifts out of its box, raise region_bind_steps. cutout_head is the one genuinely
per-build knob β head_probe.ipynb sweeps cutout_head/region_bind_steps/cutout_eta and scores
each so you can re-pick if your diffusers build indexes heads differently than the paper's 14/20.
Fallback: if the Cutout is unstable on your build, note that Region Binding alone already gives
strong position accuracy (the paper's ablation reports ~81% of the full method's spatial benefit) at
the cost of blend quality. Set cutout_eta=1.0 to keep the full-mass prefix and you get
approximately that behaviour with the composite still in place.
diffusers (main / β₯ 0.40), transformers, accelerate, sentencepiece, protobuf. No extra
weights, no detector, no LLM β prompt β box decomposition is out of scope by design: the caller
supplies regions, like a layout-to-image API.
Stitch by Jessica Bader, Mateusz Pach, Maria A. Bravo, Serge Belongie and Zeynep Akata. Clean-room implementation: the reference repo ExplainableML/Stitch carries no license, so none of its code was read or copied β this block was written from the paper's description alone (the same discipline as our panorama and regional-prompting ports; the method itself is not copyrightable).
Authored with AI assistance (Claude) and validated by the Remyx AI team; method credit to the Stitch authors. Uses FLUX.1-dev under its non-commercial license β this derivative inherits that restriction.
@misc{bader2025stitch,
title={Stitch: Training-Free Position Control in Multimodal Diffusion Transformers},
author={Bader, Jessica and Pach, Mateusz and Bravo, Maria A. and Belongie, Serge and Akata, Zeynep},
year={2025}, eprint={2509.26644}, archivePrefix={arXiv}, primaryClass={cs.CV},
url={https://arxiv.org/abs/2509.26644}
}