cv2.getPerspectiveTransform
Four points in, one homography out
- src
- dst
- nparray
This is the geometry half of "straighten that photographed document". You give it four source points and the four points you want them to land on; it hands you the 3×3 perspective matrix that maps one to the other. Then you feed that matrix to warpPerspective and the pixels follow. No matching, no RANSAC, no estimation - just an exact solve from a known correspondence.
How it works
A perspective (homography) transform has eight degrees of freedom, and four point correspondences give exactly eight equations. So the node solves a small linear system - that's why solveMethod exists as a dropdown built from OpenCV's DECOMP_* family (DECOMP_LU is the default, and the sane one; the SVD/QR/Cholesky/Eigen variants are there for numerically hostile cases you'll rarely meet). Change the solver and you change how the system is solved, not what it means: the resulting matrix should be the same to within floating-point noise.
The inputs are two NPARRAY point sets:
src- the four points in your image, typically ordered top-left, top-right, bottom-right, bottom-left. The pack's shipped document-scan example wires this straight fromCV Find Quadrilateral, which is the curated node that goes hunting for the page corners for you.dst- the four points you want them to become. The same example usesCV Quad To Rectangle, which turns a quad into an axis-aligned target box. If you're building your own,CV Pointsemits a point array from a typed literal, andCV Image Cornersgives you the four corners of the frame.
Output is nparray: the 3×3 matrix. Straight into warpPerspective's M input (the pack also ships CV Homography Map if you're heading for per-pixel maps instead).
The specifics that bite
Exactly four points. This is the 4-point solve, and it wants a four-point array per side - not three (that's getAffineTransform), not thirty. Hand it a detected quad with five or six vertices and OpenCV complains. If your corner detector returns more vertices than you expected, take four of them deliberately rather than hoping.
Float32. cv2 wants CV_32F point arrays. The pack's own emitters (CV Find Quadrilateral, CV Quad To Rectangle, CV Points) produce what cv2 accepts, so stay in that lane; a hand-built array of integer pixel coordinates will be rejected, and CV Cast Array is the fix if you hit it. Point arrays also want the (N, 1, 2) or (N, 2) shape - CV Reshape Array reshapes without touching data if you're arriving with something else.
Ignore one tooltip. The pack appends a generic note to any parameter literally named dst: "the low-level cv2 function writes its result into this array in place, but this wrapper passes cv2 a private copy." That's true for output-buffer parameters across the registry, and it is boilerplate noise here - for getPerspectiveTransform, dst is a genuine input (your destination points), not a scratch buffer. The wrapper does copy every array before it hands it to cv2, so nothing you link in gets mutated either way.
A note on direction
The matrix maps src to dst. That direction matters when you're chaining: if your quad is defined in a crop but you're warping the full frame, or you want the inverse mapping for sampling, you need the inverse matrix - cv2.invert is in the same pack, and CV Scale Homography rescales a homography between resolutions, a real annoyance when the detector ran on a downscaled copy.
Install
pip install "opencv-contrib-python-headless~=5.0.0.93"
ComfyUI Manager → search ComfyUI CV, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
then restart ComfyUI. Python ≥ 3.12 and a recent V3-API ComfyUI are required, and the pack is curated against OpenCV 5.0.0.93. No models, no data files - the raw cv2 lane needs nothing but the wheel.
Friction, honestly
This node is auto-generated and uncurated, and the pack's README says so in as many words. Two consequences worth internalising before you build a pipeline on it:
- You supply the correspondence. There's no RANSAC here. If your four points come from a detector, four slightly wrong points give you a confidently wrong homography - the failure looks like a smeared warp, not an error.
CV Find Homography (RANSAC)is the curated, robust alternative when you have many tentative matches and outliers; this node is the right tool when you have exactly four corners you trust. - The wide-angle caveat. A homography models a plane viewed by a pinhole camera. Straighten a page photographed with heavy lens distortion and the straight lines come out bent. Undistort first (
cv2.getOptimalNewCameraMatrix→initUndistortRectifyMap→remap), then solve for the quad. Order of operations is the whole game in this lane, and if you get it backwards the result looks almost right, which is the worst kind of wrong.
For a sanity check on the numbers before you warp anything, wire the matrix into Inspect CV Data - shape, dtype and statistics - and read it. Three rows, three columns, last element 1.0 for a proper projective transform: a fast way to spot a matrix that's actually an affine solve in disguise.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| src | NPARRAY | - - - A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. | |
| dst | NPARRAY | - - - The low-level cv2 function writes its result into this array in place, but this wrapper passes cv2 a private copy, so your input array is never modified. A data array (points / matrix), NOT an image - only an NPARRAY link is accepted here. | |
| solveMethodopt | COMBO | DECOMP_LU | - - - |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| nparray | NPARRAY | — |