Image Crop + Rotate + Pad
Crop, straighten and pad on the canvas — and get the mask for free
- image
- mask
- stitcher
- original
- width
- height
- prompt_image
Outpainting in ComfyUI has an annoying amount of prep: extend the image onto a bigger canvas, mask only the new area, and land on a size the model will accept. Most people do it with a chain of ImagePad, MaskComposite and ImageScale nodes and get it slightly wrong the first three times.
Image Crop + Rotate + Pad (QG_ImageCropRotatePad) compresses that into one node with a visual editor on it. Load an image, drag it around until the framing is right, drag a canvas edge to add padding, and the node hands you the transformed image, the mask of what needs generating, and a stitcher for putting the result back. It's the most interesting node in this pack.
How it works
The panel writes into normal node parameters - rotation_degrees, the crop_* fields, the pad_* fields - so save, undo and API submission go through ComfyUI's native path. You're dragging sliders, just with a picture attached, which means everything you do by hand has a parameter a script can set.
The mask is the clever bit. White means "the model may paint here", and three things land in it:
- Padding you added - the fill-colour edges.
- The void corners a rotation leaves behind.
- See-through parts of your source image - anything under 90% solid, i.e. the soft edge of a cutout, treated as paintable.
Point three catches people out, pleasantly or otherwise: feed an RGBA cutout into an outpaint and the transparent parts become generation targets. If you'd rather keep your picture under the paint and only mark it with the mask - the right call for a low-denoise refinement pass - set painted_area to keep picture. On fill (the default) the model never sees what was there, which is what you want when the area genuinely needs inventing.
canvas_multiple is the small feature that saves the most runs. Set it to 32 and the output becomes a multiple of 32: a padded side grows a few pixels, an unpadded side gets trimmed by a few. VAEs are much happier on multiples of 8 and up.
The stitcher is a AUSBOSS_STITCHER value - not an image, but instructions for pasting a generation back onto the original, aligned to the transform you did. Wire it into a stitch node (AusBoss's Stitch Inpaint is the intended partner) and it restores kept source pixels over the result. stitch_blend is the ramp width at the seam; stitch_grow shifts the seam itself - positive lets generation eat a strip of real image, negative keeps more of the source.
The inputs you actually touch
Fourteen required fields and seven optional ones, most of them panel-driven. The ones worth knowing:
image(required, enum) - the file picker, reading from ComfyUI'sinputfolder.rotation_degrees(required, −180 to 180) - clockwise, applied before crop and padding.crop_aspect_ratio(required) -free,source, or a fixed ratio like16:9; locks the crop handles.crop_x/crop_y/crop_width/crop_height- the crop rect on the rotated image;0width or height means "keep the full available side".pad_left/pad_top/pad_right/pad_bottom- fill pixels per side, all of which land in the mask.feather(default 24) - softens the mask into kept pixels; the image keeps a hard edge.canvas_multiple- the divisibility fix above;1disables it.fill_color- hex,R,G,Bor a CSS colour name; unparseable values fall back to mid-grey.resize_to_megapixels+megapixels+resize_method+resolution_steps(optional) - core's Scale Image to Total Pixels semantics, aspect preserved.
Outputs: image, mask, stitcher, original, width, height, prompt_image. That last one is the one people miss: same picture with transparent regions shown on white instead of the fill, so a captioning node sees a backdrop rather than a grey slab.
Install
cd ComfyUI/custom_nodes
git clone https://github.com/live1617/ComfyUI-QG-tools
Full restart; the console should say [QG-tools] v0.9.2 加载完成 | 节点数 24. No pip install needed. The node imports av for its video-frame path, but av is a ComfyUI core dependency, so any working install already has it. scipy and pymatting are optional with built-in fallbacks - their absence is not an error.
Search for it under crop, rotate, pad or outpaint; it was ported from AusBoss, MIT.
Where people get burned
Expecting alpha to be preserved. Transparent areas are paint targets, not transparency to keep. Set painted_area to keep picture when the source should stay under the paint.
A stitcher with nothing to plug it into. The stitcher output only means anything to a stitch node that understands the type. Skip it and composite manually if you must - but you then lose the "unmasked pixels never touch the VAE" benefit the KB names as the reason crop-and-stitch beat every other inpainting approach.
Rotation before crop. crop_x and crop_y are measured on the rotated image. Rotate first, then read the coordinates off the panel; do it the other way round and your crop numbers land somewhere you didn't choose.
Inputs (21)
| Name | Type | Default | Description |
|---|---|---|---|
| image | COMBO | Choose or upload an image from ComfyUI's input folder. | |
| rotation_degrees | FLOAT | 0.0-180–180 | Clockwise rotation applied before crop and padding. |
| crop_aspect_ratio | COMBO | free | Locks crop handles to a ratio; free allows any rectangle. |
| crop_x | INT | 00–65536 | Left edge of the crop in pixels, measured on the rotated image. |
| crop_y | INT | 00–65536 | Top edge of the crop in pixels, measured on the rotated image. |
| crop_width | INT | 00–65536 | 0 keeps the full available width. |
| crop_height | INT | 00–65536 | 0 keeps the full available height. |
| pad_left | INT | 00–32768 | Fill-color pixels added on the left; padding lands in the output mask. |
| pad_top | INT | 00–32768 | Fill-color pixels added on top; padding lands in the output mask. |
| pad_right | INT | 00–32768 | Fill-color pixels added on the right; padding lands in the output mask. |
| pad_bottom | INT | 00–32768 | Fill-color pixels added on the bottom; padding lands in the output mask. |
| feather | INT | 240–4096 | Feathers the mask into kept pixels; the image keeps a hard edge against the fill. |
| canvas_multiple | INT | 11–4096 | Makes the width and height divide evenly by this number: padding grows by a few pixels, and a side with no padding is trimmed by a few instead. Some models need sizes divisible by 8, 16 or 32; 1 = off. |
| fill_color | STRING | #808080 | Color for rotation voids and padding; accepts #RGB/#RRGGBB hex, R, G, B (0-255 or 0..1 floats), one grayscale number, or a CSS color name. Unparseable values fall back to mid-gray. |
| resize_to_megapixelsopt | BOOLEAN | false | Resize the output to the megapixel budget below, aspect preserved. |
| megapixelsopt | FLOAT | 1.000.01–16 | Output pixel budget in megapixels (x 1024x1024, matching the core Scale Image to Total Pixels node). |
| resize_methodopt | COMBO | lanczos | Sampling filter for the resize; lanczos is the sharp default. |
| resolution_stepsopt | INT | 11–256 | Rounds each resized dimension to a multiple of this - 8 or 64 keeps VAE-friendly sizes. |
| stitch_blendopt | INT | 320–512 | Ramp of the stitcher's paste, in pixels, into the kept picture: where generated pixels fade over the source. Separate from feather, which shapes the mask itself. |
| stitch_growopt | INT | 0-256–256 | Moves the paste boundary before the ramp: positive lets the generation replace a strip of the source next to the seam, negative keeps more of the source. |
| painted_areaopt | COMBO | fill | What the parts you painted in the MaskEditor are handed on as (see-through parts of a PNG count too). Fill: flat fill, so the model never sees what was there. Keep picture: your picture stays under the paint and only the mask marks it. Pick keep picture when a later step needs what was there, like a denoise under 1. |
Outputs (7)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | The transformed image batch in BHWC format. |
| mask | MASK | White where the model paints: padding, the corners a turn leaves empty, and see-through parts of your picture. |
| stitcher | AUSBOSS_STITCHER | Full-canvas stitcher: restores kept source pixels over an outpaint result; wire to Stitch Inpaint. |
| original | IMAGE | Your picture before rotation, crop, padding or resize. See-through parts show as white, or as your picture when painted_area keeps it. |
| width | INT | Output width after the transform and any resize. |
| height | INT | Output height after the transform and any resize. |
| prompt_image | IMAGE | The image with see-through parts shown on white instead of the fill. Wire it to the node that writes your prompt, so a cutout gets a real backdrop. The same as image when your picture has no see-through parts, or painted_area keeps the picture. |