Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

removebg

Command-line tool that removes image backgrounds with rembg. Runs one image or a whole batch, locally or as a Cloud Run Job reading/writing a GCS bucket. Handles full-body subjects plus fine detail (hair, jewellery, thin props).


What runs behind the scenes

Processing pipeline (per image)

input (JPEG/PNG, e.g. 15MP)
  │
  ├─ load + convert to RGB                              (Pillow)
  ├─ downscale a COPY to --max-edge (default 2048px)    (Pillow LANCZOS)
  ├─ run the segmentation model on the small copy       (rembg → ONNX Runtime)
  │     → alpha mask (soft 0–255 matte)
  ├─ upscale mask back to the ORIGINAL resolution       (Pillow LANCZOS)
  ├─ apply mask as alpha to the full-res RGB original
  ├─ optionally flatten onto a solid background (--bg)
  └─ save (format chosen by output extension)
output (full-res PNG w/ transparency, or flattened JPG/WebP)

Why downscale-then-upscale: the model runs on the small copy (bounded memory / speed), but the mask is scaled back up and applied to the untouched original, so output keeps full resolution. Heavy models (birefnet) resize internally to ~1024px anyway, so their RAM is model-bound; the cap mainly protects lighter models on very large inputs.

Tech stack

Layer Choice Why
Language Python 3.11–3.13 onnxruntime has no 3.14 wheels yet
CLI click subcommands, typed options
Segmentation rembg[cpu]onnxruntime pretrained U²-Net / IS-Net / BiRefNet ONNX models, CPU inference
Imaging Pillow load, resize, mask apply, encode
Batch concurrent.futures in-process (1 worker) or process pool (N workers)
Container python:3.11-slim + Docker model pre-baked into the image
Cloud Cloud Run Jobs + GCS via gcsfuse one-shot batch, no HTTP server needed

Models (--model)

model peak RAM speed¹ notes
birefnet-general (default) ~4.6 GB ~45s best full-body + fine detail; MIT; commercial-safe
u2net_human_seg ~0.8 GB ~6s light, good body integrity, weaker on thin props
isnet-general-use ~1.2 GB ~10s light, good detail, tends to crop full-body poses
u2netp small fastest low quality; quick previews
silueta small fast generic silhouette
birefnet-portrait ~4.6 GB ~45s head/shoulders framing, not full body

¹ single 15MP image, 4 CPU threads. Not bundled: bria-rmbg — excellent for commercial cutouts but CC BY-NC-SA (non-commercial) unless you license it from BRIA.


Install (local)

python3.13 -m venv .venv
.venv/bin/pip install -e ".[dev]"

pip install -e (editable install) generates src/removebg.egg-info/ — metadata pip uses to resolve the package (name, version, entry points, dependency list) without copying files into site-packages. It's a build artifact, safe to delete/regenerate (rm -rf src/removebg.egg-info && pip install -e .), and belongs in .gitignore, not version control.

Usage

# single image — output format follows the -o extension
removebg single photo.jpg -o cutout.png                 # transparent PNG
removebg single photo.jpg -o cutout.jpg --bg white      # flattened white-bg JPG

# batch a folder
removebg batch ./in -o ./out --workers 4 --recursive
removebg batch ./in -o ./out --ext .jpg --bg white      # all outputs as white-bg JPG

Key options (both commands):

  • --model — see table above (default birefnet-general).
  • --max-edge N — cap long edge fed to the model (default 2048; 0 = full-res inference).
  • --bgtransparent (default), white, black, or #RRGGBB. JPG/BMP can't hold transparency, so they're auto-flattened onto white if --bg is unset — use --bg to pick a colour. This is the fix if a downstream app "can't open" a transparent PNG.
  • --alpha-matting — extra edge refinement for hair/filaments (slower).

Batch concurrency: --workers 1 runs in-process (one model copy, clean errors). Each extra worker loads its own copy of the model, so keep workers × model_RAM ≤ RAM. For birefnet (~4.6 GB/worker) stay at 1 unless you have lots of memory.


Cloud Run Jobs

Configuration

The commands below use shell variables so nothing environment-specific is hard-coded. Set them once for your own GCP setup:

export GCP_PROJECT_ID=your-project-id
export GCP_PROJECT_NUMBER=000000000000     # gcloud projects describe $GCP_PROJECT_ID --format='value(projectNumber)'
export REGION=us-central1
export BUCKET=your-bucket-name              # inputs in in/, outputs in out/
export IMAGE=${REGION}-docker.pkg.dev/${GCP_PROJECT_ID}/removebg/removebg

Architecture

you ──gcloud storage cp──▶ gs://${BUCKET}/in/*.jpg
                                   │
        gcloud run jobs execute ──▶ removebg-job (Cloud Run Job)
                                   │   gcsfuse mounts gs://${BUCKET} at /data
                                   │   removebg batch /data/in -o /data/out
                                   ▼
you ◀─gcloud storage cp── gs://${BUCKET}/out/*.png

Deploy

# Artifact Registry repo (legacy gcr.io createOnPush is disabled)
gcloud artifacts repositories create removebg \
  --repository-format=docker --location=${REGION}

# build + push (Dockerfile pre-bakes the birefnet model → no cold-start download)
gcloud builds submit --tag ${IMAGE}

# bucket
gcloud storage buckets create gs://${BUCKET} --location=${REGION}

# grant the job's service account bucket access (else the gcsfuse mount fails)
gcloud storage buckets add-iam-policy-binding gs://${BUCKET} \
  --member="serviceAccount:${GCP_PROJECT_NUMBER}-compute@developer.gserviceaccount.com" \
  --role="roles/storage.objectAdmin"

# job — bucket mounted at /data via gcsfuse (no code change needed)
gcloud run jobs create removebg-job \
  --image ${IMAGE} \
  --region ${REGION} \
  --memory 16Gi --cpu 4 \
  --execution-environment gen2 \
  --task-timeout 3600 --max-retries 1 \
  --set-env-vars OMP_NUM_THREADS=4 \
  --add-volume name=data,type=cloud-storage,bucket=${BUCKET} \
  --add-volume-mount volume=data,mount-path=/data \
  --args="batch,/data/in,-o,/data/out,--workers=1"

The job runs as the project's default compute service account (${GCP_PROJECT_NUMBER}-compute@developer.gserviceaccount.com) unless you pass --service-account. Whichever account it uses needs roles/storage.objectAdmin on the bucket (granted above).

Run

# 1. upload inputs
gcloud storage cp ./myphotos/* gs://${BUCKET}/in/

# 2. run
gcloud run jobs execute removebg-job --region ${REGION} --wait

# 3. download results
gcloud storage cp -r gs://${BUCKET}/out/* ./results/

Rebuild after code changes

gcloud builds submit --tag ${IMAGE}
# jobs pick up :latest on the next execution; no re-create needed

Gotchas (learned the hard way)

  • --execution-environment gen2 is required for gcsfuse volume mounts.
  • The job's service account needs storage.objectAdmin on the bucket, or the mount fails with a storage.objects.list PermissionDenied and the container exits 255 before doing any work.
  • OMP_NUM_THREADS=4 (match --cpu): birefnet peaks lower on RAM at 4 threads than at 1, and runs ~40% faster.
  • Memory is model-bound. birefnet needs ~5 GB resident regardless of input size; 8Gi is too tight once Linux ONNX Runtime + gcsfuse + the process overhead stack up → OOM, which surfaces as an opaque BrokenProcessPool. 16Gi has margin (can trim to ~12Gi).

Trace a failed execution

gcloud logging read \
  'resource.type="cloud_run_job" AND resource.labels.job_name="removebg-job"' \
  --project=${GCP_PROJECT_ID} --limit=100 --order=asc --format="value(textPayload)" \
  | grep -iE "error|traceback|permission denied|killed"

Project layout

removebg/
├── src/removebg/
│   ├── cli.py       # click commands: single, batch
│   ├── core.py      # per-image pipeline: downscale → infer → mask → flatten → save
│   ├── batch.py     # folder walk, in-process or ProcessPool fan-out
│   └── models.py    # model registry + cached ONNX sessions
├── tests/           # pytest
├── Dockerfile       # slim image, model pre-baked, OMP_NUM_THREADS=4
└── pyproject.toml

Tests

.venv/bin/pytest -q

About

Python CLI for batch image background removal using local AI models, with Google Cloud Run integration for running heavy models

Topics

Resources

Stars

Watchers

Forks

Contributors

Languages