Nodes/ComfyUI-QG-tools/Image Crop + Rotate + Pad
ComfyUI Node

Image Crop + Rotate + Pad

Crop, straighten and pad on the canvas — and get the mask for free

By live1617·Created 9 days ago·Updated a day ago· 1
Image Crop + Rotate + Pad
    • image
    • mask
    • stitcher
    • original
    • width
    • height
    • prompt_image
    ◄image▾►
    ◄rotation_degrees0.0►
    ◄crop_aspect_ratiofree►
    ◄crop_x0►
    ◄crop_y0►
    ◄crop_width0►
    ◄crop_height0►
    ◄pad_left0►
    ◄pad_top0►
    ◄pad_right0►
    ◄pad_bottom0►
    ◄feather24►
    ◄canvas_multiple1►
    ◄fill_color#808080►
    ◄resize_to_megapixelsfalse►
    ◄megapixels1.00►
    ◄resize_methodlanczos►
    ◄resolution_steps1►
    ◄stitch_blend32►
    ◄stitch_grow0►
    ◄painted_areafill►

    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:

    1. Padding you added - the fill-colour edges.
    2. The void corners a rotation leaves behind.
    3. 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's input folder.
    • rotation_degrees (required, −180 to 180) - clockwise, applied before crop and padding.
    • crop_aspect_ratio (required) - free, source, or a fixed ratio like 16:9; locks the crop handles.
    • crop_x / crop_y / crop_width / crop_height - the crop rect on the rotated image; 0 width 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; 1 disables it.
    • fill_color - hex, R,G,B or 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.

    CategoryQG-tools/Image

    Inputs (21)

    NameTypeDefaultDescription
    imageCOMBOChoose or upload an image from ComfyUI's input folder.
    rotation_degreesFLOAT0.0-180–180Clockwise rotation applied before crop and padding.
    crop_aspect_ratioCOMBOfreeLocks crop handles to a ratio; free allows any rectangle.
    crop_xINT00–65536Left edge of the crop in pixels, measured on the rotated image.
    crop_yINT00–65536Top edge of the crop in pixels, measured on the rotated image.
    crop_widthINT00–655360 keeps the full available width.
    crop_heightINT00–655360 keeps the full available height.
    pad_leftINT00–32768Fill-color pixels added on the left; padding lands in the output mask.
    pad_topINT00–32768Fill-color pixels added on top; padding lands in the output mask.
    pad_rightINT00–32768Fill-color pixels added on the right; padding lands in the output mask.
    pad_bottomINT00–32768Fill-color pixels added on the bottom; padding lands in the output mask.
    featherINT240–4096Feathers the mask into kept pixels; the image keeps a hard edge against the fill.
    canvas_multipleINT11–4096Makes 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_colorSTRING#808080Color 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_megapixelsoptBOOLEANfalseResize the output to the megapixel budget below, aspect preserved.
    megapixelsoptFLOAT1.000.01–16Output pixel budget in megapixels (x 1024x1024, matching the core Scale Image to Total Pixels node).
    resize_methodoptCOMBOlanczosSampling filter for the resize; lanczos is the sharp default.
    resolution_stepsoptINT11–256Rounds each resized dimension to a multiple of this - 8 or 64 keeps VAE-friendly sizes.
    stitch_blendoptINT320–512Ramp 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_growoptINT0-256–256Moves 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_areaoptCOMBOfillWhat 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)

    NameTypeDescription
    imageIMAGEThe transformed image batch in BHWC format.
    maskMASKWhite where the model paints: padding, the corners a turn leaves empty, and see-through parts of your picture.
    stitcherAUSBOSS_STITCHERFull-canvas stitcher: restores kept source pixels over an outpaint result; wire to Stitch Inpaint.
    originalIMAGEYour picture before rotation, crop, padding or resize. See-through parts show as white, or as your picture when painted_area keeps it.
    widthINTOutput width after the transform and any resize.
    heightINTOutput height after the transform and any resize.
    prompt_imageIMAGEThe 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.