cv2.invertAffineTransform
Going backwards through a warp without rebuilding the matrix by hand
- M
- nparray
You warped an image. A rotation, a scale, a shift - a 2×3 affine matrix, probably from CV Transform (Rotate/Scale/Shift) or cv2_estimateAffinePartial2D, applied with cv2_warpAffine. Then something downstream wants to go the other way: the user clicked a point in the warped output and you need to know where it came from, or you want to paste content back into the original frame's coordinate system. You need the inverse of that 2×3.
cv2.invertAffineTransform is that, and nothing else. A thin raw wrapper in bmad4ever/comfyui_cv.
Why there's a dedicated node for it
A general cv2.invert gives you the inverse of a square matrix, and it comes with a status flag, decomposition options and a pseudo-inverse fallback. That's overkill and mismatch for an affine transform, which lives as a 2×3 - three columns, not a square. invertAffineTransform knows the shape, does the closed-form 2×2 inverse plus the translation flip, and returns the inverse as another 2×3.
Input is M - NPARRAY only, because it's a matrix, not a picture. Output is a single nparray: the inverse, same 2×3 layout, ready to feed back into cv2_warpAffine, or into the point-transform nodes if you're mapping coordinates rather than pixels.
That last distinction is the one worth holding on to. There are two ways to "unwarp", and they are not the same operation:
- Warp the image with the inverse matrix. Produces a new picture in the original frame's geometry. Pixels get resampled twice if you do it after a forward warp, so quality suffers - do it on the source image when you can.
- Transform points with the inverse. Cheap and lossless. If all you need is "where did this landmark come from", map the coordinates and leave the pixels alone.
The pack has nodes for both directions (CV Transform Points (Scale, Rotate, Translate), the map generators, cv2_warpAffine and friends), which is handy: you can verify the inverse is right by mapping four corners of the output back and checking they land where you expect.
Where it fits in a workflow
The realistic pattern is a round trip. Fit an affine from correspondences (or build one by hand with a rotate/scale/translate node), warp with it, do something in the warped space - measure, detect, annotate, blend - then invert the matrix to put your results back in the original coordinates. That's the same shape as the homography workflows, just with the simpler transform family.
It's also the honest tool when someone hands you a 2×3 and asks for the reverse: reformulating it as a 3×3 to use cv2_invert works, and is more typing and more assumptions.
Installing the pack
Manager → search comfyui_cv, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Python ≥ 3.12 and a ComfyUI recent enough for the V3 node API; OpenCV behaviour curated at 5.0.0.93. Core OpenCV function, so no contrib-specific concern for this node - though if a different node from this pack is missing from your menu, the contrib wheel question is where to look (tools/repair_opencv_contrib.py --check).
Gotchas
Give it a 2×3. The function is defined for the affine layout. A 3×3 homography is a different object - that's cv2_invert's job, and passing one here is a category error, not a small typo.
Don't forget the translation. Hand-rolled inverses of affine transforms almost always get the rotation part right and the translation part wrong, because the translation must be rotated and negated. That's exactly the arithmetic this node exists to save you from.
Chained warps need composition, not repeated inversion. Inverting warp B, then warp A, then multiplying them is not the same as inverting their product - mind the order, and promote to 3×3 with an eye if you need to compose affines.
Resolution still belongs to the pixels. The inverse matrix is exact; the image it produces depends on the interpolation and borders you choose in the warp node, so a forward-and-back round trip will be slightly soft. That's inherent to resampling, not a bug in this node.
As for the pack overall: its README is explicit that it's a personal, heavily LLM-assisted project, with possible overfitting to its own tests and a warning against production use without review. For a closed-form wrapper like this one, the interface is the risky part, not the maths - and the interface here is a single matrix in, a single matrix out.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| M | NPARRAY | - - - A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| nparray | NPARRAY | — |