CV Vector Displacement Map
Warping with a direction, not just an amount
- map_x
- map_y
- vector_field
- dx
- dy
- map_x
- map_y
The pack has two displacement nodes and the difference is one word: vector. DisplacementMap drives both axes from the same grayscale value - you can push pixels further or less far, but always along the same diagonal-and-slope direction, because one number can't say "left" separately from "up". CV Vector Displacement Map adds a per-pixel (dx, dy) field, so every pixel can move in its own direction by its own amount.
That's the difference between a bump and a swirl.
How it works
The node works on the coordinate maps rather than the image. You feed in map_x and map_y - the source-coordinate maps you'd hand to cv2_remap - and it adds the displacement into them:
map_x' = map_x + strength_x * dx
map_y' = map_y + strength_y * dy
Those two outputs then go to cv2_remap (with WARP_RELATIVE_MAP set - see CV Warp Flags). Because you're editing coordinates rather than pixels, these nodes compose: chain an identity map through a wave map, a vector map, a pinch map, and you get all three distortions in one remap, in one resampling. That's a much better deal than warping an image four times and blurring it a little each pass.
The chain always starts from CV Identity Map (absolute coordinates) or CV Relative Identity Map (offsets from zero, which is what WARP_RELATIVE_MAP wants).
Inputs and outputs
map_x / map_y are required, same shape, and are the incoming chain. strength_x / strength_y (default 10 each) are plain multipliers in pixels on the displacement - the knobs you'll actually turn, and they let one displacement source drive different magnitudes on each axis.
Then the displacement source itself, optional and with a precedence rule:
- vector_field - a multi-channel image.
HxWx2is a direct vector field;HxWx3/HxWx4is a normal map, using R asdxand G asdy;HxWapplies the same value to both axes. uint8 inputs are mapped to [-1, 1], so mid-gray means "don't move" - that's what makes a tangent-space normal map work directly. float inputs are used as raw pixel offsets, no mapping. - dx / dy - single-channel
HxWarrays, and they overridevector_fieldwhen connected. This is where optical flow goes if you've computed it: flow's X and Y components are exactly this.
Outputs are map_x and map_y, ready to continue the chain or feed the remap.
That uint8-vs-float distinction is the one detail that decides whether your warp works, so it's worth saying twice. A normal map from any standard source is uint8 with 128 as neutral, and this node handles that. A float displacement field of your own construction is treated as literal pixels of movement. The two are not interchangeable, and a float field containing 0-255 values will send your image roughly 200 pixels sideways.
Where it earns its place
Flow-driven warping. Feed optical flow in and the current frame gets warped toward the next one - frame interpolation's dumb cousin (the pack's CV Flow Interpolate does the real job with a proper blend).
Normal-map-driven relighting. A tangent-space normal map as a displacement field gives the "emboss on a curved surface" look, and separate per-axis strengths let you exaggerate the X shading on its own.
Depth-driven parallax. A depth map whose x-gradient drives dx gives the classic dolly effect on a still - the illusion that the camera moved sideways. Same family as depth-estimation.md: one photo in, depth out, depth used to fake motion that was never filmed.
Installing it
Manager → ComfyUI CV, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
Restart. Python ≥ 3.12, a recent ComfyUI on the V3 node API, opencv-contrib-python-headless~=5.0.0.93. No models. GPL-3.0, forked from opencv-comfyui, and the author's standing caution applies: this is LLM-assisted code, several nodes in this pack are genuine algorithm implementations rather than thin cv2 wrappers, and this is one of the remap-family composition nodes you should sanity-check once with a known input before you build a pipeline on it.
Where people get burned
Remapping without WARP_RELATIVE_MAP. Absolute coordinate maps and relative offsets look the same in a graph and behave completely differently. If your output is a smear, uniform shift, or a solid block of border colour, that flag is the first suspect - that's why CV Warp Flags exists as its own node.
Shape mismatches. map_x and map_y must share a shape, and in practice also share the shape of the image you eventually remap. A displacement field sampled at a different resolution than the map chain gives you a mess that's hard to read as an error.
Confusing strength with scale. strength_x = 0 disables X regardless of what the field says, which is a perfectly good way to isolate one axis while debugging.
Four remaps in a row. Each one resamples. Compose the maps (they're just arrays) and remap once - that's the whole design intent of this node family, and it's the difference between a clean warp and mush.
Inputs (7)
| Name | Type | Default | Description |
|---|---|---|---|
| map_x | NPARRAY | Incoming source-x coordinate map. Start the chain with 'CV Identity Map' or 'CV Relative Identity Map'. | |
| map_y | NPARRAY | Incoming source-y coordinate map (same shape as map_x). | |
| strength_x | FLOAT | 10.0-10000–10000 | Multiplier for the X displacement in pixels. |
| strength_y | FLOAT | 10.0-10000–10000 | Multiplier for the Y displacement in pixels. |
| vector_fieldopt | NPARRAY,IMAGE,MASK | Multi-channel displacement source: uses ch0 as dx, ch1 as dy. HxWx2 (direct vector field), HxWx3/4 (normal map: R->dx, G->dy), or HxW (same for both axes). uint8 mapped to [-1,1]; float used as-is. 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. | |
| dxopt | NPARRAY | Per-pixel X displacement (overrides vector_field when connected). Single-channel HxW. | |
| dyopt | NPARRAY | Per-pixel Y displacement (overrides vector_field when connected). Single-channel HxW. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| map_x | NPARRAY | Displaced source-x coordinate map. |
| map_y | NPARRAY | Displaced source-y coordinate map. |