The short version: you don't need Photoshop, remove.bg credits, or a subscription to cleanly cut a subject out of a photo. A free, open-source Python library called rembg does it locally on your machine in a couple of seconds — and once it's a script, Claude Code can run it for you every time you drop new photos in a folder. No more "let me just quickly cut this out" derailing twenty minutes of your afternoon.

This post gives you the actual script (copy-paste or download it), walks through setup start to finish, and shows a real run — not a mockup — end to end.

Why bother scripting this?

If you only ever need to cut out one photo a year, sure, use an online tool. But the moment you're doing this repeatedly — product shots for a shop, headshots for a team page, thumbnails for a blog — the "upload, wait, download, repeat" loop gets old fast, and most of the free online tools throttle you, watermark you, or want a subscription past a handful of images.

A local script fixes all three problems at once: it's free forever, it never uploads your photos anywhere, and it can process an entire folder in the time it takes one image to finish on a web tool.

The tool doing the actual work: rembg

rembg is an open-source (MIT-licensed — genuinely free, no strings) Python library built on U²-Net, a neural network trained specifically to separate a subject from its background. The first time you run it, it downloads a small model file (~170MB, one-time, then cached forever); after that it works completely offline.

The script

Save this as remove_bg.py. Point it at one image or a whole folder — it saves transparent PNGs either way.

#!/usr/bin/env python3
"""
remove_bg.py — batch background removal, free and offline after first run.

Point it at one image or a whole folder; it saves transparent PNGs next to
(or into) an output folder. Uses rembg (U^2-Net under the hood) — MIT
licensed, no API key, no per-image cost, no upload to a third-party service.

Usage:
    python remove_bg.py photo.jpg
    python remove_bg.py ./product-shots/ -o ./product-shots/no-bg
    python remove_bg.py ./product-shots/ --model isnet-general-use
"""
import argparse
import sys
from pathlib import Path

IMAGE_EXTENSIONS = {".png", ".jpg", ".jpeg", ".webp", ".bmp"}


def main():
    parser = argparse.ArgumentParser(description=__doc__.strip().splitlines()[0])
    parser.add_argument("input", help="Image file or folder of images")
    parser.add_argument("-o", "--output", default="no-bg", help="Output folder (default: ./no-bg)")
    parser.add_argument(
        "--model",
        default="u2net",
        help="rembg model: u2net (general, default), u2net_human_seg (people), "
        "isnet-general-use (sharper edges, slower)",
    )
    args = parser.parse_args()

    try:
        from rembg import remove, new_session
        from PIL import Image
    except ImportError:
        print("Missing dependencies. Run: pip install rembg pillow onnxruntime")
        sys.exit(1)

    input_path = Path(args.input)
    if not input_path.exists():
        print(f"Not found: {input_path}")
        sys.exit(1)

    output_dir = Path(args.output)
    output_dir.mkdir(parents=True, exist_ok=True)

    if input_path.is_dir():
        images = sorted(p for p in input_path.iterdir() if p.suffix.lower() in IMAGE_EXTENSIONS)
    else:
        images = [input_path]

    if not images:
        print(f"No images found in {input_path}")
        sys.exit(1)

    print(f"Loading model '{args.model}' (first run downloads it, then it's cached)...")
    session = new_session(args.model)
    print(f"Removing backgrounds from {len(images)} image(s)...\n")

    for i, img_path in enumerate(images, 1):
        out_path = output_dir / f"{img_path.stem}.png"
        print(f"  [{i}/{len(images)}] {img_path.name} -> {out_path}", end="  ", flush=True)
        with Image.open(img_path) as img:
            result = remove(img, session=session)
            result.save(out_path)
        print("done")

    print(f"\nAll set — {len(images)} transparent PNG(s) saved to {output_dir}/")


if __name__ == "__main__":
    main()

Download remove_bg.py

Setup, step by step

1. Install Python (if you don't already have it)

macOS and most Linux distros already have Python 3. Check with:

python3 --version

If that fails, grab it from python.org (Windows/Linux) or brew install python3 (macOS).

2. Create a virtual environment (recommended, not required)

Keeps rembg's dependencies out of your system Python. Optional, but tidy:

python3 -m venv venv
source venv/bin/activate   # Windows: venv\Scripts\activate

3. Install the three dependencies

pip install rembg pillow onnxruntime

Heads up: some environments install rembg without pulling in onnxruntime automatically — if you get ModuleNotFoundError: No module named 'onnxruntime', just pip install onnxruntime on its own and you're set.

4. Save the script and run it

python remove_bg.py your-photo.jpg

Or point it at an entire folder:

python remove_bg.py ./product-shots/ -o ./product-shots/no-bg

Watch it actually run

This isn't a mockup — it's the real command and the real output from running the script above on a sample image, replayed as an animation:

remove_bg.py — zsh

The real result

Before / after — genuinely produced by the script above, not staged in an image editor:

Before and after background removal — a mug on a solid background, then the same mug with a fully transparent background

And because it's actually transparent (not just "white background"), it drops cleanly onto literally anything:

The same cutout mug placed onto a transparency grid, a solid pink background, and a gradient background — proving genuine transparency

Make Claude Code run this for you

Here's the part that actually saves time long-term: instead of remembering the command every time, wire it up as a Claude Code custom slash command. Drop this file into any project:

.claude/commands/remove-bg.md
---
description: Remove the background from an image or folder of images
---

Run `python remove_bg.py $ARGUMENTS` in the project root and report the
output folder and how many images were processed. If rembg, pillow, or
onnxruntime aren't installed, install them first with pip, then re-run.

Now, from inside a Claude Code session in that project, you just type:

/remove-bg ./photos/

...and Claude Code runs the script, handles a missing dependency if it hits one, and tells you where the results landed — no context-switching to a terminal, no re-explaining what you want every time. That's the whole point: turn a five-step manual chore into one command you never have to think about twice.

A few things worth knowing

Photos of people?

Swap the model: --model u2net_human_seg. It's trained specifically on human subjects and handles hair/edges noticeably better than the general model.

Edges look rough?

Try --model isnet-general-use — sharper edge detection, at the cost of running a bit slower per image.

First run is slow

That's the one-time ~170MB model download. Every run after that is fast and fully offline — nothing leaves your machine.

Always PNG out

JPEGs can't store transparency. The script always saves results as .png regardless of your input format — that's not a bug.