CV Polynomial Radial Map
Barrel, pincushion and lens warp you can compose
- map_x
- map_y
- coeffs
- map_x
- map_y
A different species of node from the rest of the pack's one-shot operations. This is a map generator: it produces coordinate maps, and nothing is resampled until you feed the map to cv2.remap.
What it's for
Lens distortion, mostly. Real lenses bend straight lines; a wide phone lens barrels outward, a long lens pinches in. Correcting that - or faking it as a look - needs a radial distortion model, and this node gives you an arbitrary one: the distance r of each pixel from the centre is scaled by a polynomial you supply.
That last part is the point. Rather than a fixed "k1, k2" correction, coeffs is an NPARRAY input: P(r) = c0 + c1·r + c2·r² + … + cn·rⁿ, and the sampling radius for each pixel is r' = r · P(r). One coefficient of [1.0] is the identity. The three-coefficient set [1+s, -2s, s] reproduces the familiar radial-extrapolate formula r' = r · (1 + s(1−r)²), where positive s magnifies the centre by pushing outer content outward and negative s pinches inward. Since coefficients are data and not a widget, the same array can feed several nodes, or come out of a fit, or be switched by a branch without rewiring.
The composition trick
The pack's remap nodes are designed to chain, and this is where they're genuinely nicer than stacking cv2.remap calls. Start with CV Identity Map, add this one, add CV Wave Map, add a homography - each node takes map_x/map_y in and gives them back, and the whole chain collapses into a single resampling pass at the end. One interpolation, one generation of resampling blur, no matter how many distortions you stacked. If you've ever watched something survive three separate warp nodes in other packs and come out mush, that's the benefit.
center_x and center_y are fractions of image width/height (0.5/0.5 default) rather than pixels, so the effect centre travels with the image if you change resolution.
Inputs and outputs
map_x and map_y come from the previous node in the chain - both must be the same shape, i.e. from the same chain. coeffs is your 1D polynomial array; CV Scalar is the easy way to type one, e.g. (1.3, -0.6, 0.3) for s = 0.3. Then center_x/center_y if the optical centre isn't the image centre.
It emits map_x and map_y, ready for another remap node or for the final cv2_remap call. There's no image output; the maps are lazy until you sample them.
Installing
ComfyUI Manager → search "ComfyUI CV", or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
# restart ComfyUI
Python ≥ 3.12, recent V3-API ComfyUI, and the pinned OpenCV build:
pip install "opencv-contrib-python-headless~=5.0.0.93"
Gotchas
Units are normalised, and that bites. The r in the polynomial is a normalised radius, not pixels, which is why [1.0] is the identity and why the same coefficients behave sensibly at any resolution. Coefficients tuned by eyeballing on one image will look wrong on a differently cropped one - they're a model of a lens, not a look.
Chaining requires both maps. A remap node missing one of its two inputs can't compose, so keep map_x and map_y travelling together through the chain. Splitting a chain, feeding one branch the wave map and the other the radial map, is how you end up with a shear that isn't in any of your coefficients.
The wheel must be contrib. All OpenCV distributions share one site-packages/cv2; installing opencv-python over the contrib build silently empties the contrib submodules and nodes disappear from the menu. The pack ships tools/repair_opencv_contrib.py, and --check will tell you if that's happened.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| map_x | NPARRAY | Incoming source-x coordinate map. Start the chain with 'CV Identity Map'; further remap nodes compose onto it. | |
| map_y | NPARRAY | Incoming source-y coordinate map (same shape as map_x - both come from the same chain). | |
| coeffs | NPARRAY | 1D polynomial coefficients [c0, c1, c2, ..., cn]. P(r) = c0 + c1*r + c2*r² + ... + cn*r^n, and r' = r * P(r). For the radial-extrapolate formula use [1+s, -2s, s] (s = strength). Create the array with 'CV Scalar', e.g. '(1.3, -0.6, 0.3)' for s=0.3. | |
| center_xopt | FLOAT | 0.500–1 | Center of the radial effect as a fraction of the image width. |
| center_yopt | FLOAT | 0.500–1 | Center of the radial effect as a fraction of the image height. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| map_x | NPARRAY | — |
| map_y | NPARRAY | — |