Kepri Image Finalize (Resize + Crop/Pad + Bg)
The one node that finishes every product photo
- background_color
- image
- mask
- background_image
- image
- mask
- over_zoom_risk
If your workflow ends in a rat's nest of Crop, Resize, Pad and composite nodes, this is the node that eats them all. KepriImageFinalize is the flagship of the Kepri pack, and it's the terminal step of exactly one job: take a cut-out (from RMBG-2.0, BiRefNet, whatever your background-removal step was), and turn it into a finished, framed, on-background product photo in a single torch-only pass.
It was built by Kepri, a fashion-reseller startup, to run their production pipelines on Modal and RunPod - so it's API-first, every parameter is a wire, and it's tuned for the boring chores that eat your afternoon: re-centering the object, adding breathing room, hitting a target aspect ratio at a target resolution, and compositing a background without any of it looking off.
How it works
The node resolves the object mask in priority order: a wired mask input wins, otherwise the embedded alpha of an RGBA image, otherwise the whole frame. It finds the mask's bounding box, re-centers the object, applies padding, frames to the canvas, and only then composites the background - order matters, because padding changes how much of a background image shows.
The one widget that rules them all is background_mode:
- transparent - outputs an RGBA image. The alpha rides inside the IMAGE tensor, so a plain SaveImage writes a real transparent PNG.
- color - a flat
#RRGGBBfill picked with a native browser color swatch (the KEPRI_COLOR widget, not a hand-rolled canvas), plusbackground_opacityfor solid-to-semi-transparent. - image_preset - cover-crops your wired
background_image(concrete, marble, wood) to the final canvas. The mode wins over any wired background image, deliberately, so you can keep one workflow and switch campaigns by injecting just the mode.
framing has two modes: full_view fits the whole object with padding around it (the default), and details fills the format by cropping - for close-ups where the object is already cut by the photo frame and padding would render as ugly bars. Wire in framing_data from KepriFramingAnalysis and the node goes fully adaptive, resolving each axis independently and guarding the crop with max_crop_frac.
The inputs that matter
longest_edge(2048) - resolution of the longest side of the final canvas. Drop this for low-res previews, keep it for e-commerce output.aspect_ratio-1:1,4:3,16:9,original, and friends.originaluses the object's own bounding-box ratio.padding_h/padding_w- breathing room around the centered object, in percent of the object or pixels of the output. Zero means "as large as possible in the frame."
Outputs: image (RGB, or RGBA in transparent mode), mask (final alpha), and over_zoom_risk, a boolean that fires when the adaptive crop guard had to cap the zoom - wire it to a preview and log it in production.
Install
It ships in the comfyui-kepri-nodes-pack, so you get all five Kepri nodes at once. In ComfyUI Manager, search "comfyui-kepri-nodes-pack" and install; or the manual way:
cd ComfyUI/custom_nodes
git clone https://github.com/wearekhepri/comfyui-kepri-nodes-pack
Restart ComfyUI and refresh the browser; the nodes appear under Kepri. One gotcha: the README's own clone command points at wearekhepri/comfyui-kepri.git, which is the wrong URL - the real repo is comfyui-kepri-nodes-pack. And there are zero extra dependencies: no requirements.txt, no model downloads. The models live in your other nodes, not this pack.
Where people get burned
The big one: your transparent PNG comes out black. That happens when the alpha is a separate MASK output feeding SaveImage - ComfyUI drops it and writes black. Keep the alpha inside the IMAGE (transparent mode) or save the MASK path correctly. Related trap: core Load Image's MASK output is the inverted alpha (the inpainting convention), so feed an RGBA image directly or stick an InvertMask in the path. The node itself is forgiving - malformed colors or framing_data strings fall back to sane behavior instead of crashing, which is the production pedigree showing.
Inputs (15)
| Name | Type | Default | Description |
|---|---|---|---|
| longest_edge | INT | 204864–8192 | — |
| aspect_ratio | COMBO | 1:1 | 10 options: original, 1:1, 4:3, 3:4, 16:9, 9:16, +4 |
| background_mode | COMBO | color | 3 options: transparent, color, image_preset |
| background_color | KEPRI_COLOR | #FFFFFF | Couleur de fond (mode 'color'). Clique la pastille = sélecteur de couleur natif du navigateur. Format #RRGGBB. |
| background_opacity | FLOAT | 1.000–1 | Opacité du fond (mode 'color' uniquement). 1 = couleur pleine, 0 = fond transparent, entre = fond semi-transparent (l'opacité est portée par la sortie mask). |
| padding_unit | COMBO | percent | 2 options: percent, pixels |
| padding_h | INT | 00–4096 | Marge verticale (haut ET bas) autour de l'objet centré. Unité = padding_unit ci-dessus. En 'percent' = % de la hauteur de l'objet (ex: 10) ; en 'pixels' = nb de px dans la résolution finale. Appliqué AVANT le fond. |
| padding_w | INT | 00–4096 | Marge horizontale (gauche ET droite) autour de l'objet centré. Unité = padding_unit ci-dessus. En 'percent' = % de la largeur de l'objet (ex: 10) ; en 'pixels' = nb de px dans la résolution finale. Appliqué AVANT le fond. |
| imageopt | IMAGE | — | |
| maskopt | MASK | — | |
| background_imageopt | IMAGE | — | |
| framingopt | COMBO | full_view | full_view = objet entier : il rentre en entier dans le format, le padding ajoute de la marge autour. details = photo close-up où l'objet est déjà coupé par le cadre : remplit le format cible (cover) en rognant le minimum, centré, sur le côté le plus long ; padding_h/padding_w/padding_unit ignorés. |
| framing_dataopt | STRING | Métadonnées de cadrage packées venant de KepriFramingAnalysis (ex. 'details;T=1,B=0,L=0,R=0;mT=0,mB=42,mL=30,mR=30'). Si branché : override le widget framing et pilote le padding/crop ADAPTATIF par axe (bord collé = 0 pad + ancrage ; bord libre = padding donné + extension du fond ; axe collé des 2 côtés = cover). Vide/absent = widget framing, comportement inchangé. | |
| max_crop_fracopt | FLOAT | 0.220–0.9 | Garde-fou sur-zoom du mode adaptatif. Quand un axe collé des 2 côtés (cover) force l'autre axe à déborder, fraction maximale de l'objet rognable sur cet axe ; au-delà le rognage est PLAFONNÉ à cette valeur et seul le manque restant devient des bandes de fond sur l'axe collé (+ over_zoom_risk=True). Curseur continu : 0 = jamais rogner (bandes maxi), 0.9 = quasi toujours plein cadre. N'agit que si framing_data est branché. |
| aspect_ratio_overrideopt | STRING | Format branchable qui override le widget aspect_ratio. Permet de piloter PLUSIEURS KepriImageFinalize depuis une seule source (ex. primitive STRING dans le graph blending → le finalize transparent du subgraph background ET le recadrage de la plaque restent synchro). Accepte 'original' ou tout couple entier 'L:H' (ex. '1:1', '16:9'). Vide/absent ou invalide = widget aspect_ratio, comportement inchangé. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| mask | MASK | — |
| over_zoom_risk | BOOLEAN | — |