Auto Crop Black Borders
Let the pixels find the edge so you don't have to
- image
- IMAGE
- BOX_MASK
Every now and then you load an image into ComfyUI and half of it is dead space: a screenshot with a black frame around the content, a scanned document with big dark margins, a generated video frame letterboxed with bars top and bottom. You can crop by hand - stare at the numbers, guess the box - but that breaks the moment you have more than a couple of images, and honestly it's the exact job a deterministic pixel operation should own. Auto Crop Black Borders, from liaowu's tiny single-node pack of the same name, is that operation. Feed it an image, it finds the smallest rectangle that contains all the non-black content, crops to it, and hands you a mask so you can composite things back. No model files, no API, no AI even - just numpy doing bounding-box math, which is precisely the point. This is the "reach for the cheap primitive before the generative pass" lesson from the post-processing playbook applied to trimming.
How it works
Under the hood it's embarrassingly simple, and that's a compliment. The node takes your image, converts it to grayscale using the standard Rec.709 luma weights (so color makes no difference, only brightness), and flags every pixel brighter than your threshold as "content." Then it finds the topmost, bottommost, leftmost, and rightmost content pixels and crops to that bounding box. That's the whole algorithm - the README calls it "intelligent detection," but it's a five-line argmax.
Because it's brightness-based rather than edge- or model-based, it's fast and dependency-light: the only requirements are torch and numpy, both of which already ship with ComfyUI. No weights to download, no GPU footprint worth mentioning. There's a reason this class of node doesn't get talked about - it just works.
The two knobs that matter
Only three inputs exist, and two of them do the real work:
threshold(INT, default 10, range 0–255) - what counts as "black." At the default, near-black pixels are treated as background, which handles compression noise in the bars. Crank it toward 50 if your content has dark shadows you don't want eaten; drop to 0 if you genuinely have pure-black edges.padding(INT, default 0, range −1000 to 1000) - nudges the crop boundary after detection. Positive expands outward to keep a thin black border (nice for aesthetics, or when a tight crop clips anti-aliased edges); negative shrinks inward to shave residual noise or compression artifacts at the edge.
The image input is a standard ComfyUI IMAGE tensor. That's it.
The output that's easy to miss
Two outputs: IMAGE (the cropped image) and BOX_MASK (a MASK the same size as the original image, with 1s where the crop kept content and 0s over the trimmed bars). That mask is the useful half most people overlook. Wire it into a paste/composite node to put the cropped result back at full resolution, or feed it to an inpainting pass so you can regenerate the removed border instead of just discarding it. The crop-with-mask pairing is exactly what the detect→crop→re-render→paste loop from the masking-detection-detailing playbook wants, just for whole-frame borders instead of detected subjects.
Installing it
Via ComfyUI Manager, search "Auto Crop Borders" and hit Install. Or manually:
cd ComfyUI/custom_nodes/
git clone https://github.com/liaowu-boos/ComfyUI-Auto-Crop-Borders
cd ComfyUI-Auto-Crop-Borders
pip install -r requirements.txt
Then restart ComfyUI. Note the README's own manual-install section still points at a stale repo path (liaowu/comfyui-auto-crop-borders) that doesn't resolve - use the liaowu-boos/ComfyUI-Auto-Crop-Borders URL above. And honestly, the pip install step is cargo-cult: the requirements are just torch and numpy, both already present in any ComfyUI install.
Where people get burned
- Batches only crop the first image. The source explicitly grabs
image[0]and crops that one frame's bounding box. Feed it a batch or a video and you get one cropped frame, not per-frame crops. For a single still - the primary use case - no problem, but don't expect it to trim every frame of a video. - All-black input returns the input unchanged with an all-zero mask, rather than erroring. Graceful, but it means your crop silently didn't happen - worth knowing if you're automating.
- The cropped size changes. If you're cropping before a pass that expects a fixed resolution (img2img, a detailer), resize after the crop or the graph will fight you.
- Too much negative padding can invalidate the crop region, in which case the node falls back to returning the original image. Negative padding is for shaving noise, not for aggressive retrims.
For screenshots and scanned docs it's a genuine time-saver, and the mask output makes it a clean citizen in larger workflows. For everything fancier - face crops, subject-aware trimming - you want a detector, not a brightness test. This node is honest about being a primitive, and that's its charm.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| image | IMAGE | — | |
| threshold | INT | 100–255 | — |
| padding | INT | 0-1000–1000 | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| IMAGE | IMAGE | — |
| BOX_MASK | MASK | — |