cv2.getRotationMatrix2D
The 2×3 matrix behind every rotate-this-image node
- center
- nparray
Three inputs, one 2×3 matrix out. It doesn't rotate anything - it builds the affine matrix that warpAffine uses to rotate something, about a centre you choose, with an optional scale baked in. If all you want is a rotated image, a curated node will do it in one step. You come here when you want the matrix: to inspect it, to chain it, or to generate a pile of rotated variants of the same input.
The three inputs
center- the pivot, as aCV_TUPLE(x, y)and floating point. This is the input people get wrong, because "rotate the image" almost always means "about the middle", and the middle depends on the frame:(256, 256)for a 512-square,(512, 384)for 1024×768. The pack's own workflows hardcode(32.0, 32.0)on 64-pixel training tiles, which is the same arithmetic. If the size is dynamic,CV Array Sizereadsw/hoff an array for exactly this kind of input, andCV Tuplejoins them.angle- degrees. Positive values are counter-clockwise as you look at the resulting image, with the origin assumed top-left, as the pack's tooltip (lifted from OpenCV's docs) puts it. Negative is clockwise.scale- an isotropic zoom folded into the same matrix.1is "same size". It defaults to0, which is not "no scaling" - a zero scale collapses the image to a point. Set it to1explicitly. This is the single most common way to get a black frame out of this node.
Output is nparray: a 2×3 CV_64F affine matrix, ready for warpAffine's M input.
Why you'd want the matrix and not just a rotated picture
Because the matrix is reusable and composable. The pack's shipped examples use this node as a generator: exercise_shape_classifier_hog and exercise_classifier_reuse instantiate it dozens of times, with titles like "circle: rotate 10 deg", to synthesize rotated training samples from the same source tiles. Same input image, thirteen matrices, thirteen variants - and because it's a deterministic function of three numbers, the augmentation set is reproducible rather than a coin flip. That's a workflow shape no single "rotate image" node gives you.
The second reason is inspection. When a warp comes out wrong, the question is usually which of the two matrices in your chain is wrong, and being able to look at the 2×3 directly (Inspect CV Data → core Preview as Text) is a real debugging step. A pure rotation about the centre of a w×h image has a very recognisable form, and an obviously wrong matrix is easier to spot than a subtly wrong image.
If you do just want a rotated image, CV Transform (Rotate/Scale/Shift) is the curated node: rotate about centre, scale, shift and flip in one warpAffine, batch-aware, no matrix plumbing between you and the result.
The geometry that catches people
Rotating about the centre of the frame doesn't rotate the frame: fixed output dimensions mean the corners swing outside the canvas and get clipped, leaving black wedges. That's not a bug in the node, it's arithmetic. Fixing it properly means computing a larger destination size from the rotated corners and translating the matrix - or scaling down to fit. The node gives you no opinion; warpAffine will happily render into whatever dsize you give it, and blank where nothing landed.
Second: this is a similarity transform - rotation plus uniform scale plus translation, no shear or anisotropy. If you need a per-axis stretch or a perspective, getPerspectiveTransform is the wrong tool too; both live in the same pack and neither is a substitute for the other.
Third: center is a composite CV_TUPLE, so it arrives as one value and can't be half-wired. The pack's 87_latent_cv_playground workflow produces its center with a CV Tuple-style node linked into the socket while the widget still holds (0.0, 0.0) - the socket wins, but the visual is confusing until you know the convention.
Install
pip install "opencv-contrib-python-headless~=5.0.0.93"
ComfyUI Manager → search ComfyUI CV, or manually:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
Then restart. Python ≥ 3.12 plus a recent ComfyUI built on the V3 node API; behaviour is curated against OpenCV 5.0.0.93. Nothing to download - this is pure arithmetic.
The pack-level gotcha you'll meet eventually
Installing a non-contrib OpenCV wheel over a contrib one strips the contrib submodules, because all four OpenCV PyPI distributions share a single site-packages/cv2. Some of this pack's nodes silently vanish from the menu when that happens, and tools/repair_opencv_contrib.py --check (then --apply) is the pack's own remedy. It won't break this node - rotation is core cv2 - but it's the failure mode that makes half a node pack look like it was never installed, and it's worth knowing before you spend an evening re-cloning things.
Also worth reading before you build on any raw wrapper here: the README's own disclaimers. The pack was written with heavy LLM involvement, the auto-generated wrappers are uncurated, and updates aren't planned. Individual nodes like this one are exactly what they look like - one cv2 call, honestly wrapped - and the rest of the graph is your responsibility.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| center | CV_TUPLE | 0,0 | Center of the rotation in the source image. One value with 2 components (x, y) - it travels as a whole, so it cannot arrive half-connected. Wire it from 'CV Tuple' or type the components in place. |
| angle | FLOAT | 0.0000-1e+38–1e+38 | Rotation angle in degrees. Positive values mean counter-clockwise rotation (the coordinate origin is assumed to be the top-left corner). |
| scale | FLOAT | 0.0000-1e+38–1e+38 | Isotropic scale factor. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| nparray | NPARRAY | — |