ComfyUI Node

Centerline to SVG

The actual magic of line art → vector

By TJ16th·Created 10 months ago·Updated 10 months ago· 2
Centerline to SVG
  • line_mask
  • original_image
  • svg_string
  • centerline_image
  • statistics
algorithm
smoothing2.0
min_path_length10
simplify_tolerance1.0
bezier_smoothingtrue
dilate_before_thin0
color_info
preserve_colorstrue

This is the node the whole pack is named for, and the one you'll actually tell people about. Everything before it prepares a mask; everything after it edits text. Here is where a line-art image finally becomes an SVG.

What it actually does

Your line art is a raster image where each stroke is maybe 5–20 pixels thick. If you traced the outline of each stroke you'd get a blob, not a line - a mess for laser cutting or plotting. So this node first thins each stroke down to a one-pixel-wide spine (the centerline), then converts those spines into SVG path data.

Mechanically, from the source: it takes your line_mask and runs one of three extraction algorithms - skeleton (Zhang-Suen thinning via scikit-image's skeletonize), medial_axis (same idea but distance-weighted, with a filter that drops spurious noise branches), or ridge (distance transform + threshold, then thin). Then cv2.findContours walks each thinned spine into a point list, cv2.approxPolyDP applies Douglas-Peucker simplification at your simplify_tolerance, and optional bezier_smoothing rounds the result into smooth curves. Out comes a clean, editable SVG string.

Inputs that matter

  • line_mask - the MASK output from Line Region Detector (or Mask Line Cleanup). Garbage in, garbage out: a noisy mask makes noisy paths.
  • algorithm - ridge, skeleton, or medial_axis. Start with the default ridge; switch to medial_axis if you're getting a lot of spurious little branches, skeleton if you want the most literal trace.
  • simplify_tolerance - Douglas-Peucker tolerance, default 1.0. Raise it for fewer, simpler points; lower it toward 0 for fidelity at the cost of file size. This is the knob you'll fiddle with most.
  • min_path_length - drops paths shorter than this (default 10). Perfect for killing dust specks.
  • smoothing - path smoothing strength, 0–10. High values can round off corners you wanted.
  • dilate_before_thin - dilate the mask first to fuse close parallel lines into one. This is your fix for the classic "double line" problem where extraction picks up both edges of a thick stroke.

Optional: original_image plus color_info and preserve_colors - if you run color_clustering on the detector, you can carry detected colors through into the SVG's stroke attributes.

Outputs: svg_string (feed it to any other TJ_Vector SVG node), centerline_image (a raster preview of the thinned lines - wire this to a preview to see what you actually extracted), and statistics (JSON: path counts, lengths, that sort of thing).

How it fits

In a typical ComfyUI graph you've already got clean line art - ControlNet lineart preprocessors are a whole ecosystem for producing it (the lineart_anime / lineart_coarse family are the usual suspects). The problem was never getting lines, it was getting them out as vectors for a laser cutter, a vinyl plotter, or a real Illustrator file. That's the gap this pack fills, and this node is the center of it.

Install and troubleshooting

Same pack-wide story: ComfyUI Manager search TJ_ComfyUI_Lineart2Vector, or:

cd ComfyUI/custom_nodes
git clone https://github.com/TJ16th/TJ_ComfyUI_Lineart2Vector.git
# restart

Dependencies come from requirements.txt - the notable ones beyond stock ComfyUI are opencv-contrib-python and scikit-image (for the thinning), so if you cloned by hand and skipped pip, that's what will bite. Nothing to download model-wise.

Where people get burned: raising smoothing too far and wondering why corners melted; setting min_path_length too high and losing small facial features or thin accent lines; and feeding it a mask with doubled-up edges without using dilate_before_thin. If the paths look lumpy, simplify more; if they look like tangled spaghetti, the mask was the problem, not this node. When in doubt, run Mask Line Cleanup on the mask first - it exists for exactly this.

CategoryTJ_Vector

Inputs (10)

NameTypeDefaultDescription
line_maskMASK
algorithmCOMBO3 options: ridge, skeleton, medial_axis
smoothingFLOAT2.00–10
min_path_lengthINT101–1000
simplify_toleranceFLOAT1.00–10
bezier_smoothingBOOLEANtrue
dilate_before_thinINT00–10Dilate mask before thinning to merge close lines (0=disabled)
original_imageoptIMAGE
color_infooptSTRING
preserve_colorsoptBOOLEANtrue

Outputs (3)

NameTypeDescription
svg_stringSTRING
centerline_imageIMAGE
statisticsSTRING