Detect Edges (Canny) Chunked
What the Chunked Version Fixes
- image
- image
Here's a way to lose an evening: you queue a video preprocessing graph and ComfyUI stops responding. No error, no OOM, no progress bar. The window just never updates again. If the node in flight was the stock Detect Edges (Canny) on a couple hundred frames, it didn't crash. It ate your VRAM, spilled into system RAM, and quietly fell off a cliff.
That's the entire reason this node exists. Detect Edges (Canny) Chunked does the same edge extraction, on the same operator, but chews the batch a few frames at a time so it never builds the giant transient that kills the stock version.
Why edge detection still matters
Canny is the boring, reliable half of a lot of workflows: thin outline maps for a ControlNet hint, or an edge pass over every frame of a clip. It also survived the union-model purge: canny is in the FLUX.2 Fun, Z-Image and Qwen-Image unions, while the exotic SD-era conditions were left behind. So people still run it, and people still run it on video, which is where the batch size explodes.
The mechanism, because it explains the freeze
The stock node hands the whole batch to kornia.filters.canny in one call. Two things inside that call are expensive in ways that don't show up until your batch is large:
- Hysteresis is a Python-level
whileloop that dilates strong edges one pixel per round. At 1024² that's roughly 70–190 rounds, and its stop condition (.abs().any()) forces a GPU→CPU sync every round. - Each round's convolution output is 8 channels wide, so it's 8× the input volume, twice over. And the loop condition is batch-wide - if one frame out of 200 hasn't converged, all 200 pay another round.
The author measured it on a 4090: 200 frames at 1024² peaks near 25 GB. On a 24 GB card that doesn't OOM - it spills to system memory under Windows WDDM, and the node is uninterruptible while it runs. Hence the freeze.
This node calls the exact same expression - same dtype, same layout, same function - but on a slice. Peak memory drops to whatever the slice costs (8 frames ≈ 1 GB), and the result is reported as pixel-identical: bit-exact at matching batch size, zero pixel difference at chunk sizes 1/4/8/100 on real images at 256² through 1024². The one honest exception is pure uniform noise, where CUDA's algorithm choice can flip ~0.003% of threshold-boundary pixels - the stock node shows the same deviation if you feed it 8 frames at a time.
What you actually set
The required inputs mirror the stock node, so nothing to relearn, plus two optional ones:
image- your batch. Any frame count; long sequences are the point.low_threshold(0.4) andhigh_threshold(0.8) - same meaning as stock, 0–1. Raisinglow_thresholdis also your speed lever, since shorter weak-edge chains mean fewer hysteresis rounds.chunk_size(8) - frames per chunk. Smaller = lower peak, more calls. Leave it at 8 unless you're on a small card.auto_chunk(on) - reads free VRAM and shrinks the chunk below your request if needed. It only ever shrinks, never grows.use_gpu(on) - turn it off and everything runs on CPU.
One output, image: the usual 3-channel edge map, so it wires anywhere the stock node did - a ControlNet apply node, ImageToMask, a save node.
Install
ComfyUI Manager, search the pack title, or:
cd ComfyUI/custom_nodes
git clone https://github.com/user2318/ComfyUI-CustomNodeKit
Restart. kornia is the only thing this node needs, and ComfyUI core already installs it for its own Canny node. Note the pack's install.py runs on import, so the first startup after cloning may pip-install groundingdino-py and transformers for the pack's pose nodes - nothing to do with canny. If it fails it prints the exact pip install line and keeps going.
Where it still bites
low_thresholdabovehigh_thresholdraises before doing any work, with a clear message. The thresholds also have to be strictly inside (0, 1) - kornia's own check, just surfaced earlier.use_gpu = falseis not a memory holiday. It's a VRAM holiday. The author notes 200 frames at 1024² still wants around 27 GB of system RAM on CPU, which is whyauto_chunkstays active on that path too.- Watch the console line. It logs frames, resolution, device and both the requested and actual chunk with free memory. If you asked for 8 and got 2,
auto_chunkdid it. - It can be cancelled. Progress bar, plus an interrupt check at the top of each chunk - the difference between "wait it out" and "kill the process".
No models, no config. It's a drop-in replacement for one node that handles large batches badly - and if your video graph has ever hung on the edge pass, that's the node.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | 图像批次 (B, H, W, C)。支持任意帧数(长视频序列可放心直连)。 | |
| low_threshold | FLOAT | 0.400.01–0.99 | 低阈值(与官方 Canny 同名同义,0-1)。调高可显著减少迟滞循环轮数。 |
| high_threshold | FLOAT | 0.800.01–0.99 | 高阈值(与官方 Canny 同名同义,0-1)。必须 >= low_threshold。 |
| chunk_size | INT | 81–4096 | 每块帧数(上限)。越小峰值显存越低但调用次数越多;8 帧约 1GB 峰值。 |
| auto_chunkopt | BOOLEAN | true | 按当前空闲显存自动收缩块大小(只减不增),避免溢出到系统内存导致卡死。 |
| use_gpuopt | BOOLEAN | true | 关闭则全程在 CPU 上算(慢但完全不占显存),用于显存被大模型占满时。 |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |