Zura · Prepare Background Text Cleanup
Deleting on-screen text without touching your actor
- combined_mask
- performer_mask
- clip_details
- performer_guard
- crop_text_mask
- generation_mask
- crop_width
- crop_height
- crop_x
- crop_y
- has_background_text
Source footage has captions, watermarks, channel bugs and shop signage baked into it. You want them gone before you regenerate the scene, and - critically - you do not want the model improvising over your actor's face in the process. This node works out which text is safely away from the performer, crops to it, and hands you a mask pair that separates "clean this up with an inpainter" from "this is what the video model generates."
The pack's README describes the outcome for H3's Remove on-screen text option: it "repairs eligible background text before sampling… and skips a detected text panel that touches or comes close to the performer." This is the node that decides eligible.
Inputs
Three required:
combined_mask- the artist-approved mask for the whole edit. This is the union of everything you've said the pipeline may change.performer_mask- the performer, as approved. This one defines what must be protected.clip_details- the reviewed-clip descriptor, the sameGENJ_CLIPpayload other studio nodes take. It carries the reviewed text regions and their source resolution.
Two optional:
context(default 32, range 0–128) - pixels of crop padding around the eligible text region. The crop is snapped to 8-pixel alignment afterwards, which matters for inpainting models that dislike odd offsets.performer_guard- a single mask of the genuine performer union for the whole continuous shot. The tooltip is worth quoting because it names the exact bug it prevents: "Keeps cleanup eligibility and crop bounds stable across windows." Without it, each window's crop is computed from that window only, and a person walking past a sign in window three shifts the crop mid-shot.
Outputs
Seven, and the split between the first two is the whole point:
crop_text_mask- the text-only mask, cropped. This is what the inpainter sees.generation_mask- yourcombined_maskminus the eligible background text. Hand this to the video model so it doesn't try to generate through the text area at the same time as the inpainter is repairing it. Two nodes fighting over the same pixels is the classic source of ghosting.crop_width,crop_height,crop_x,crop_y- the crop rectangle, so you can slice the frames and stitch the repaired patch back.has_background_text- a boolean gate. Wire it into a switch so the whole cleanup branch is skipped when there's nothing to remove. (When it's false you get a harmless 8×8 zero mask rather than an error.)
How the eligibility test works
For each reviewed text region, the node scales the box to the decoded raster, then measures distance from every performer pixel to that rectangle - using a spatial union across all frames, not per-frame. If anything is within 32 pixels, the region is rejected and stays on the normal generation path. That's a deliberately conservative rule: a caption that sits near the actor's chin doesn't get removed, because "close" is exactly where an inpainter will happily eat a jawline.
Eligible regions are then intersected with approved pixels from combined_mask only. The node can never invent editable pixels - if you didn't approve the area for editing, it isn't in the crop mask, full stop. All the eligible rectangles are unioned into one crop, which is why performer_guard exists: a shot-level rectangle set gives the inpainter identical context across repeated frames instead of a crop that jitters window to window.
Install
Ships with the pack:
cd ComfyUI/custom_nodes && git clone https://github.com/ZURAVFX/ComfyUI_zura_nodes
pip install -r ComfyUI_zura_nodes/requirements.txt
The inpainting half is not in this pack. The README names ComfyUI-ArtVenture (which may download big-lama.pt, around 196 MB, on first use) and ComfyUI-KJNodes as the pair it uses for the H3 text-repair branch. Install both through Manager. If you're rolling this by hand on a different inpaint path, crop_text_mask is an ordinary MASK and goes into any LaMa-style or diffusion inpainter.
Common issues
"Reviewed text regions need their source width and height." clip_details came from somewhere that doesn't carry the reviewed text region metadata - a hand-built dict, or a clip that never went through the text detection stage. This node needs the reviewed record, not just a frame count.
"The shot performer guard must include every genuine performer pixel in this window." Your performer_guard is too tight. It has to be a union over the whole shot, so it's a superset of every window's performer. The check exists because a guard that clips the actor would silently make the actor eligible for inpaint.
Nothing gets removed, has_background_text is false. Two usual causes: the text panel is within 32 px of the performer (working as designed), or the text area isn't inside combined_mask - the node will not repair pixels you haven't approved for editing.
A visible rectangle in the repaired output. The crop is snapped to 8 px and padded by context; if the inpaint model's output is a different exposure or sharpness than the surroundings, it shows. Raise context toward 48–64, and check that you're compositing the repaired crop back through the mask rather than pasting the whole rectangle.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| combined_mask | MASK | — | |
| performer_mask | MASK | — | |
| clip_details | GENJ_CLIP | — | |
| contextopt | INT | 320–128 | — |
| performer_guardopt | MASK | Optional genuine performer union for the complete continuous shot. Keeps cleanup eligibility and crop bounds stable across windows. |
Outputs (7)
| Name | Type | Description |
|---|---|---|
| crop_text_mask | MASK | — |
| generation_mask | MASK | — |
| crop_width | INT | — |
| crop_height | INT | — |
| crop_x | INT | — |
| crop_y | INT | — |
| has_background_text | BOOLEAN | — |