Nodes/ComfyUI CV/CV Quad To Rectangle
ComfyUI Node

CV Quad To Rectangle

The target rectangle for a document scan

By bmad4ever·Created 4 months ago·Updated 15 days ago· 1
CV Quad To Rectangle
  • corners
  • rect_corners
  • dsize
  • width
  • height
  • size
◄max_size8192►
◄image_width0►
◄image_height0►
◄focal_ratio0.78►

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.

Categoryimage/CV/contours

Inputs (5)

NameTypeDefaultDescription
cornersNPARRAY4x1x2 quad ordered TL/TR/BR/BL (from 'CV Find Quadrilateral').
max_sizeINT819264–32768Cap on the output width/height; larger results are scaled down proportionally.
image_widthoptINT00–32768Width 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_heightoptINT00–32768Height 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_ratiooptFLOAT0.780.05–5Assumed 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)

NameTypeDescription
rect_cornersNPARRAY4x1x2 float32 destination rectangle, same TL/TR/BR/BL order.
dsizeSTRING'(width, height)' literal, for the composite cv2 params that are still free-text.
widthINT—
heightINT—
sizeCV_TUPLEThe target (width, height) as ONE composite value - wire it into cv2.warpPerspective's dsize.