Phase Stretch Transform
The edge detector that thinks your image is a light wave
- image
- edge_image
- phase_image
The name makes it sound like a physics lab gone rogue, which is honestly closer to the truth than most node names. Phase Stretch Transform (PST) is a real edge-detection algorithm from UCLA's Jalali lab that treats your image like a light wave passing through a dispersive medium, and this pack drops it into ComfyUI as one clean node under image → processing. There's essentially zero community buzz around it - we checked the usual forums and found nothing - so you're early. Whether that's exciting or a warning sign depends on how much you like fiddling with frequency-domain math. If you just want edges, Canny exists. PST is for when you want different edges.
Why you'd reach for it
When a ControlNet needs structural conditioning, most people grab Canny: thin, hard, reliable, boring. PST is the interesting alternative in the preprocessor pile. Instead of gradients and hysteresis, it works in the frequency domain and pulls edges out of the phase of a warped wave. That gives it a different failure-and-success profile - it's less twitchy about photographic noise, and it tends to catch edges Canny smooths away, especially in textured or low-contrast areas. For architecture or mechanical subjects you'll probably stay on Canny. For noisy photos, organic subjects, or just variety in a line-art or sketch workflow, PST is worth a look.
How it works
The node grayscales your image, FFTs it, applies a Gaussian low-pass in the frequency domain, then multiplies by a "warped phase ramp" kernel - a pure-phase filter that warps the spectrum according to the warp_strength parameter. Inverse FFT, take the phase angle, threshold it, and you've got edges. Toggle morph_flag and it runs a binary closing/opening pass to tidy the map into clean connected lines.
One honest gotcha: this runs on CPU via numpy/scipy, looping frame-by-frame in Python. Great for a single still; don't feed it a video batch and expect speed.
The knobs that matter
You'll set three of them 90% of the time:
lpf_sigma- Gaussian low-pass FWHM (yes, FWHM, not sigma - the code follows the reference implementation). Higher = more smoothing up front, fewer false edges from noise.phase_strength(S) - how hard phase variations get amplified. More edges, including weak ones.warp_strength(W) - how aggressively the frequency spectrum is warped. Higher values emphasize finer, sharper edges.
Then min_thresh/max_thresh slice the phase into the binary map (only used when morph_flag is on), morph_flag switches between binary-with-cleanup and a soft continuous map, and output_mode picks white-edges-on-black versus red-overlay-on-original.
Outputs and where they go
You get two IMAGE outputs. edge_image is the white-on-black map - hand that straight into a ControlNet Apply node, since canny-style ControlNet models are trained on exactly that format, or use it for line-art styling. phase_image is the raw normalized phase, a soft gradient map that's handy for debugging or as a gentler conditioning input. Both are three-channel, so nothing in the wiring fights you.
Install
ComfyUI Manager → search "PhaseStretchTransform", or the manual way:
cd ComfyUI/custom_nodes
git clone https://github.com/bemoregt/ComfyUI_PhaseStretchTransform
cd ComfyUI_PhaseStretchTransform
pip install numpy scipy
Restart ComfyUI. No model files to download, MIT licensed, done.
Troubleshooting
ImportError: scipy- ComfyUI ships numpy but scipy isn't guaranteed.pip install scipyand restart.- The README is out of date. It lists
min_thresh -1.0,max_thresh 0.003,morph_flag True; the shipped code defaults to-0.35,0.35,False. Trust the node's own defaults - the README numbers produce a noticeably thicker, noisier map. - Edges look messy - raise
lpf_sigmaand tighten the thresholds toward zero rather than cranking the strength knobs.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| lpf_sigma | FLOAT | 0.210.01–1 | Gaussian LPF FWHM (same unit as reference code). Higher = more smoothing before PST. |
| phase_strength | FLOAT | 0.480–10 | PST phase strength (S). Higher = stronger edge response. |
| warp_strength | FLOAT | 12.10–100 | PST warp strength (W). Controls frequency warping. |
| min_thresh | FLOAT | -0.350-3.15–0 | Lower phase threshold for binary edge map (morph_flag=True only) |
| max_thresh | FLOAT | 0.3500–3.15 | Upper phase threshold for binary edge map (morph_flag=True only) |
| morph_flag | BOOLEAN | false | True = binary edges with morphological clean-up; False = soft continuous edge map (|phase| normalised) |
| output_mode | COMBO | grayscale | grayscale = white edges on black; rgb_overlay = edges overlaid in red on original |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| edge_image | IMAGE | — |
| phase_image | IMAGE | — |