CV Quad To Rectangle
The target rectangle for a document scan
- corners
- rect_corners
- dsize
- width
- height
- size
Half of a document scan. CV Find Quadrilateral finds the page in the photo; this node works out what rectangle the page should be, so the perspective transform has a destination.
What it does
Take a quadrilateral in TL/TR/BR/BL order and answer two questions: how big should the output be, and where are the corners of that output? It gives you the destination corners in the same order, a dsize string, and the width and height as numbers.
The naive answer - and the default - is to measure the projected edge lengths and take the longest of each opposing pair. That's fine for mild perspective and badly wrong for strong perspective. Shoot a portrait page from its bottom edge and the top edge collapses to a fraction of the bottom one in the photo, so the "rectangle" this produces is a stretched landscape, and your document comes out sideways and squashed.
The perspective-corrected mode
Wire image_width and image_height - the dimensions of the photo the corners live in, straight from CV Array Shape - and both dimensions above zero switch on a proper aspect recovery from the perspective itself, using Zhang's whiteboard method with the assumption of square pixels and a principal point at the image centre. The output keeps the pixel count of the edge-length version, but redistributes it according to the recovered aspect ratio, so a portrait page rectifies portrait.
focal_ratio (0.78 by default) only comes into play when the quad has a single vanishing point - opposing edges parallel in the photo, so the focal length can't be recovered from the quad. 0.78 approximates a 28mm-equivalent phone lens; smaller values mean a wider lens and a stronger correction. If your scan looks subtly over- or under-stretched on an almost-fronto-parallel shot, this is the dial.
max_size (8192) caps the output dimensions, scaling the result down proportionally. Photograph a big poster and you can easily ask for a 12000-pixel edge; this keeps it sane.
Inputs and outputs
corners is a 4x1x2 quad in TL/TR/BR/BL order. Order matters here, unlike in the sibling CV Quad Warp, which sorts its points for you - so if your corners come from a detector, check they're in the order this node documents.
Out come rect_corners (4x1x2, same order), dsize - a "(width, height)" literal for the cv2 wrappers whose dsize is still free text - plus width and height as integers, and size, the same pair as one composite value for cv2.warpPerspective's dsize socket.
The assembly is: corners and rect_corners into cv2.getPerspectiveTransform as source and destination, then that matrix into cv2.warpPerspective with dsize. That's a manual document scanner, and it is genuinely just those three steps - the value of this node is getting the destination rectangle right, not the warp.
Installing
Pack-wide. ComfyUI Manager → search "ComfyUI CV", or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
# restart ComfyUI
Python ≥ 3.12, a recent V3-API ComfyUI, opencv-contrib-python-headless~=5.0.0.93, numpy, torch. Nothing to download.
Gotchas
Zero image dimensions keeps the legacy behaviour. If your aspect ratio looks wrong, check that image_width and image_height are actually wired - 0 is not "auto", it's "use the old projected-edge method", and the node gives you the wrong answer silently rather than complaining.
Degenerate quads fall back. Three collinear corners or an implausible recovered focal length makes the true-aspect path bail out and return the edge-length rectangle. That's the sane failure, but it means a bad set of corners produces a plausible-looking result - eyeball the output before you trust a batch.
Ordering is on you. TL/TR/BR/BL, consistently. If you need automatic ordering, use CV Quad Warp instead or sort the points upstream.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| corners | NPARRAY | 4x1x2 quad ordered TL/TR/BR/BL (from 'CV Find Quadrilateral'). | |
| max_size | INT | 819264–32768 | Cap on the output width/height; larger results are scaled down proportionally. |
| image_widthopt | INT | 00–32768 | Width of the photo the corners live in. Both dimensions > 0 enable perspective-corrected aspect recovery; 0 keeps the projected-edge-length behavior. Wire from 'CV Array Shape'. |
| image_heightopt | INT | 00–32768 | Height of the photo the corners live in. Both dimensions > 0 enable perspective-corrected aspect recovery; 0 keeps the projected-edge-length behavior. Wire from 'CV Array Shape'. |
| focal_ratioopt | FLOAT | 0.780.05–5 | Assumed focal length as a fraction of max(image_width, image_height), used ONLY when the quad has a single vanishing point (opposing edges parallel in the photo), where the focal length cannot be recovered from the quad. 0.78 ~ a 28mm-equivalent phone lens; smaller = wider lens = stronger foreshortening correction. |
Outputs (5)
| Name | Type | Description |
|---|---|---|
| rect_corners | NPARRAY | 4x1x2 float32 destination rectangle, same TL/TR/BR/BL order. |
| dsize | STRING | '(width, height)' literal, for the composite cv2 params that are still free-text. |
| width | INT | — |
| height | INT | — |
| size | CV_TUPLE | The target (width, height) as ONE composite value - wire it into cv2.warpPerspective's dsize. |