AP Apply RAFT Optical Flow (Masked)
Warp only the part that moves — masked optical flow without smearing your background
- images
- mask
- flow_data
- output_images
- warped_images
- warped_mask
- valid_mask
The plain APApplyRAFTOpticalFlow warps your whole frame. That's fine when the whole scene moves - a pan, a dolly, a subject that fills the frame. But most temporal work is the opposite: you've got a moving subject on a static background, and warping everything smears the background and drags ghosting across regions that should be untouched. This node is the fix. Give it a mask, and only the masked region gets warped; everything else stays put.
It's the node the author clearly intends as the workhorse. The README's recommended "professional anti-blur compositing" pipeline routes this node's warped_mask output into APFlowComposite.effect_mask, not the unmasked variant's. If you're building a temporal-consistency or video-inpaint workflow and you're using the plain apply node, you're probably making life harder than you need to.
The mechanism
It's the same grid-sample warp as the unmasked node, plus mask math. Your mask is optionally inverted (invert_mask), feathered (mask_feather, an average-pool blur of 2*feather+1 pixels), and scaled by mask_strength before it's used as the blend alpha. The image is warped fully, the mask is warped too, and then output = warped × (warped_mask × original_mask) + original × the inverse. So inside the mask you get the flow-moved pixels; outside, you get the untouched original.
That's why there are four outputs instead of two. warped_images is the raw warped frame (everything moved), output_images is the masked composite (only your region moved), and warped_mask is the mask after it's been pushed through the flow - which is genuinely useful because it tells you where the subject ended up, not where it started. valid_mask is the usual warp-validity map for the frame edges.
The inputs that matter
Everything from the unmasked node carries over (flow_direction, batch_mode, strength, invert_flow, flow_skip/frames_skip, the optional current_frame_index). The new ones, for a beginner:
mask- the region to warp. From a SAM segmentation, a detected subject, a hand-drawn region. The README's recommended settings arestrength0.6–1.0,mask_feather3–8,mask_strength0.8–1.0.mask_feather- softens the mask edge so you don't get a hard seam between warped and unwarped pixels. Start at 3–8; if the mask edge is visible, raise it.mask_strength- scales the mask's influence. Under 1.0 lets a bit of the original bleed through even inside the mask, which helps hide warp errors.invert_mask- swap which side warps. Occasionally you have a background mask and want the inverse; this saves you anInvertMasknode.
The wiring pattern that actually works
The README's recommended anti-ghosting setup, roughly:
- Compute flow from neighboring frames with
APGetRAFTOpticalFlow. APFlowOcclusionMaskfrom the sameflow_datato find covered/revealed regions.- This node to warp image + mask.
APFlowComposite- feedwarped_maskinto itseffect_mask, setalpha_mode = flow_confidence_x_mask, enableuse_difference_gatewith a lowdifference_threshold(0.005–0.02).
That last part is the difference between a clean result and a smeary one: the difference gate blocks the composite from touching pixels where the warped frame barely differs from the original - i.e. background that shouldn't have changed - so changes stay focused on actually-warped regions.
Installing it
Standard pack install: ComfyUI Manager (search "AP_OpticalFlow"), or clone it manually:
cd ComfyUI/custom_nodes
git clone https://github.com/adampolczynski/ComfyUI_AP_OpticalFlow
python -m pip install -r custom_nodes/ComfyUI_AP_OpticalFlow/requirements.txt
Restart ComfyUI. Dependency is just torchvision>=0.15; RAFT weights auto-download on first flow compute.
When to reach for the masked variant
Video inpainting is the canonical case: you're masking the moving parts and repainting them per-frame, and you want the warp (and its valid mask) to stay inside that moving region so the inpaint doesn't fight the static background. Same logic applies to face-consistent video and anything where a subject moves against a fixed scene. If your region is the whole frame, save the extra wires and use the unmasked node.
Inputs (15)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| mask | MASK | — | |
| flow_data | AP_FLOW | — | |
| flow_direction | COMBO | ab | 2 options: ab, ba |
| batch_mode | COMBO | auto | 3 options: auto, by_index, repeat_image |
| flow_skip | INT | 00–2147483647 | — |
| frames_skip | INT | 00–2147483647 | — |
| strength | FLOAT | 1.00-4–4 | — |
| invert_flow | BOOLEAN | false | — |
| invert_mask | BOOLEAN | false | — |
| mask_strength | FLOAT | 1.000–2 | — |
| mask_feather | INT | 00–64 | — |
| interpolation | COMBO | bilinear | 3 options: bilinear, nearest, bicubic |
| padding_mode | COMBO | border | 3 options: border, zeros, reflection |
| current_frame_indexopt | INT | 00–2147483647 | — |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| output_images | IMAGE | — |
| warped_images | IMAGE | — |
| warped_mask | MASK | — |
| valid_mask | MASK | — |