An open API service indexing awesome lists of open source software.

https://github.com/derekslinz/photoram

Modern CLI photo tagger powered by RAM++. Command-line image tagger that uses RAM++ (Recognize Anything Model) to generate high-quality English tags with optional confidence scores, batch inference, and metadata writing (EXIF/XMP/IPTC). Stable JSON output contract (always a list), flexible device selection (cpu/cuda/mps) and darktable script.
https://github.com/derekslinz/photoram

computer-vision darktable darktable-plugin imagetagging

Last synced: 26 days ago
JSON representation

Modern CLI photo tagger powered by RAM++. Command-line image tagger that uses RAM++ (Recognize Anything Model) to generate high-quality English tags with optional confidence scores, batch inference, and metadata writing (EXIF/XMP/IPTC). Stable JSON output contract (always a list), flexible device selection (cpu/cuda/mps) and darktable script.

Awesome Lists containing this project

README

          

# photoram

`photoram` is a CLI photo classifier powered by ImageNet-21K. It is designed as a modern, scriptable successor to [photils-cli](https://github.com/scheckmedia/photils-cli). The installed command is `photoram-cli`.

## Highlights

- ImageNet-21K classification with 21k-class label space.
- Offline inference after first-run model download.
- Batch inference (`--batch-size`) with streaming mini-batch loading.
- Stable JSON output contract: `--format json` always returns a list.
- Standardized exit codes and validation errors.
- Service-layer architecture (`TaggingService`) that decouples CLI from model internals.
- photils compatibility flags: `--image`, `--output_file`, `--with_confidence`.

## Security Hardening

- Deterministic model selection: pinned default model id (`vit_base_patch16_224.augreg_in21k`).
- Image safety checks: decompression-bomb protection is enabled (`PHOTORAM_MAX_IMAGE_PIXELS`, default `120000000`).
- Metadata subprocess hardening: exiftool invocation uses `--` before image path to prevent option parsing.
- Streaming batch loader: avoids preloading all tensors for large jobs.
- CI security gates: `bandit` (SAST) and `pip-audit` (dependency CVEs).

## Requirements

- Python 3.9+
- `pip`
- `git` (for source installs)

## Installation

### Local editable install

```bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
pip install -e .
photoram-cli --help
```

### Reproducible install (constraints)

```bash
pip install -c constraints.txt -e .
```

### Optional extras

```bash
# Metadata fallback support (pyexiv2)
pip install -e ".[metadata]"

# Dev + test + security tooling
pip install -c constraints.txt -e ".[dev]"
```

If you use `--write-metadata`, installing `exiftool` is recommended:

```bash
# macOS
brew install exiftool

# Debian/Ubuntu
sudo apt install libimage-exiftool-perl
```

## Quick Start

```bash
# Tag one image
photoram-cli tag photo.jpg

# Include confidence scores
photoram-cli tag photo.jpg --confidence

# Recursive directory tagging
photoram-cli tag ./photos --recursive

# Batch inference (faster on GPU, higher VRAM use)
photoram-cli tag ./photos --recursive --batch-size 32

# Write metadata
photoram-cli tag photo.jpg --write-metadata

# JSON output (always a list)
photoram-cli tag photo.jpg --format json

# Print timing summary (model load, tagging, total)
photoram-cli tag photo.jpg -T

# Show environment/model cache info
photoram-cli info
```

Note: first run requires internet access to download the ImageNet-21K model into the Hugging Face cache.

## Commands

### `photoram-cli tag`

```text
photoram-cli tag [OPTIONS] INPUT...

Arguments:
INPUT Image file(s) or directory to tag

Options:
-t, --threshold FLOAT Minimum class probability 0.0-1.0 (default: 0.0)
-n, --top-n INTEGER Maximum number of tags to return
-c, --confidence Show confidence scores
-f, --format FORMAT Output format: text, json, csv (default: json)
-o, --output FILE Write results to file
-r, --recursive Recursively scan directories
-w, --write-metadata Write tags to image EXIF/XMP/IPTC metadata
--overrides FILE Tag override/translation JSON file
--batch-size INT Images per inference batch (default: 16)
-T, --timings Print basic timings (load, tagging, total)
-q, --quiet Suppress progress output
-h, --help Show help
```

Compatibility alias for photils-dt-style calls:

```bash
photoram-cli tag --image "$FILE"
```

### `photoram-cli info`

Prints package version, torch/runtime capability, resolved default device, and model cache information.

## darktable Lua Plugin

A multi-image darktable plugin is included at:

- `darktable/photoram.lua`

What it does:

- Works on all selected images in one action (not single-image only).
- Exposes tunable parameters in darktable preferences:
- `photoram: executable` -> command/path to `photoram-cli`
- `photoram: max tags` -> `--top-n`
- `photoram: threshold (%)` -> `--threshold`
- `photoram: batch size` -> `--batch-size`
- `photoram: write metadata` -> `--write-metadata`
- `photoram: quiet output` -> `--quiet`
- `photoram: show timings` -> `--timings`
- `photoram: device` -> `--device`
- `photoram: overrides file` -> `--overrides`

Install:

```bash
mkdir -p ~/.config/darktable/lua
cp darktable/photoram.lua ~/.config/darktable/lua/photoram.lua
echo 'require "photoram"' >> ~/.config/darktable/luarc
```

Then restart darktable and run the selected-images action:

- `photoram auto-tag`

The plugin also registers a visible panel module:

- Name: `photoram auto-tagger`
- Location: right panel in lighttable (or left panel in darkroom)
- Button: `auto-tag selected`

Troubleshooting:

1. Check darktable Lua logs for load errors:
- Linux/macOS: start darktable from terminal and inspect stderr output.
2. Ensure the panel is enabled in darktable module visibility settings.
3. Confirm plugin is in the exact path:
- `~/.config/darktable/lua/photoram.lua`
4. Confirm `luarc` contains:
- `require "photoram"`
5. If `photoram-cli` is not on PATH, set preference:
- `photoram: executable` to full binary path (for example `/usr/local/bin/photoram-cli`).

## Output Contract

### JSON (`--format json`)

Always returns a list, even for one image.

```json
[
{
"file": "photo.jpg",
"tags": ["tree", "sky"]
}
]
```

- Add `--confidence` to include `confidences`.
- Failed files include an `error` field.

### Text (`--format text`)

- One image: `tag1 | tag2 | tag3`
- Multiple images: `\t` per line

### CSV (`--format csv`)

- Base columns: `file`, `tags`
- Optional columns: `confidences`

## Exit Codes

| Code | Meaning |
|------|---------|
| 0 | Success |
| 1 | No images found |
| 2 | Invalid arguments |
| 3 | Model/download/load error |
| 4 | Other runtime error |

## Tag Overrides

Create `override_labels.json`:

```json
{
"blackbackground": "black background",
"art": "kunst",
"shadow": "schatten"
}
```

Lookup order:

- Explicit `--overrides `
- `~/.config/photoram/override_labels.json`

## Architecture

```text
cli.py -> service.py -> model.py
\-> utils.py
\-> schemas.py
\-> errors.py
\-> metadata.py
```

- `src/photoram/cli.py`: Click commands, progress, output and exit handling.
- `src/photoram/service.py`: orchestration layer (collection, dispatch, post-processing).
- `src/photoram/model.py`: ImageNet-21K model loading, preprocessing, inference, safety checks.
- `src/photoram/schemas.py`: result schemas (`TagResult`, `BatchResult`).
- `src/photoram/errors.py`: exception hierarchy and exit codes.
- `src/photoram/metadata.py`: metadata writing adapters.
- `src/photoram/utils.py`: file discovery, overrides, format helpers.

## Development and Tests

```bash
pip install -c constraints.txt -e ".[dev]"
pytest
```

Security scans:

```bash
bandit -q -r src
pip-audit
```

Main CLI contract tests:

- `tests/test_cli.py`
- `tests/test_cli_integration.py`

## License

MIT