Kepri Framing Analysis (Border Contact + Margins)
Is your product touching the edge of the frame? This node decides
- mask
- mask
- framing
- touches_top
- touches_bottom
- touches_left
- touches_right
- margin_top
- margin_bottom
- margin_left
- margin_right
- free_sides
- framing_data
Here's the product-photo problem nobody warns you about: you've cut out the object, you need a 1:1 canvas, and the object touches the bottom edge of the original photo. Pad that side and you've painted a fake background band where the shoe was cropped by the frame. Crop it blindly and you might cut the object. KepriFramingAnalysis is the node that looks at the mask and tells the rest of the pipeline which sides are actually glued to the border - so padding and cropping happen per side instead of by guesswork.
It's part of the Kepri pack, built by a fashion-reseller startup for exactly this job: their automated background-removal and product-centering backend. Roughly 90% of shots are full_view - the object floats with room around it - but the moment one border is touched, that default padding is wrong and an adaptive rule has to kick in. This node detects and measures that. It doesn't do the pad/crop math itself; KepriImageFinalize does that. This one is the eyes.
Why it has to run before the bounding box
A mask→bbox node recenters the object, which destroys the "did it touch a wall?" information forever. So this node runs on the full-image mask before the bbox step, and its findings travel past that node in a packed string (see framing_data below). Placement: right after the chosen mask - post-fallback if you've chained it after KepriMaskAnalysis - and before the mask→bounding-box node.
How it decides
It binarizes the mask and scans a thin band along each edge, then applies two thresholds:
edge_tolerance_px(2) - active pixels within this many pixels of the edge count as "touching".contact_min_percent(1.0) - the minimum % of the border's length that must actually be covered for it to count as contact, so one stray pixel doesn't flip the verdict.
Any border that clears both is touching; a side with no contact is free, and the free margin (edge→object, as a % of that dimension) is measured for the sides that matter.
Outputs
Most of them are for debugging, and one is the real wire:
framing-full_view(no contact) ordetails(≥1 contact).touches_top/bottom/left/right- four booleans.margin_top/bottom/left/right- free margin per side in %,0.0when touching.free_sides- comma-joined, handy in a preview node.framing_data- the packed string likedetails;T=1,B=0,L=0,R=0;mT=0,mB=60,mL=30,mR=30. Wire this single link into KepriImageFinalize'sframing_datainput and the finalize node goes fully adaptive: glued side gets zero padding and stays flush, free sides get the given padding with background extension, and an axis glued on both sides gets cover-cropped up to a safety cap. Themaskoutput is a passthrough, so the node doesn't disturb your existing wiring.
Gotchas and install
An empty mask doesn't crash it - it returns full_view, all margins at 100, all sides free, and leaves the degenerate case to KepriMaskAnalysis upstream. Batched masks (multi-box detection before a merge) are unioned across the batch, so the verdict never depends on detection order.
It's part of comfyui-kepri-nodes-pack: ComfyUI Manager → search "comfyui-kepri-nodes-pack", or:
cd ComfyUI/custom_nodes
git clone https://github.com/wearekhepri/comfyui-kepri-nodes-pack
Restart and refresh; it appears under Kepri/Background. No extra dependencies, no models to download. One small trap: the README's clone snippet uses the wrong repo URL (comfyui-kepri.git), so copy the command above.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| mask | MASK | — | |
| edge_tolerance_px | INT | 20–64 | An object counts as 'touching' a border if active pixels lie within this many px of the edge. |
| contact_min_percent | FLOAT | 1.00–100 | Minimum % of the border length that must be covered to count as contact (ignores a stray pixel). |
Outputs (12)
| Name | Type | Description |
|---|---|---|
| mask | MASK | — |
| framing | STRING | — |
| touches_top | BOOLEAN | — |
| touches_bottom | BOOLEAN | — |
| touches_left | BOOLEAN | — |
| touches_right | BOOLEAN | — |
| margin_top | FLOAT | — |
| margin_bottom | FLOAT | — |
| margin_left | FLOAT | — |
| margin_right | FLOAT | — |
| free_sides | STRING | — |
| framing_data | STRING | — |