Nodes/ComfyUI-CustomNodePacks/Vector Roto — Bezier (MEC)
ComfyUI Node

Vector Roto — Bezier (MEC)

Vector rotoscoping with Catmull-Rom tweening, no model needed

By Code2Collapse·Created 6 months ago·Updated a day ago· 52
Vector Roto — Bezier (MEC)
    • mask
    • info
    roto_json{"canvas": {"w": 1024, "h": 1024}, "frames": [{"frame": 0, "splines": []}]}
    frame_count1
    width1024
    height1024
    samples_per_seg24
    feather_px0.0

    Roto is the old-school way to get a mask: you draw a spline around the thing, keyframe it as the thing moves, and let the software tween between keyframes. It's deterministic, it's controllable, and it works when no segmentation model can figure out what you're pointing at. VectorRotoMEC is the MEC pack's take on that - a cubic-Bezier vector roto engine with Catmull-Rom keyframe tweening that hands evaluated polylines to a scanline rasterizer. No models, no downloads, just math.

    This is the node for the fiddly cases: a shadow, a glint, a prop that changes shape - anything where SAM's "3 candidate masks, pick one" flow is the wrong tool and you'd rather draw the shape yourself. It pairs naturally with the pack's SplineMaskMEC, but the division of labor matters: SplineMaskMEC is the interactive canvas node; VectorRotoMEC is the headless rasterizer that takes spline data as JSON and turns it into frames.

    How it works

    You feed it roto_json - a JSON structure describing a canvas size and a list of keyframe frames, each carrying its own splines. It then evaluates each spline as a cubic Bezier, tweens between your keyframes using Catmull-Rom interpolation, samples each curve at samples_per_seg points, and scanline-rasterizes the resulting polygons into a mask batch. Everything is resolution-independent until the final rasterize, and the canvas coordinates are scaled to your requested width/height.

    The default input is a skeleton that shows the expected shape: {"canvas": {"w": 1024, "h": 1024}, "frames": [{"frame": 0, "splines": []}]}. You replace splines with your control points; the interactive Spline Mask canvas elsewhere in the pack is the convenient way to author that JSON.

    Inputs and outputs that matter

    • roto_json (STRING, multiline) - the keyframed spline data. This is the input you'll actually edit.
    • frame_count (INT, default 1, up to 4096) - how many frames of mask to produce. Set this to your clip length.
    • width / height (INT, defaults 1024) - output resolution; the canvas JSON is scaled to fit.
    • samples_per_seg (INT, default 24) - curve resolution. Higher is smoother and slower; 24 is usually plenty.
    • feather_px (FLOAT, default 0) - edge softness in pixels, for a softer mask edge.

    Outputs:

    • mask (MASK) - the rasterized result, (frame_count, H, W).
    • info (STRING) - diagnostics (frame count, spline count, timing).

    Where it fits

    Classic use: roto a static-ish element across a short clip - draw it on keyframes, let the Catmull-Rom tween fill the gaps, rasterize to a mask batch, and feed that into whatever consumes masks (inpainting, compositing, the pack's mask propagate nodes). It's CPU-only, so it runs anywhere.

    Installing it

    Standard pack install:

    cd ComfyUI/custom_nodes
    git clone https://github.com/Code2Collapse/ComfyUI-CustomNodePacks.git
    

    restart ComfyUI or use Manager → "CustomNodePacks", and look for [MEC] Loading MaskEditControl node pack … in the console. No extra dependencies for this node.

    The honest caveat

    Roto is tedious no matter what, and this node doesn't do motion tracking - the tweening is geometric, not driven by the footage. For footage where the subject actually moves, pair it with a tracker or the pack's spline track mode (Lucas-Kanade) instead of hand-keyframing every move. For mostly-static shots, this is a clean, model-free way to get a precise mask out of a JSON description.

    CategoryMaskEditControl/Roto

    Inputs (6)

    NameTypeDefaultDescription
    roto_jsonSTRING{"canvas": {"w": 1024, "h": 1024}, "frames": [{"frame": 0, "splines": []}]}
    frame_countINT11–4096
    widthINT102416–8192
    heightINT102416–8192
    samples_per_segINT242–256
    feather_pxFLOAT0.00–64

    Outputs (2)

    NameTypeDescription
    maskMASK
    infoSTRING