cv2.pencilSketch
Line-art and colour-pencil looks in one deterministic node
- src
- sketch
- color
Two outputs, no model, milliseconds of compute on the CPU, and the result is a grey pencil drawing of your photo plus a colour-pencil version of it. It's the non-photorealistic cv2.photo family - the same edge-preserving-filter machinery as stylization and detailEnhance, pointed at pencil instead.
Where this earns its place: whenever you want a sketch look without another round trip through a diffusion model. Turn a photo into a drawing for a composition test, make a line-art plate to hand to a ControlNet lineart preprocessor, or generate a "drawn by hand" variant that doesn't re-roll the face. The KB's post-processing doc says the quiet part out loud - these deterministic operations are $0 and instant, and "the failure mode this doc exists to prevent is doing color correction with img2img". Same logic applies here: if you want pencil texture, filter. Don't prompt for it and hope.
How it works
Under the hood it's a recursive edge-preserving smoothing pass. sigma_s sets the spatial scale - how far the filter reaches, so how much large-scale tone survives. sigma_r is the range threshold: colour differences below it get flattened, above it survive as edges. Then the smoothed image is turned into a pencil look by a multiplication/division pass whose strength is shade_factor.
That's why the node behaves the way it does, and the pack's tooltips say it better than a spec sheet would:
sigma_s- spatial scale in pixels, 0-200, "how far the filter reaches. Bigger = stronger flattening." Preset to OpenCV's 60.sigma_r- range/edge threshold, 0-1: "colour differences below it are smoothed away, above them are kept as edges. Bigger = fewer surviving edges." Preset to 0.07.shade_factor- "Darkness of the pencil shading (0-0.1): 0 = white paper, larger values ink in more tone." Preset to 0.02.
Those three presets are OpenCV's defaults, and they're a genuinely good starting point: you get a recognisable pencil sketch out of the box. The knob that changes the character is shade_factor - push it toward 0.05-0.08 for a heavy, inky drawing, keep it near 0.01 for a light one. sigma_r is the one to lower if you're losing structure you wanted, because it's the edge-survival threshold: too high and small features get smoothed into paper.
Inputs and outputs
One input that matters: src. It's a match-typed socket - link a ComfyUI IMAGE and both outputs come back as IMAGE, link an NPARRAY and it stays NPARRAY. No manual bridge-and-return conversion either way, which is why Load Image → cv2.pencilSketch → Save Image is a complete graph. A MASK link isn't accepted here: the socket is deliberately image-only, since a single-channel canvas is meaningless to a colour-pencil filter. The two outputs are sketch (the grey pencil drawing) and color (the colour-pencil version), and both echo the input's format.
That match-typing is the nicest thing about this node in a normal workflow: Load Image → cv2.pencilSketch → Save Image is the whole graph, and you never touch an array.
Two practical notes on the outputs. The grey sketch is a single-channel result deep down, and the pack's restore path replicates it to three channels when it comes back as an IMAGE, so it slots into image pipelines with no channel gymnastics. And sketch and color are separate pictures of the same frame rather than two flavours of the same array, which is why the socket type is per-output and not shared.
This node is also one of the few raw wrappers that handles batches properly: it's in the pack's per-frame-batch table (and in the image-only guard, since it needs three channels). Feed it a 40-frame IMAGE batch and you get a 40-frame pencil sketch back - genuinely useful for turning a clip into a pencil test. Linking a single-channel MASK batch skips the batch path for the same 3-channel reason, so don't try to pencil-sketch a mask.
Gotchas
It wants 8-bit 3-channel input. A ComfyUI IMAGE arrives as exactly that. A float NPARRAY in 0-1 range will not be interpreted as a picture - if you're splicing mattes or depth into this, cast and rescale to 0-255 uint8 first. CV Cast Array handles the dtype; cv2.normalize or convertScaleAbs handles the range.
It's not a style transfer. Two different photos of the same scene give two different sketches, but the style is fixed - there's no reference image input, and no amount of knob-twiddling will make it look like a specific artist's pencil. For that you want a model. For "make this read as a drawing, now, deterministically", this is the tool.
Installing
ComfyUI Manager → search ComfyUI CV → Install → restart. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Python ≥ 3.12 and a recent ComfyUI (the pack is built on the V3 node API), with that OpenCV version as the reference it's curated against. pencilSketch is core cv2 - available in every wheel, no models, no downloads. Unless you install a non-contrib opencv-python over a contrib one, which is the pack's one chronic environment failure and empties the contrib submodules; tools/repair_opencv_contrib.py --check diagnoses it.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| src | COMFY_MATCHTYPE_V3 | Input 8-bit 3-channel image. The image output(s) echo this input's format. Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size. | |
| sigma_sopt | FLOAT | 60.0000-1e+38–1e+38 | %Range between 0 to 200. Preset to the OpenCV default (60.0). |
| sigma_ropt | FLOAT | 0.0700-1e+38–1e+38 | %Range between 0 to 1. Preset to the OpenCV default (0.07). |
| shade_factoropt | FLOAT | 0.0200-1e+38–1e+38 | %Range between 0 to 0.1. Preset to the OpenCV default (0.02). |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| sketch | COMFY_MATCHTYPE_V3 | Echoes the 'src' input's format: an IMAGE link comes back as IMAGE, MASK as MASK, NPARRAY stays NPARRAY. |
| color | COMFY_MATCHTYPE_V3 | Echoes the 'src' input's format: an IMAGE link comes back as IMAGE, MASK as MASK, NPARRAY stays NPARRAY. |