Image Transform KJ
Crop, resize, pad, and rotate on one interactive canvas
- image
- mask
- output
- output_mask
- bbox
- bbox_mask
- width
- height
Most image workflows end up with a chain of separate crop, resize, pad, and rotate nodes stacked one after another, each with its own numeric widgets you're nudging blind. Image Transform KJ replaces that chain with one node and a live canvas: connect an image, a preview appears right there in the node, and you draw your crop, drag to reposition, and rotate by eye instead of by trial-and-error numbers.
How it works
Once an image is connected, the preview shows up automatically inside the node. From there it's direct manipulation, not widgets:
- Cropping - click and drag to draw a crop region. Drag inside the region to move it, drag an edge or corner to resize it. Right-click a region to delete it. Hold Ctrl while dragging to snap to a grid, Shift while resizing to constrain aspect ratio, and Alt while resizing to resize symmetrically from the center.
- Padding - hold Shift and drag to adjust where the padding sits relative to your crop.
- Rotation - a rotate button turns on a rotation cross you drag to spin the image; right-click it to reset back to zero.
Underneath the interaction, target_width/target_height (0 = keep the original size) control the final output resolution, keep_proportion decides how the source fits into that target (crop-to-fill vs. pad-to-fit vs. stretch, depending on what your build exposes), and extra_padding adds padding filled with either a flat color or an edge-extension mode (clamp, repeat, or mirror) rather than leaving it blank.
The inputs and outputs that matter
imagein (accepts a flexible image-type input, so it plays nicely with different image/list shapes elsewhere in the graph) - the source you're transforming, optionally paired with an optionalmaskinput if you're transforming a masked region alongside the image.target_width/target_height(0 = keep original) - your desired output dimensions.upscale_method(defaultlanczos) - the resize algorithm when scaling is involved.divisible_by(default 2) - rounds output dimensions to a multiple of this value, which matters a lot for video and diffusion models that require dimensions divisible by 8, 16, or 32; set it to whatever your downstream model actually needs.bboxes(STRING) - lets you pass crop-region coordinates as text instead of drawing them by hand, useful once you've got a region dialed in and want to reuse or automate it.
Outputs come back as lists, which matches the fact that you can draw more than one crop region on the canvas: output (the transformed image(s)), output_mask, bbox (the raw crop-region coordinates), bbox_mask, plus scalar width and height for the final dimensions.
Installing it
Ships with the pack:
- ComfyUI Manager - search KJNodes for ComfyUI, install, restart.
- Manual -
cd ComfyUI/custom_nodes && git clone https://github.com/kijai/ComfyUI-KJNodes, thenpip install -r ComfyUI-KJNodes/requirements.txt, restart.
The interactive canvas is a JS-side feature of the pack, so make sure your browser cache isn't serving a stale copy of the extension after an update - a hard refresh of the ComfyUI tab fixes more "the node looks wrong" reports than you'd expect with any interactive custom node.
Common issues & troubleshooting
Output size isn't what I expected. Check divisible_by first - it silently rounds your final dimensions, so a target_width of 1000 with divisible_by at 2 might not land exactly where you think once padding and proportion rules are applied on top.
Multiple crops came out as a batch when I only wanted one. That's expected once you understand the output is list-typed: every crop region you draw on the canvas produces an entry in output. If you only drew one region, you'll get one item - if a downstream node is complaining about batch size, check how many regions are actually on the canvas.
Can't tell what changed after clicking around the canvas. The interaction set is dense (Ctrl, Shift, Alt each do something different depending on whether you're cropping or resizing), and it's easy to trigger a mode you didn't mean to. Right-click resets a region or the rotation cross - that's the fastest way back to a known state if you've lost track of what you did.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| image | COMFY_MATCHTYPE_V3 | The image or mask to transform. | |
| target_width | INT | 00–16384 | Target output width. 0 = keep original dimensions. |
| target_height | INT | 00–16384 | Target output height. 0 = keep original dimensions. |
| upscale_method | COMBO | lanczos | Interpolation method for resizing. |
| keep_proportion | COMBO | 8 options: [object Object], [object Object], [object Object], [object Object], [object Object], [object Object], +2 | |
| divisible_by | INT | 20–512 | — |
| extra_padding | COMBO | 5 options: [object Object], [object Object], [object Object], [object Object], [object Object] | |
| invert_crop | COMBO | 2 options: [object Object], [object Object] | |
| bboxes | STRING | — | |
| maskopt | MASK | Optional mask to transform alongside the image. |
Outputs (6)
| Name | Type | Description |
|---|---|---|
| output | COMFY_MATCHTYPE_V3 | — |
| output_mask | MASK | — |
| bbox | BBOX | — |
| bbox_mask | MASK | — |
| width | INT | Width of the output image. |
| height | INT | Height of the output image. |