CV Permute Axes
HWC to CHW without writing numpy in your head
- nparray
- nparray
The most boring node in this pack and one of the most used, in the way that adapters always are. CV Permute Axes reorders an ndarray's axes - it's numpy.moveaxis as a node. You need it the moment two libraries disagree about where the channel dimension goes.
OpenCV and ComfyUI images are (H, W, C). Most deep-learning runtimes want (C, H, W), and a DNN blob wants (1, C, H, W). In a DNN workflow - the pack's ONNX examples, or anything going through cv2.dnn - you'll hit this the first time a blob comes out with the wrong shape for the model, and the answer is one of these nodes rather than a Python node you maintain.
The mechanism is exactly moveaxis
Two inputs beyond the array: source (default -1, the axis to move from - the channel axis in an HWC image) and dest (default 0, the axis to move to - making channels leading, i.e. NCHW). Every other axis keeps its relative order.
So the default pair -1 → 0 is precisely the HWC→CHW conversion, and running it again with 0 → -1 puts it back. If that seems too simple, that's the point: the classic hand-rolled version of this in every custom node pack is a np.transpose with a hand-typed permutation tuple, and the classic bug is getting the tuple wrong for a 3-channel vs 4-channel input. Moveaxis inverts cleanly, which transpose-with-a-tuple does not.
Output is a single nparray, the same data with axes reordered (made contiguous, so downstream cv2 calls that want a real buffer are happy).
When you actually need it
- Into a DNN: HWC array →
(C, H, W)→ the blob node. The pack also shipsCV To Blob, which does the 4-D(1, C, H, W)conversion in one step and accepts float32 directly so pre-normalized data isn't cast to uint8 on the way; if that's your target, use it instead. - Out of a DNN: a
(1, C, H, W)output back to HWC so an image node can display it. - Batch/stack juggling: any time an array has picked up a singleton or a batch axis somewhere and a consumer expects it flattened.
- LATENT arrays: the pack passes latents through as float32 arrays, and their axis order is nobody's convention but the VAE's - permuting is sometimes the only way to make two latent arrays line up.
Install
Manager → search ComfyUI CV, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
cd comfyui_cv && pip install "opencv-contrib-python-headless~=5.0.0.93"
Restart. Python ≥ 3.12 and a ComfyUI on the V3 node API. Nothing here needs contrib OpenCV, but the pack installs the pinned contrib headless wheel regardless.
Common issues
- "Cannot move axis N to M on an array of shape …" - good news, this node raises with the shape in the message rather than doing something surprising.
source/destare clamped to-4..4, so on an array with fewer dimensions than the axis you named, that's the error you get. - Shapes look right but the image comes out wrong - you permuted a 3-channel array that was actually grayscale-repeat or RGBA. Check the actual channel count; moveaxis can't tell you.
- cv2 throws about a non-contiguous array downstream - shouldn't happen here, the node makes the result contiguous. If it does, something else in the chain is slicing.
- You're looking for a shape readout - that's not this node; use
CV Array SizeorInspect CV Datato see what you actually have before permuting blindly.
The pack-level caveat stands: LLM-assisted, personal project, not production-grade, updates whenever the author feels like it. For a one-line numpy reorder, the risk is negligible - the interesting question is just whether you permuted the right axis.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| nparray | NPARRAY | Array whose axes to permute. | |
| source | INT | -1-4–4 | Axis to move FROM (-1 = last, e.g. the channel axis in an HWC image). |
| dest | INT | 0-4–4 | Axis to move TO (0 = first, e.g. making channels the leading dimension for NCHW). |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| nparray | NPARRAY | The same data with axes reordered. |