https://github.com/damoscodehub/tag-suite
Tag images using AI, copy tags between files, and manage metadata — all from the command line.
https://github.com/damoscodehub/tag-suite
Last synced: 10 days ago
JSON representation
Tag images using AI, copy tags between files, and manage metadata — all from the command line.
- Host: GitHub
- URL: https://github.com/damoscodehub/tag-suite
- Owner: damoscodehub
- License: apache-2.0
- Created: 2026-06-25T02:02:44.000Z (24 days ago)
- Default Branch: main
- Last Pushed: 2026-06-25T22:44:06.000Z (23 days ago)
- Last Synced: 2026-06-26T00:13:48.454Z (23 days ago)
- Language: Python
- Size: 26.4 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# tag-suite
Command-line tools for AI image tagging, tag copying, and tag removal.
`joytag` uses the [JoyTag](https://github.com/fpgaminer/joytag) vision model to automatically tag images with 5000+ tags. `copytag` copies tags between files, `addtag` adds tags manually, and `untag` removes tags.
**Supported formats:** JPEG (`.jpg`, `.jpeg`), PNG (`.png`), WebP (`.webp`), TIFF (`.tiff`, `.tif`).
## Setup
Install dependencies:
```bash
pip install torch torchvision Pillow safetensors transformers einops
```
Download the model from [HuggingFace](https://huggingface.co/fancyfeast/joytag/tree/main) and place it in `models/`. The folder must contain `model.safetensors`, `config.json`, and `top_tags.txt`.
For convenience, add this repo's folder to your `PATH` to run the commands from anywhere.
## Usage
All tools accept **`.txt` files as target shortcuts** — each line is treated as a file path. This lets you select files in Explorer (Ctrl+Shift+C), paste the paths into a `.txt`, and pass it to any script.
```bash
python joytag.py mylist.txt
python addtag.py targets.txt --tags "1boy,solo"
python untag.py targets.txt --tags "1boy"
python scrubtags.py paths.txt --clean
```
### Tagging Images
```bash
# Tag all supported images in the current directory
python joytag.py
# Tag all supported images in a specific folder
python joytag.py path/to/images
# Tag a single image
python joytag.py image.jpg
# Tag multiple specific images (any formats)
python joytag.py img1.jpg img2.png img3.webp
# Mix files and folders
python joytag.py img1.jpg path/to/folder
# Overwrite existing tags (instead of merging)
python joytag.py image.png --overwrite
# Recursively process subdirectories
python joytag.py path/to/images --recursive
python joytag.py folder -r
```
By default, folders are scanned **non-recursively** (only direct children). Use `--recursive` / `-r` to process subdirectories.
By default, new tags are **merged** with any existing tags — duplicates are avoided. Use `--overwrite` to replace existing tags with only the predicted ones.
If the repo folder is in your `PATH`:
```bash
joytag # tag current directory
joytag folder # tag a folder
joytag image.png # tag a single image
joytag folder --recursive
```
### Removing Tags
Strips XMP (keywords) and IPTC metadata from images, leaving image data intact.
```bash
# Remove all tags from all supported images in current directory
python untag.py
# Remove all tags from all supported images in a folder
python untag.py path/to/images
# Remove all tags from a single file
python untag.py image.png
# Remove all tags from multiple targets (files and/or folders)
python untag.py img1.jpg img2.webp
python untag.py img1.png path/to/folder
# Remove only specific tags
python untag.py image.jpg --tags "tag1,tag2,tag3"
# Keep only specific tags, remove everything else
python untag.py image.webp --preserve "tag1,tag2"
# Remove specific tags while preserving others
python untag.py image.png --tags "bad1,bad2" --preserve "good1"
# Recursively process subdirectories
python untag.py path/to/images --recursive
# Via PATH
untag
untag image.jpg --tags "1girl,solo"
untag folder --preserve "photo_(medium)"
```
### Copying Tags
Copies tags from one tagged image to other files without running the model — reads existing tags from the source and writes them to targets.
```bash
# Copy tags from source to one or more targets (any formats)
python copytag.py source.png target1.jpg target2.webp
# Copy tags to all images in a folder
python copytag.py source.jpg path/to/folder
# Mix files and folders as targets
python copytag.py source.png img1.jpg path/to/folder
# Overwrite existing tags on targets (instead of merging)
python copytag.py source.webp target.jpg --overwrite
# Recursively process target subdirectories
python copytag.py source.png path/to/images -r
# Via PATH
copytag source.png target.jpg
copytag source.jpg folder --overwrite
```
By default, source tags are **merged** into targets — duplicates are avoided. Use `--overwrite` to replace all tags on targets with only the source's tags.
### Adding Manually Tags
Add specific tags to images without running the AI model.
```bash
# Add tags to all images in current directory (interactive prompt)
python addtag.py
# Add tags to a specific folder or files
python addtag.py path/to/images --tags "tag1,tag2,tag3"
python addtag.py img1.jpg img2.png --tags "new_tag,another_tag"
# Overwrite all existing tags (default is append)
python addtag.py image.jpg --tags "tag1,tag2" --overwrite
python addtag.py image.png -ow --tags "tag1"
# Read tags from a .txt file (one tag per line)
python addtag.py folder --tags_file mytags.txt
# Use a .txt file as target list
python addtag.py targets.txt --tags "tag1,tag2"
# Recursively scan subdirectories
python addtag.py path/to/images --tags "tag1" -r
# Exclude files matching glob patterns
python addtag.py path --tags "tag1" --exclude "*thumb*"
# Via PATH
addtag
addtag image.jpg --tags "1girl,solo"
addtag folder --tags_file mytags.txt
```
If neither `--tags` nor `--tags_file` is provided, you'll be prompted to enter tags interactively (comma-separated).
| Flag | Short | Description |
|------|-------|-------------|
| `--tags` | | Comma-separated tags to add |
| `--tags_file` | | Read tags from a .txt file (one per line) |
| `--append` | `-a` | Merge tags with existing (default behavior) |
| `--overwrite` | `-ow` | Replace all existing tags with the new ones |
| `--recursive` | `-r` | Process subdirectories |
| `--exclude` | | Glob patterns to exclude (comma-separated) |
### Replacing Tags
Find and replace tags across images. Renames a tag everywhere it appears without affecting other tags.
```bash
# Replace a single tag in current directory (interactive prompt)
python replacetag.py
# Replace tags in a specific folder or files
python replacetag.py path/to/images --tags "old_tag=new_tag"
python replacetag.py img1.jpg img2.png --tags "bad_tag=good_tag,old=new"
# Remove a tag entirely (replace with nothing)
python replacetag.py image.jpg --tags "unwanted_tag="
# Read replacements from a .txt file (one old=new per line)
python replacetag.py folder --tags_file repls.txt
# Use a .txt file as target list
python replacetag.py targets.txt --tags "old=new"
# Recursively process subdirectories
python replacetag.py path/to/images --tags "old=new" -r
# Exclude files matching glob patterns
python replacetag.py path --tags "old=new" --exclude "*thumb*"
# Via PATH
replacetag
replacetag image.jpg --tags "1girl=solo"
replacetag folder --tags_file repls.txt
```
To remove a tag without a replacement, use `--tags "unwanted_tag="` (equals sign with empty new name). If neither `--tags` nor `--tags_file` is given, you'll be prompted interactively.
| Flag | Short | Description |
|------|-------|-------------|
| `--tags` | | Comma-separated `old=new` replacements |
| `--tags_file` | | Read replacements from a .txt file (one per line) |
| `--recursive` | `-r` | Process subdirectories |
| `--exclude` | | Glob patterns to exclude (comma-separated) |
### Saving Tags (Backup)
Exports tags from images to a JSON backup file. Useful for recovery or batch editing tags externally.
```bash
# Save all tags from current directory
python savetags.py
# Save tags from a specific folder or files
python savetags.py path/to/images
python savetags.py img1.jpg img2.png
# Save to a specific path
python savetags.py -o mybackup.json
# Auto-sequence (next available number)
python savetags.py -s
python savetags.py -o mybackup.json -s
# Overwrite existing
python savetags.py -ow
python savetags.py -o mybackup.json -ow
# Append to existing (merge, no duplicates)
python savetags.py -a
python savetags.py -o mybackup.json -a
# Rename the new file
python savetags.py -rn
# Rename the existing file
python savetags.py -ro
# Exclude files matching glob patterns
python savetags.py path/to/images --exclude "*thumb*,*small*"
# Recursively scan subdirectories
python savetags.py folder -r
# Via PATH
savetags
savetags -o mybackup.json -s
```
If no mode flag is given and the target file exists, you'll be prompted:
```
tags_backup.json already exists.
[N] Auto-sequence [RN] Rename new [RO] Rename old [OW] Overwrite [A] Append
Choose:
```
| Flag | Short | Description |
|------|-------|-------------|
| `--output` | `-o` | Output path (any mode) |
| `--overwrite` | `-ow` | Replace existing file |
| `--append` | `-a` | Merge into existing file |
| `--sequencenew` | `-s` | Pick next available number |
| `--renamenew` | `-rn` | Choose a new name for the output |
| `--renameold` | `-ro` | Rename existing file, then write new
### Restoring Tags
Reads a JSON backup and writes tags back to matching images. Matches files by relative path first, then falls back to filename.
```bash
# Restore tags from default tags_backup.json to all images in current directory
python restoretags.py
# Restore from a specific backup file
python restoretags.py --backup mybackup.json
# Restore to a specific folder or files
python restoretags.py path/to/images
python restoretags.py img1.jpg img2.png
# Append backup tags to existing tags (never duplicates)
python restoretags.py path/to/images --append
# Exclude files matching glob patterns
python restoretags.py --backup backup.json --exclude "*thumb*"
# Recursively scan subdirectories
python restoretags.py folder -r
# Via PATH
restoretags
restoretags --backup mybackup.json
```
By default, tags are **overwritten** on the image. Use `--append` / `-a` to merge with any existing tags.
### Scrubbing Suspicious Tags
Detects and removes non-tag data (coordinates, serialized floats, Lightroom parameters) that may have been mistakenly written into the keyword field by buggy metadata parsers.
```bash
# Dry-run: scan and show suspicious tags without modifying anything
python scrubtags.py
python scrubtags.py path/to/images
python scrubtags.py image.jpg
# Actually remove the suspicious tags
python scrubtags.py image.jpg --clean
python scrubtags.py path/to/images --clean
# Recursively scan subdirectories
python scrubtags.py folder --clean -r
# Exclude files matching glob patterns
python scrubtags.py path --clean --exclude "*thumb*,*backup*"
# Via PATH
scrubtags
scrubtags image.jpg --clean
```
Running without `--clean` is a safe dry-run — it prints which tags it would remove, along with a full list of all unique suspicious patterns found. Pass `--clean` to actually remove them.