Skip to main content

smart-crop-ai

Crop to the interesting part of an image instead of the middle of it — and get a confidence number that tells you when the crop is barely better than a guess.

Install

pip install smart-crop-ai

Quickstart

import numpy as np, smart_crop_ai
from PIL import Image

wall = np.full((400, 800, 3), 200, dtype=np.uint8)                       # a plain wall
subject = np.random.default_rng(0).integers(0, 255, (140, 140, 3))       # something worth keeping
wall[140:280, 560:700] = subject.astype(np.uint8)                        # off to the right
result = smart_crop_ai.crop(Image.fromarray(wall), ratio=(1, 1))
print(result.summary())
smart-crop-ai: <image>
  source      800 x 400 pixels
  asked for   400 x 400 pixels
  crop        400 x 400 at (328, 0), box (328, 0, 728, 400)
  strategy    saliency (asked for: auto)
  confidence  0.69  strong - a centre crop of the same size would have missed 69% of the detail this window keeps
  keeps       50% of the source area, off centre

result.image is the cropped PIL.Image. A centre crop would have sliced the subject in half; this one contains it, and says by how much it won.

What it does

  • Finds the subject. Every window of the size you asked for is scored against an energy map and the best one wins. No model, no download, no OpenCV.
  • Tells you when it did not. confidence is the share of the chosen window's detail that a plain centre crop would have missed. At 0, cropping here bought you nothing, and notes says why.
  • Refuses to bluff. A blank wall, a gradient, a sky — anything with a flat energy map falls back to the centre and records that it did, instead of maximising sensor noise and calling it a subject.
  • Says which of the two it was. Confidence 0 has two very different causes, and notes never confuses them: either the image holds no detail anywhere, or it holds plenty but your crop is so large that every placement contains the same thing. The second one tells you to ask for a smaller crop.
  • Never upscales behind your back. Ask for a crop bigger than the source and you get the whole image, confidence 0, and a note saying so.
  • Takes any image Pillow can open. Greyscale, RGBA, palette, 16-bit and CMYK all work. EXIF orientation is applied before anything is measured, so the box refers to the upright image.
  • Leaves your image alone. The input is never modified, and the same input always produces the same box.

API

crop(image, width=None, height=None, *, ratio=None, strategy="auto", padding=0.05) -> CropResult

Give width and height, or a ratio such as (16, 9), 1.0 or "16:9" — not both, which raises ValueError. A single width or height keeps the source's aspect ratio. image is a PIL.Image, a path, or a numpy array.

strategy what it measures when to reach for it
"auto" saliency, falling back to the centre when the map is nearly flat the default; use this
"saliency" gradient magnitude plus local colour variance general photographs
"entropy" local Shannon entropy texture, foliage, crowds
"edges" Sobel edge density product shots on plain backgrounds
"center" nothing a baseline to compare against

padding (0 to 0.45) is breathing room: that fraction of each edge is left out of the score, so the subject is framed inside the crop rather than pressed against its border.

crop_to_file(src, dst, **kw) -> CropResult   # crop and save; **kw goes to crop()
thumbnail(image, size, **kw) -> PIL.Image    # crop to the subject, then resize to exactly size

thumbnail crops at the aspect ratio of size and then scales, so the subject survives the shrink instead of landing wherever it lands.

CropResult

attribute what it holds
.image the cropped PIL.Image.Image
.box (left, top, right, bottom) in source pixels, always inside the image
.strategy_used what actually chose the box: a strategy name, "center" on a flat image, or "whole_image"
.confidence 0 to 1 — how much the chosen window beat a centre crop
.confidence_label "strong", "moderate", "weak" or "none"
.notes anything you should know, in plain language
.scores the raw window and energy numbers behind the decision
.size, .offset, .covers, .moved convenience views of the box
.summary() the human-readable report above
.to_dict() / .to_json() JSON-safe, pixels left out
.save(path) write the crop; alpha is flattened onto white for JPEG

CLI

Nothing is written unless you ask, so you can look at the confidence first.

smart-crop-ai photo.jpg --ratio 16:9
smart-crop-ai photo.jpg --width 800 --height 600 --output hero.jpg
smart-crop-ai shots/ --ratio 1:1 --out-dir square/ --strategy edges --suffix -crop
smart-crop-ai shots/ --recursive --ratio 4:3 --quiet
smart-crop-ai photo.jpg --ratio 1:1 --json

--thumb WxH resizes after cropping, --report PATH writes the printed report as UTF-8, and --min-confidence F exits with code 2 when any crop scores below F — useful in a pipeline that would rather fail than ship a guess. Exit codes: 0 all good, 1 a usage or read error, 2 under --min-confidence.

License

MIT

Metadata

Release files for smart-crop-ai 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for smart-crop-ai 0.1.0
File Size Uploaded
smart_crop_ai-0.1.0.tar.gz 36.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for smart-crop-ai 0.1.0
File Interpreter ABI Platform
smart_crop_ai-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 62.7 kB

Release files / smart_crop_ai-0.1.0.tar.gz

Download URL smart_crop_ai-0.1.0.tar.gz
Size 36.3 kB
Tags Source
SHA-256 checksum
How to use checksums
736e7d4a6268e4e99b1ac4e442537e18a2741157aa329b1e6916b7db3be4fd87
BLAKE2b-256 checksum
How to use checksums
8d9247e9c0d3a82a3842f472f7484a2a00923126bddcdae991711dc15430e082
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release files / smart_crop_ai-0.1.0-py3-none-any.whl

Download URL smart_crop_ai-0.1.0-py3-none-any.whl
Size 26.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a913585537f4f3e9adef177dca0826fa635056b5524c100ea9c7a343ee4b1589
BLAKE2b-256 checksum
How to use checksums
d86ebf2d8a8324b6a33e3f6d399739dab6d6b10c0b338ca38add46b03d21eb0e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.14

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page