GPU-accelerated sparse track renderer. Renders circles at a high reference resolution and downscales with bilinear interpolation so circle sizes match the CPU version. All work — rasterisation, compositing and resize — stays on GPU.
| 207 | |
| 208 | @comfy_node(name="LTXVDrawTracks", description="LTX Draw Sparse Tracks") |
| 209 | class LTXVDrawTracks(io.ComfyNode): |
| 210 | """GPU-accelerated sparse track renderer. |
| 211 | |
| 212 | Renders circles at a high reference resolution and downscales with |
| 213 | bilinear interpolation so circle sizes match the CPU version. |
| 214 | All work — rasterisation, compositing and resize — stays on GPU. |
| 215 | """ |
| 216 | |
| 217 | @classmethod |
| 218 | def define_schema(cls): |
| 219 | return io.Schema( |
| 220 | node_id="LTXVDrawTracks", |
| 221 | category="Lightricks/motion_tracking", |
| 222 | description=( |
| 223 | "GPU-accelerated sparse track renderer. Rasterises circles at " |
| 224 | "high resolution and downscales with bilinear interpolation." |
| 225 | ), |
| 226 | inputs=[ |
| 227 | io.String.Input( |
| 228 | "tracks", |
| 229 | multiline=True, |
| 230 | tooltip="JSON string of track coordinates (list of point lists with x/y keys).", |
| 231 | ), |
| 232 | io.Int.Input( |
| 233 | "width", |
| 234 | default=512, |
| 235 | min=8, |
| 236 | max=8192, |
| 237 | step=8, |
| 238 | tooltip="Output image width in pixels.", |
| 239 | ), |
| 240 | io.Int.Input( |
| 241 | "height", |
| 242 | default=512, |
| 243 | min=8, |
| 244 | max=8192, |
| 245 | step=8, |
| 246 | tooltip="Output image height in pixels.", |
| 247 | ), |
| 248 | ], |
| 249 | outputs=[ |
| 250 | io.Image.Output(), |
| 251 | ], |
| 252 | ) |
| 253 | |
| 254 | @classmethod |
| 255 | def execute(cls, tracks: str, width: int, height: int) -> io.NodeOutput: |
| 256 | device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
| 257 | parsed = _parse_tracks(tracks) |
| 258 | if not parsed: |
| 259 | blank = torch.zeros(1, height, width, 3, device=device, dtype=torch.half) |
| 260 | return io.NodeOutput(blank) |
| 261 | num_tracks = len(parsed) |
| 262 | num_frames = max(len(t) for t in parsed) |
| 263 | rw, rh, sx, sy = _render_resolution(width, height, _REF_SHORT_SIDE) |
| 264 | |
| 265 | point_xy = torch.zeros(num_tracks, num_frames, 2, device=device) |
| 266 | vis = torch.zeros(num_tracks, num_frames, dtype=torch.bool, device=device) |
nothing calls this directly
no outgoing calls
no test coverage detected