CLI tool
leblancfg/autocrop avatar
leblancfg/autocrop

autocrop: face-aware image cropping from the shell or Python

📸 Automatically detects and crops faces from batches of pictures.

680 stars120 forksPythonNOASSERTION

At a glance

What is it?
autocrop wraps OpenCV's YuNet face detector in a single-purpose CLI and a Cropper class, so batches of portraits come out centered on the biggest detected face. It processes one image per invocation, which shapes both the install and the batch scripts.
Who is it for?
Adopt autocrop if you have a directory of portraits or ID-card photos and want the crop centered on the largest detected face without writing detection code, either through the autocrop command or the Cropper class. Skip it if you need multi-face layout, video input, or a service that processes many images in one process, since the README states it deliberately handles one image per invocation.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 21 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 10, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What autocrop does with a photo of a face

The README is direct about the output: autocrop "will output images centered around the biggest face detected." That single sentence defines the scope. It is not a general smart-crop tool that weighs composition, horizon lines or saliency. It detects faces, picks the largest one, and frames the crop around it.

The README names two intended uses: "profile picture processing for your website or batch work for ID cards." Both share a shape. You have many images, each with one dominant face, and you want a consistent, centered result. Website avatars and ID photos fit that description; a group photo where four people matter equally does not. The README says biggest face, singular, and nothing in the CLI options suggests a way to keep a second face in frame.

Because the tool is face-aware rather than face-recognizing, it does not care who is in the picture. There is no enrollment, no gallery, no identity output. Detection drives a crop rectangle and nothing else.

YuNet detection and the Cropper pipeline

The README states that "Autocrop v2 uses OpenCV's YuNet neural-network face detector." The model file is not downloaded at runtime. The pyproject.toml force-includes autocrop/face_detection_yunet_2023mar.onnx into the built wheel, so the detector ships inside the package. That choice removes a network dependency at first run and pins the model version to the release you installed.

The dependency list is short: numpy>=1.10, opencv-python-headless>=4.8,<5, and Pillow>=9.0.0. The headless OpenCV build is the one detail worth noticing. It omits GUI functions, which is the right trade for a library and CLI that only reads and writes image files, and it keeps the install smaller than the full opencv-python wheel.

From Python, the flow is: construct a Cropper, call crop() with a filepath or an OpenCV-style BGR/BGRA numpy array, and receive an RGB/RGBA numpy array back. The README notes the return is None when no face is detected, and the example guards on that before saving. That None return is the whole error contract in the Python path. There is no exception to catch for the common case of a photo without a face. The CLI path writes cropped bytes to stdout when no output is given, so a failed detection and a successful one are not obviously distinguished by exit status in the README's examples.

Installing autocrop and cropping a first portrait

The README's installation section is one command. It assumes Python 3.10 or newer, which pyproject.toml sets as requires-python.

bash
pip install autocrop

After that, the autocrop entry point is on your PATH, registered in pyproject.toml as autocrop = "autocrop:command_line_interface". The simplest run takes one file and writes the cropped image to stdout, which the README shows as a redirect.

bash
autocrop portrait.jpg > cropped.jpg

If you prefer an explicit output path, pass -o. The extension of that path selects the output format, so the same input can become a PNG.

bash
autocrop portrait.jpg -o cropped.png

The defaults are 500 by 500 pixels, per the -w and -H options. If you want the original crop pixels instead of that resize, add --no-resize.

bash
autocrop portrait.jpg --no-resize > cropped.jpg

The Python route is the same pipeline with an object in front of it. The README's example imports Cropper, calls crop(), and saves with Pillow only when the result is not None.

python
from PIL import Image
from autocrop import Cropper

cropper = Cropper()
cropped_array = cropper.crop('portrait.png')

if cropped_array is not None:
    Image.fromarray(cropped_array).save('cropped.png')

What you should see: a square image centered on the detected face, 500x500 by default, or the untouched crop rectangle with --no-resize. If the source has no detectable face, the Python call returns None and the CLI writes nothing useful to your redirect target.

Batching is shell work, not a built-in feature

This is the design decision that will decide whether autocrop fits your workflow. The README states plainly that "Autocrop intentionally processes one image per invocation. For recursive or filtered batch workflows, compose autocrop with shell tools." There is no --recursive flag, no input directory argument, and no worker pool. The source positional accepts one file, or '-' for stdin.

The README supplies recipes for find, fd, xargs, and GNU parallel. The find version walks a directory, recreates the tree under crop/, and pipes each file through autocrop:

sh
mkdir -p crop
find pics -type f \( -iname '*.jpg' -o -iname '*.png' \) -print0 |
  while IFS= read -r -d '' file; do
    out="crop/${file#pics/}"
    mkdir -p "$(dirname "$out")"
    autocrop "$file" > "$out"
  done

One consequence deserves attention. Each invocation loads the ONNX model and initializes the detector. For a few hundred images that is tolerable; for hundreds of thousands it is repeated fixed cost that a persistent Python process would avoid. If your batch is large, the Cropper class in a loop is the cheaper path, and the CLI is the convenience path.

There is also a video recipe. The README suggests extracting frames with ffmpeg first, then feeding the frames to autocrop. The tool never reads a video container itself. It reads still images, and the README's supported-types list is long: EPS, GIF (first frame only), JPEG 2000, JPEG, LabEye IM, ICNS, MSP, PCX, PNG, PBM/PGM/PPM, SGI, SPIDER, TGA, TIFF, WebP, BMP/DIB, ICO, and XBM. Output is narrower, limited to writable formats such as .jpg, .png, .webp, .tiff, .pdf, and .bmp.

Where autocrop is the wrong tool

Three cases stand out.

Group photos. The tool frames the biggest face. If you need every person in the frame, or a crop that keeps two faces, the README offers no option for it. The output is centered on one face by definition.

No-face images. The Python API returns None, which is a clean signal, but the CLI's stdout behavior means a batch script that redirects output can silently produce empty or invalid files. The README's batch examples do not show a check for that case. If your corpus contains landscapes, product shots, or portraits where the face is small or turned away, you need your own validation step after the crop, and the README does not describe one.

Multi-image services. A web service that crops uploads one at a time in a long-running process should use the Cropper class, not shell out per request. The CLI's one-image-per-invocation contract is a poor fit for that shape, and the README does not present it as one.

The --facePercent flag is the other place to be careful. Its help text reads "Percentage of face to image height," which tells you what it controls but not what values are sensible or what the default is. The README does not give a default or a range, so tune it against your own images rather than assuming.

How autocrop differs from a general image pipeline

The natural alternative is to call a face detector yourself and compute the crop rectangle. OpenCV ships the same YuNet model, and a few dozen lines of Python around cv2.FaceDetectorYN plus numpy slicing gets you a crop. The difference is what you inherit: model file management, the BGR/RGB conversion, the resize logic, the None-on-no-face contract, and the CLI argument parsing. autocrop packages exactly those pieces and stops there. If you need a different crop policy (padding rules, multi-face boxes, aspect-ratio targets), you are writing that code either way, and starting from the detector directly avoids fighting a fixed policy.

The other alternative is a full image-processing library with a face-crop helper, or a hosted cropping API. Those trade control for breadth: more formats, more transforms, and usually a service dependency or a heavier install. autocrop's counter-position is narrowness. One job, one model file bundled in the wheel, three dependencies, no network calls at runtime.

For batch work specifically, the shell composition is the differentiator. Because autocrop reads stdin and writes stdout, it slots into find, xargs, and parallel without a wrapper script, which is not true of libraries that only expose a Python API.

Maintenance, upgrades and licensing

The repository is not archived, and the last push was on 2026-07-15. The most recent release listed is v1.3.0 from 2022-01-25. That gap between release tags and repository activity is worth noting: fixes and CI changes may land on master without a new PyPI version, so pin the version you install and read changelog.md before upgrading.

Upgrade cost is low by construction. The public surface is one CLI, one Cropper class, and a small set of options. The main upgrade risk is the detector itself: the wheel force-includes face_detection_yunet_2023mar.onnx, so a model swap in a future release changes crop output without changing your code. If you have downstream layout that depends on exact crop rectangles, a model change is a visual regression even when the API is stable.

Licensing needs care because the metadata and the repository disagree. pyproject.toml declares license = "BSD-2-Clause" and the classifiers list "License :: OSI Approved :: BSD License", while the repository carries both a LICENSE file and a LICENSE-3RD-PARTY.txt. The GitHub metadata reports the license as NOASSERTION. The bundled ONNX model and the OpenCV dependency each carry their own terms, which is presumably what LICENSE-3RD-PARTY.txt covers. Read both files before redistributing the wheel inside a commercial product. This is a description of what the files say, not legal advice.

Editorial conclusion

Adopt autocrop if you have a directory of portraits or ID-card photos and want the crop centered on the largest detected face without writing detection code, either through the autocrop command or the Cropper class. Skip it if you need multi-face layout, video input, or a service that processes many images in one process, since the README states it deliberately handles one image per invocation. Before committing, verify two things on your own sample: what the default 500x500 resize does to your aspect ratio, and what --facePercent produces at your intended output size, because the README documents the flag's meaning only as the percentage of face to image height.

Frequently asked questions

How do I use autocrop?

Install it with pip install autocrop, then run autocrop portrait.jpg > cropped.jpg to write the cropped image to stdout, or pass -o cropped.png for an explicit output file. From Python, import Cropper, call crop() with a filepath or a numpy array, and check that the result is not None before saving.

Is autocrop free?

The package is published on PyPI and pyproject.toml declares the license as BSD-2-Clause, with a BSD classifier. The repository also includes a LICENSE-3RD-PARTY.txt covering bundled third-party components such as the YuNet ONNX model, so check both files for your use case.

How do I auto crop a picture with autocrop?

Run autocrop on the file and it outputs an image centered on the biggest detected face, 500x500 pixels by default. Add --no-resize to keep the original crop pixels, or set -w and -H to change the output size.

What is auto crop?

In autocrop's case it means detecting the biggest face with OpenCV's YuNet detector and framing the output image around it, rather than trimming borders or centering on the whole frame. The README describes the result as images centered around the biggest face detected.

Official sources

  1. Issues
  2. leblancfg/autocrop on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/leblancfg-autocrop.svg)](https://hysenlabs.com/projects/leblancfg-autocrop)