Python CLI for batch image watermarking with logo or text
Project description
wmk: Python CLI to watermark images (batch watermark, logo watermark, text watermark)
Python CLI for batch image watermarking with logo or text.
wmk is built for people searching "how to add watermark to image", "add watermark to photo", "put watermark on picture", "add logo to image", "add text watermark to photo", "batch watermark images", "watermark multiple photos at once", and "protect photos with watermark".
20-Second Quickstart: watermark images with a Python CLI
pipx install add-watermark
wmk add
# Expert one-liner: add text watermark to photo
wmk add --input ".\photo.jpg" --text "(c) ACME" --pos br --opacity 40
Install add-watermark for batch watermark, logo watermark, and text watermark
PyPI package name: add-watermark; CLI commands: wmk (primary) and add-watermark (alias).
A) Install (Recommended): pipx
pipx install add-watermark
If pipx is not on PATH yet:
pipx ensurepath
B) Install (Alternative): pip
python -m pip install add-watermark
C) Install from source (repo clone)
git clone https://github.com/ibrahimm7004/add-watermark.git
cd add-watermark
Install from this repo with pipx:
pipx install .
Install from this repo with pip:
python -m pip install .
D) Uninstall (pipx and pip)
pipx uninstall add-watermark
python -m pip uninstall add-watermark
You can also run the beginner-friendly alias:
add-watermark add
Versioning
This project follows Semantic Versioning (MAJOR.MINOR.PATCH):
MAJOR: breaking CLI/API changesMINOR: backward-compatible featuresPATCH: backward-compatible fixes
Check the installed CLI version:
wmk --version
Before and after: put watermark on picture
| Before | After |
|---|---|
Regenerate both demo images:
python examples/generate_examples.py
Add watermark to photo: wizard and expert one-liners
Beginner wizard:
wmk add
Single image with text watermark:
wmk add --input ".\photo.jpg" --text "(c) ACME Studio" --pos br --opacity 40
Single image with logo watermark:
wmk add --input ".\photo.jpg" --watermark ".\logo.png" --pos tr --opacity 35
Batch watermark folder:
wmk add --input ".\photos" --watermark ".\logo.png" --pos br --opacity 35
Batch watermark folder recursively:
wmk add --input ".\photos" --recursive --text "(c) ACME" --pos br --opacity 35
Glob input:
wmk add --input ".\photos\**\*.png" --watermark ".\logo.png" --opacity 35
Dry run (plan only):
wmk add --input ".\photos" --watermark ".\logo.png" --dry-run
Defaults
wmk add defaults (from current implementation):
- Default position:
br - Default opacity:
35(0is invisible,100is fully visible) - Default corner margin:
24px - Image watermark default scale: target width is about
20%of base image width, clamped to at least48pxand at most80%of base width - Text watermark default scale: font size is about
5%of image width, clamped to16..256 - Text watermark style: white text with black stroke and subtle shadow for readability
- Single-image default output:
<input_stem>_watermarked.<ext>next to input - Batch default output (folder input):
watermarked/next to the input folder - Batch default output (glob input):
./watermarked/in current working directory - Batch mode preserves relative folder structure for folder input
- For glob input, paths are preserved only when files are under the current working directory; otherwise output falls back to filenames under
./watermarked/ - Without
--overwrite, existing destination paths are not replaced; a_watermarkedsuffix is appended to avoid collisions
Windows notes
- Quote paths that contain spaces:
- PowerShell/CMD:
--input "C:\Users\me\My Photos\photo 01.jpg"
- PowerShell/CMD:
- Quote globs to keep behavior consistent across shells and avoid shell expansion differences:
--input ".\photos\**\*.jpg"
- PowerShell example:
wmk add --input ".\My Photos\Client A" --recursive --text "(c) ACME" --pos br --opacity 35
- CMD example:
wmk add --input ".\My Photos\*.jpg" --watermark ".\brand logo.png" --pos tr --opacity 35
Why offline CLI (vs online watermark tools)
If you are searching for "add watermark to image online free", an offline CLI is often better for production workflows:
- Privacy: images stay on your machine
- Speed: no upload/download loop
- Batch scale: watermark hundreds of files in one command
- Automation: scriptable for product photos, photography delivery, and repeatable pipelines
Supported formats and behavior
- Input/output formats:
jpg,jpeg,png,webp,tiff,bmp,gif - GIF behavior: first frame only
- JPEG output is written as RGB (quality 95)
- PNG/WEBP preserve alpha when present
Common searches this tool solves
- how to add watermark to image
- add watermark to photo
- put watermark on picture
- how to watermark a photo
- add logo to image
- add text watermark to photo
- batch watermark images
- bulk watermark photos
- watermark multiple photos at once
- add watermark to 100 photos
- signature watermark
- copyright watermark on images
CLI reference
wmk add [OPTIONS]
Options:
--input, -i File path, folder path, or glob pattern
--output, -o Output file (single mode) or output folder (batch mode)
--watermark, -w Watermark image path
--text, -t Text watermark content
--pos Watermark position: tl|tr|bl|br|c
--opacity Opacity 0-100 (0 invisible, 100 fully visible)
--recursive Recurse subfolders for folder input
--overwrite Overwrite existing outputs
--dry-run Print planned outputs without writing files
--verbose Show traceback/debug details
Validation rules:
- Exactly one of
--watermarkor--textin non-interactive mode - If
--inputis missing, wizard prompts for required fields
Library usage (optional)
CLI is the primary interface. If you need direct Python usage:
from pathlib import Path
from watermarker.engine import process_single
process_single(
input_path=Path("photo.jpg"),
output_path=Path("photo_watermarked.jpg"),
text="(c) ACME",
position="br",
opacity=35,
overwrite=True,
)
Open source trust signals
- CI workflow:
.github/workflows/ci.yml - Tests:
pytestsuite for position logic, opacity mapping, and integration behavior - Lint/format checks:
ruff check .andruff format --check . - Contribution guide:
CONTRIBUTING.md - Code of conduct:
CODE_OF_CONDUCT.md - Changelog:
CHANGELOG.md - License:
MIT
FAQ and troubleshooting
wmk command is not found on Windows
- For
pipxinstalls, runpipx ensurepathand restart terminal. - For
pipinstalls, ensure your Python Scripts directory is on PATH.
Why does GIF output look static?
- v1 processes only the first GIF frame.
Why can text look slightly different across machines?
- The tool tries common system fonts first and falls back to Pillow default when needed.
Development
python -m pip install -e .[dev]
ruff check .
ruff format --check .
pytest
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file add_watermark-0.1.1.tar.gz.
File metadata
- Download URL: add_watermark-0.1.1.tar.gz
- Upload date:
- Size: 13.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff7c4c0f3b77d6fb3c5124b4973b2ae9430e0bb8d17347040783a3fa518f052d
|
|
| MD5 |
aaa449bfd07e872f9b54211ed45372ce
|
|
| BLAKE2b-256 |
62f2191a7443f02428688e86f46ed46df861467f38f73b2f358b0cbc23ecc2c7
|
File details
Details for the file add_watermark-0.1.1-py3-none-any.whl.
File metadata
- Download URL: add_watermark-0.1.1-py3-none-any.whl
- Upload date:
- Size: 13.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4da72565d0390b91129e4ae4d62c6b315369dfdc213419a500cdd787451fc1a3
|
|
| MD5 |
396f2d795606a2575e7fa8b398960c7c
|
|
| BLAKE2b-256 |
acca4b2888d9b0742209d56ad85d03795e56c1978402727dd219ced756bf377e
|