Image Perspective
Pin your corners, get the perspective for free
- images
- images
- mask
A flat render and a photograph of a wall will never agree until the flat thing is wearing the wall's perspective. WAS Image Perspective drags each corner of your frame somewhere else and lets the picture follow - a poster onto a wall, a screen into a photo, a label onto a box. It's also the tool for the reverse job: taking the keystone out of a plate shot off-axis, the architectural-correcting trick. If you want to warp a picture, this is a different animal from the local bend of Image Displacement Warp - this is the straight-line mapping a camera actually makes.
Mechanically it's a corner-pin: eight integers tell the node where each of the four corners lands, and everything between follows. The inputs are named after what they are - top_left_x, top_left_y, top_right_x, and so on. The trap for beginners is that they're not all measured the same way. Read the tooltips: top_left_x and bottom_left_x are measured across the output from the left edge, while top_right_x and bottom_right_x are measured in from the right edge. A top-left x of 0 is flush with the output's left; a top-right x of 0 is flush with its right. Get that backwards and your picture folds in on itself in a way that's confusing to untangle. The y coordinates are simpler - top corners measure down from the top, bottom corners measure up from the bottom.
Then there's the output canvas. width and height default to 0, which means "the same size the frames came in at" - fine if all four corners stay inside the frame, useless if you're pushing corners outside it to get a real perspective lean. Set 1920×1080 (or whatever your plate is) and you have room for corners that go negative. Whatever space the warped picture doesn't cover gets filled by edge: empty leaves it black (right for compositing), hold the edge smears the outermost pixel out, mirror folds the frame back on itself.
The second output is the sleeper feature. Alongside the warped images you get a mask - white where the warped picture landed, black around it - designed for compositing the result onto a plate. So the workflow is: pin the corners of your generated poster into the photo's screen area, wire the warped image and its mask into a masked composite over the room photo, and the poster now sits in the scene with correct perspective and a ready-made matte. You don't need to cut anything by hand.
Corners can be pushed anywhere within ±16384 px, so this handles dramatic foreshortening, not just gentle leans. Every frame of a batch gets the same mapping, which makes it workable for matching a graphic into a moving shot - the mapping is fixed per frame, though, so for real motion tracking you'd be driving the eight coordinates from elsewhere, not relying on a static warp.
Install
This node ships in WAS Node Suite v3. ComfyUI Manager, search "WAS Node Suite", or:
cd ComfyUI/custom_nodes
git clone https://github.com/WASasquatch/was-node-suite-comfyui
Restart after. Needs ComfyUI 0.14.0+ and Python 3.10+, and no extra dependencies or models - it runs on ComfyUI's own tensor stack. The v2 pack's install pain (pip pins that broke on ComfyUI updates) doesn't exist in v3.
Common issues
- The picture folds or mirrors wrongly. Check your
*_xconventions: the right-edge corners are measured in from the right, not across from the left. - Corners disappear off the canvas.
width/heightare at 0 (keep input size). Set an explicit output size big enough for the pushed corners. - Can't tell what's image and what's empty. Wire the
maskoutput into your composite so the warped area is the only thing pasted.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | The frames to warp. Each one gets the same mapping and comes back at the output size. | |
| top_left_x | INT | 0-16384–16384 | Where the frame's top left corner lands, across the output, in pixels. 0 = the output's own top left, 120 = 120px in from it. |
| top_left_y | INT | 0-16384–16384 | Where the frame's top left corner lands, down the output, in pixels. 0 = flush with the top, 80 = 80px down. |
| top_right_x | INT | 0-16384–16384 | Where the frame's top right corner lands, measured in from the right edge. 0 = flush with it, 120 = 120px in. |
| top_right_y | INT | 0-16384–16384 | Where the frame's top right corner lands, down the output, in pixels. 0 = flush with the top, 80 = 80px down. |
| bottom_right_x | INT | 0-16384–16384 | Where the frame's bottom right corner lands, measured in from the right edge. 0 = flush with it, 120 = 120px in. |
| bottom_right_y | INT | 0-16384–16384 | Where the frame's bottom right corner lands, measured up from the bottom edge. 0 = flush with it, 80 = 80px up. |
| bottom_left_x | INT | 0-16384–16384 | Where the frame's bottom left corner lands, across the output, in pixels. 0 = flush with the left, 120 = 120px in. |
| bottom_left_y | INT | 0-16384–16384 | Where the frame's bottom left corner lands, measured up from the bottom edge. 0 = flush with it, 80 = 80px up. |
| width | INT | 00–16384 | Width of the answer in pixels. 0 = the same width the frames came in at; 1920 makes room for corners pushed outside them. |
| height | INT | 00–16384 | Height of the answer in pixels. 0 = the same height the frames came in at; 1080 makes room for corners pushed outside them. |
| edge | COMBO | What fills the space outside the warped picture. `empty` leaves it black, which is what a composite wants; `hold the edge` smears the outermost pixel out; `mirror` folds the frame back. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| images | IMAGE | The warped frames, at the output size. |
| mask | MASK | White where the warped picture landed, black around it, for compositing it onto a plate. |