Skip to main content

📷 PhotoS — Batch Image Compression & Format Conversion

Python Platform License Tests PyPI

PhotoS is a cross-platform batch image processing tool with both GUI and CLI. Built for photographers who need to deliver images at specific sizes, and for AI agents that need reliable image processing pipelines.

🖥 GUI for humans — ⌨️ CLI for AI agents — pip install photo-s-tools

✍️ Core developers: deepseek-v4-flash · GLM-5.2 · Kimi K3

English · 中文


✨ Features

Feature GUI CLI Description
Batch compress JPEG/WebP/HEIC/AVIF quality tuning
Target size mode Auto-tune quality to fit under a target file size
Format convert JPEG / PNG / WebP / TIFF / BMP / HEIC / AVIF
RAW decode 22+ camera RAW formats, built-in (rawpy/libraw)
Resize / Scale Max dimensions, percentage, or longest-side cap
Visual preview Live original↔processed preview rendered through the real pipeline
Tone & color Brightness/contrast/saturation/gamma/sharpen, B&W, sepia
White balance --wb 5600 Kelvin, or --wb-from ref.jpg sample a gray card
Exposure --ev +1 stops, or --auto-exposure 0.45 normalize to target
Auto levels --auto-levels 2% clip histogram stretch
LOG recovery --log-curve SLOG3/CLOG3/LOGC3/DLOG/VLOG/HLG (1D LUT, no deps)
LUT grading --lut film.cube or preset names (built-in trilinear; photo-s-plugin-lut adds tetrahedral + 5 film presets)
Denoise ✅¹ --denoise 10 NLM ([enhance] extra)
Auto-straighten ✅¹ --auto-straighten level the horizon, confidence-gated ([enhance] extra)
Crop / Rotate / Flip / Pad --crop 800x600+0+0, --rotate 90, --flip h, --pad 16:9
Print size --print-size 8x10@300dpi center-crop + exact print pixels
Smart rename {date}_{camera}_{seq} templates
Auto folder organize --organize date-camera subfolder creation
Watermark Text + image overlay, 7 positions
Multi-size output --sizes thumb:480x,screen:1920x
Metadata tagging exif --rating / --keywords / --caption batch tag (UserComment)
Metadata filter exif --show --rating-min 3 --keywords beach find tagged photos
Metadata import exif --from-csv meta.csv batch write from spreadsheet
Culling photo-s cull exposure/sharpness filter (GUI keeps only matches, undoable)
Burst keep-sharpest dedup --action keep-sharpest pick the sharpest of a burst
Checksum manifest photo-s hash SHA-256 archive integrity + --verify
HTML gallery photo-s gallery self-contained index.html + thumbs
Presets Save/load named configs
Multi-profile batch --profiles web,thumb one input set, N outputs
Parallel processing -j 8 multi-threaded
JSON output --json for AI agent consumption
Config file photo-s.toml defaults (config init/show)
EXIF edit photo-s exif *.jpg --artist "Me"
EXIF date shift --date-shift "-5h30m" timezone/camera clock fixes
Privacy scrub --scrub strips EXIF+ICC+GPS
Sync date --sync-date output mtime ← EXIF datetime
Folder watch photo-s watch ~/incoming/ auto-process ([watch] extra)
Auto-rotate EXIF Orientation-based
Image dedup Perceptual hash duplicate detection
Quality metrics --evaluate SSIM + --blur-score
CSV report --report out.csv per-file stats
Integrity check photo-s check corrupt file scan
Contact sheet photo-s contact-sheet *.jpg -o sheet.png
Color management --srgb / --flatten-cmyk
REST API photo-s serve for AI agents
Plugin system Third-party plugin support
Official plugin manager photo-s plugin list/install/info/fetch + pip install photo-s-plugin-scunet
MCP server photo-s mcp expose 11 tools to MCP clients (Claude Desktop)
Batch benchmark photo-s bench --dir ~/shoot -j 1,2,4,8 measure worker scaling

¹ Denoise / auto-straighten need an optional dependency: pip install photo-s-tools[enhance] (opencv-python-headless). When missing, these features give a clear install hint and the rest keeps working.


📦 Installation

pip install (recommended)

pip install photo-s-tools

# With optional features
pip install photo-s-tools[all]       # everything
pip install photo-s-tools[heic]      # HEIC support
pip install photo-s-tools[avif]      # AVIF support
pip install photo-s-tools[watch]     # folder watching
pip install photo-s-tools[exif]      # EXIF editing
pip install photo-s-tools[enhance]   # NLM denoise + auto-straighten (opencv)
pip install photo-s-tools[mcp]       # MCP server (Python 3.10+)

From source

git clone https://github.com/Dongwu259/photo_s.git
cd photo_s
pip install -e .

⌨️ CLI Usage

photo-s --help                  # Show all commands
photo-s compress *.jpg -q 80    # Batch compress
photo-s convert *.png -f webp   # Convert format
photo-s batch ~/photos/ -r      # Recursive batch
photo-s exif *.jpg --artist "Me" # Edit EXIF
photo-s preset save web -q 70   # Save preset
photo-s preset list             # List presets
photo-s watch ~/incoming/       # Auto-process new files
photo-s dedup ~/photos/         # Find duplicates
photo-s info                    # Supported formats
photo-s --version               # Show version

Photographer workflows

# Cull: find over/under-exposed shots
photo-s cull ~/shoot/ -r --overexposed-max 2% --underexposed-max 2% --list

# Tag + filter by tags (core workflow)
photo-s exif ~/shoot/ -r --rating 4 --keywords "keep,beach"   # batch tag
photo-s exif ~/shoot/ -r --show --rating-min 4 --list         # pick >=4-star paths
photo-s exif ~/shoot/ -r --show --keywords beach --json        # filter by keyword
photo-s exif --from-csv meta.csv                               # batch write from CSV
photo-s batch $(photo-s exif ~/shoot/ -r --show --rating-min 4 --list) -o /deliver/

# Archive: generate + verify a SHA-256 manifest
photo-s hash ~/archive/ -r -o manifest.csv
photo-s hash --verify manifest.csv

# Burst selection: keep the sharpest of each group
photo-s dedup ~/burst/ --action keep-sharpest --dry-run

# Delivery: HTML gallery / print size / white balance
photo-s gallery ~/shoot/ -o gallery/ --title "2026 Sichuan"
photo-s batch ~/shoot/ --print-size 8x10@300dpi
photo-s batch ~/shoot/ --wb 5600 --auto-levels

# Global correction: exposure / LOG recovery / denoise / straighten
photo-s batch ~/shoot/ --ev +0.5 --auto-exposure 0.45
photo-s batch ~/log/    --log-curve SLOG3 --wb 5600        # LOG footage recovery
photo-s batch ~/highiso/ --denoise 12 --ev -0.3            # high-ISO denoise
photo-s batch ~/tilted/ --auto-straighten --max-straighten-angle 8

Common examples

# Compress to ~5MB with auto-tune, 8 threads, JSON output (AI agent)
photo-s compress *.jpg --target-size 5MB -j 8 --json

# Convert to AVIF with parallel workers
photo-s convert *.jpg -f AVIF -q 60 -j 4

# Organize by date+camera, add a watermark
photo-s batch ~/photos/ --organize date-camera --watermark-text "© Me" -j 4

# Smart rename with EXIF metadata
photo-s compress *.jpg --rename "{date}_{camera}_{seq}"

# Find duplicate images
photo-s dedup ~/photos/ --action report

JSON output (for AI agents)

--json prints pure JSON to stdout (progress/diagnostics go to stderr). All agent-facing subcommands support it: compress/batch/convert (batch results), check/dedup (reports), rename, contact-sheet, info, and --dry-run (config preview).

{
  "summary": {"total": 5, "success": 5, "failed": 0, "saved_bytes": 27262976, "saved_percent": 52.0},
  "results": [{"input": "photo.jpg", "output": "photo_compressed.jpg", "input_size": 10485760, "output_size": 5242880, "format": "JPEG", "dimensions": [6000, 4000], "quality": 78, "status": "ok"}]
}

Use with any AI agent: photo-s compress *.jpg --json --target-size 5MB | your-agent

Exit-code convention: failures in batch/rename/check → 1; dedup returns 1 when duplicates are found (0 otherwise), so agents can branch on it. Under --json, --remove-original / dedup --action move|delete skip interactive confirmation (an explicit agent request is taken as consent).


🖥 GUI Usage

photo-s          # Launch GUI (no args = GUI)
photo-s gui      # Explicit GUI mode

GUI features: Chinese/English language switch, drag-and-drop (needs pip install photo-s-tools[gui]), cancellable batch processing, before/after comparison, global shortcuts (⌘/Ctrl+O add, ⌘/Ctrl+R start, Esc cancel, ⌘/Ctrl+E review, ⌘/Ctrl+D dedup, ⌘/Ctrl+G gallery, ⌘/Ctrl+Z undo), a checkbox file list (every row has a real checkbox; all actions — process, review, dedup, gallery — run on the checked files; adding a folder scans subfolders), a review & rate lightbox (←/→ navigation, 0-5 stars, keywords/title, rating & keyword filters — writes EXIF), a duplicate viewer (side-by-side groups with sharpness scores, keep-checkboxes, move-to-trash instead of delete), and HTML gallery export. Tagged photos can then be filtered in the CLI (photo-s exif --rating-min 4 --list) or used by AI agents.

GUI changes & interface contract: docs/GUI_CHANGES.md

Screenshots

  • Main window with file list + settings panel
  • Processing progress bar and summary dialog
  • Before/after comparison view

🎯 Target Size Mode

Unique feature: set a target file size and PhotoS auto-tunes JPEG/WebP/AVIF quality via binary search.

photo-s compress *.jpg --target-size 5MB
# Auto-tunes quality ∈ [5, 85] to make each output ≤ 5MB

🔌 Plugin System

Third-party plugins extend PhotoS via Python entry_points.

pip install photo-s-plugin-s3    # Example: auto-upload to S3
photo-s compress *.jpg           # plugins auto-apply

Official plugins

Official plugins are separate PyPI distributions photo-s-plugin-<name>. Install via either the plugin manager or pip. The first official plugin is SCUNet strong denoise — stronger high-ISO denoising than the built-in NLM. Once installed, --denoise N prefers it automatically (and falls back to NLM otherwise):

# Channel 1: plugin manager (agent-friendly, --json)
photo-s plugin list
photo-s plugin install scunet --json
photo-s plugin fetch scunet          # pre-download the ONNX weights (~10-40MB, sha256-verified)
photo-s plugin info scunet

# Channel 2: plain pip
pip install photo-s-plugin-scunet

# Usage (auto uses SCUNet when installed, else NLM)
photo-s batch ~/highiso/ --denoise 12

Model weights are not shipped in the wheel: downloaded on first use to ~/.cache/photo-s/models/ (override with $PHOTOS_CACHE_DIR), sha256-verified. All official plugins follow the "separate distribution + external weights" model.

Writing a plugin

# setup.py / pyproject.toml
[project.entry-points."photo_s.plugins"]
my-plugin = "my_package:MyPlugin"

# my_package.py
from photo_s.hooks import PhotoSPlugin

class MyPlugin(PhotoSPlugin):
    name = "my-plugin"

    def on_post_process(self, result, ctx):
        print(f"Processed: {result.output_path}")

See docs/PLUGINS.md for the full API, including operation providers (e.g. a denoise slot provider) and model-weight handling.


📋 Supported Formats

Format Read Write Notes
JPEG quality, progressive, EXIF
PNG optimize
WebP quality
AVIF quality (requires pillow-avif-plugin)
HEIC requires pillow-heif
TIFF LZW compression
BMP
ICO
RAW (22+ formats) built-in via rawpy (libraw)

✏️ Naming Convention

Context Form Notes
Python package / import photo_s syntax-enforced: import photo-s is invalid
CLI command photo-s shell convention: photo-s compress *.jpg
PyPI distribution photo-s-tools pip install photo-s-tools (the obvious photo-s is blocked by PyPI — too similar to the existing photos package)
UI title / brand / doc headings PhotoS human-readable brand name

Don't mix forms within the same context (e.g. photo_s compress in code examples, or photo-s in UI copy, are both wrong). This is the standard Python-ecosystem pattern (scikit-learn→sklearn, Pillow→PIL); please don't "unify" them.


🤖 Agent / Application Integration

The complete integration contract (CLI JSON shapes, exit codes, serve endpoints, async tasks, config precedence) lives in docs/AGENT_API.md — agents only need that one doc.

PhotoS offers three integration paths, by recommendation:

1. Python library (recommended when the host is Python)

from photo_s.engine import ProcessOptions, batch_process

options = ProcessOptions(
    output_dir="compressed/",
    quality=70,
    max_pixels=8000,
    strip_gps=True,          # privacy
    evaluate=True,           # SSIM
)
result = batch_process(["/path/a.jpg", "/path/b.jpg"], options, jobs=4)
for r in result.results:
    print(r.output_path, r.ssim)

No IPC overhead; just vendor the photo_s package into your app.

2. REST API (photo-s serve — non-Python host / cross-process)

photo-s serve --port 0 --token auto --ready-file ./photo-s.ready.json
  • --port 0 = random free port; --token auto = random token; --ready-file atomically writes {"port", "token", "pid"} after listening starts — the host agent polls that file (more reliable than parsing stdout, also on Windows), then: GET /health readiness probe → POST /process {"paths": [...], "options": {...}} → get BatchResult JSON (with ssim / blur_score).

Long batches / progress / cancel (POST /process with "async": true):

# 1. Submit an async task
curl -X POST .../process -H "Authorization: Bearer $TOKEN" \
     -d '{"paths": ["/photos/*.jpg"], "async": true}'
# → 202 {"task_id": "...", "poll": "/tasks/<id>", ...}

# 2. Poll progress
curl .../tasks/<id>
# → {"status": "running|done|cancelled|error", "current": N, "total": M,
#     "current_path": "...", "result": {BatchResult JSON when done}}

# 3. Cancel (queued files stop after the in-flight one finishes)
curl -X POST .../tasks/<id>/cancel

POST /process also supports "dry_run": true (returns the paths/options that would be processed, no work done) and options.output_sizes (multi-size, [["thumb",480,None], ...]) and options.pad (= pad_ratio).

  • On Windows without a Python env: use PyInstaller to bundle photo-s.exe (see below), spawn it by absolute path — no PATH dependency.
  • The host manages the process lifecycle (terminate the child on exit).

3. CLI subprocess (one-off scripts / CI)

photo-s compress a.jpg -q 80 --json → stdout JSON. Each call has a Python interpreter startup cost (~200-300ms); not recommended for high-frequency batch.

4. MCP server (Claude Desktop & MCP clients)

Model Context Protocol server — lets Claude Desktop / any MCP client call PhotoS tools directly (needs Python 3.10+ and the optional extra):

pip install "photo-s-tools[mcp]"
photo-s mcp --list-tools        # inspect the 11 tools + schemas (JSON)
photo-s mcp                     # start the stdio MCP server

Tools: process (batch quality/format/resize/tone/denoise), info (environment probe), exif (read/filter/write metadata), dedup (perceptual-hash groups, keep-sharpest), cull (exposure/sharpness filter), hash (SHA-256 manifests), contact_sheet (grid montage), gallery (HTML gallery), watermark (text/image overlay), preset (list/save/load/delete), plugin (official plugin management). Output shapes mirror the CLI --json contracts.

Claude Desktop config (claude_desktop_config.json):

{
  "mcpServers": {
    "photo-s": {
      "command": "photo-s",
      "args": ["mcp"]
    }
  }
}

Zero-install variant (uvx resolves PyPI deps on first run — the same invocation published on the official MCP Registry as io.github.Dongwu259/photo-s):

{
  "mcpServers": {
    "photo-s": {
      "command": "uvx",
      "args": ["--from", "photo-s-tools[mcp]", "photo-s", "mcp"]
    }
  }
}

Destructive safety: dedup keep-sharpest defaults to dry_run=True (deletion requires an explicit dry_run=False). process never overwrites inputs.

Windows packaging (no Python/PATH env)

pip install pyinstaller piexif pillow-heif   # optional features too
python packaging/build.py                    # full: dist/photo-s/photo-s.exe (GUI+CLI+MCP)
python packaging/build.py --lite             # lite: dist/photo-s-lite/photo-s-lite.exe (CLI+MCP, no GUI)

Two editions: the full bundle ships the GUI; the lite bundle excludes photo_s.gui + tkinter at build level (smaller, display-free) — ideal for agent-spawned serve/mcp processes. In the lite build photo-s-lite with no args prints help, gui exits 1 with a hint, and --version shows (lite).

The host launches by absolute path, no environment variables needed (see the spawn mode above). CI builds both windows-latest artifacts (.github/workflows/ci.yml).


🧪 Development

pip install -e ".[dev]"
python -m pytest tests/ -v

📄 License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

photo_s_tools-1.4.2.tar.gz (268.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

photo_s_tools-1.4.2-py3-none-any.whl (191.3 kB view details)

Uploaded Python 3

File details

Details for the file photo_s_tools-1.4.2.tar.gz.

File metadata

  • Download URL: photo_s_tools-1.4.2.tar.gz
  • Upload date:
  • Size: 268.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for photo_s_tools-1.4.2.tar.gz
Algorithm Hash digest
SHA256 f18614811d1071353a798f20b25bcf87bf60aa567af50e4dbd46565af0b36a15
MD5 fa4817bfd85f482d9c2454580accd9ae
BLAKE2b-256 1b867dc986c266baf3ffa5a330bbca4268860704082e6557999bb22c70ffd889

See more details on using hashes here.

Provenance

The following attestation bundles were made for photo_s_tools-1.4.2.tar.gz:

Publisher: publish.yml on Dongwu259/photo_s

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file photo_s_tools-1.4.2-py3-none-any.whl.

File metadata

  • Download URL: photo_s_tools-1.4.2-py3-none-any.whl
  • Upload date:
  • Size: 191.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for photo_s_tools-1.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 46900abaaaaa1587c7df4a2c15c56f2821838a177ec7ce714380a7b02f817ce5
MD5 7cddd39db7dcff7ed4bb03fb09b95911
BLAKE2b-256 1023f84f1eb82fc5d4b93bcf9feb2bd5961d1acb32ed02bf4aafcf38f698f632

See more details on using hashes here.

Provenance

The following attestation bundles were made for photo_s_tools-1.4.2-py3-none-any.whl:

Publisher: publish.yml on Dongwu259/photo_s

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page