Invisible & visible watermarking SDK for AI-generated images and video
Project description
clawID
Invisible & visible watermarking SDK for AI-generated images and video.
A research project by hawky.ai — the AI content provenance platform.
clawID embeds a UUID-based watermark into images and video frames — invisible to the human eye but detectable by the SDK. Inspired by Google SynthID. Built for AI-generated asset provenance tracking.
Links: PyPI · GitHub · Hugging Face Space · hawky.ai
Features
- Invisible watermarking via DWT-QIM (Discrete Wavelet Transform + Quantization Index Modulation)
- Resize-robust mode (
algorithm='fm') — survives ±35% resize, JPEG compression, and minor edits - Visible watermarking — overlay text/logo with configurable opacity and position
- Video support — watermark every Nth frame; detect from any single watermarked frame
- REST API — FastAPI server with
/embedand/detectendpoints - Storage backends — SQLite, PostgreSQL, MongoDB, Redis
- CLI —
clawid embed,clawid detect,clawid embed-video,clawid detect-video
Installation
# Core (images only)
pip install clawid
# With video support
pip install "clawid[video]"
# With REST API
pip install "clawid[api]"
# Everything
pip install "clawid[all]"
Quick Start
Images
from clawid import embed, detect
# Embed invisible watermark
meta = embed(
source='photo.jpg',
output_path='photo_wm.png',
metadata={'uid': 'creator123', 'platform': 'hawky.ai'},
)
print(meta['clawid']) # e.g. "3f2a1b4c-..."
# Detect
result = detect('photo_wm.png')
print(result['uid']) # creator123
print(result['platform']) # hawky.ai
Resize-robust mode
meta = embed(
source='photo.jpg',
output_path='photo_wm.png',
metadata={'uid': 'creator123'},
algorithm='fm', # survives ±35% resize
)
Video
from clawid import embed_video, detect_video
meta = embed_video(
'input.mp4',
'output_wm.mp4',
metadata={'uid': 'creator123', 'platform': 'hawky.ai'},
frame_stride=5, # watermark every 5th frame
)
result = detect_video('output_wm.mp4')
print(result['uid']) # creator123
Visible watermark
meta = embed(
source='photo.jpg',
output_path='photo_wm.png',
metadata={'uid': 'creator123'},
mode='visible', # or 'both' for invisible + visible
opacity=0.6,
position='bottom-right',
)
CLI
# Embed
clawid embed -i photo.jpg -o photo_wm.png --uid creator123 --platform hawky.ai
# Embed (resize-robust)
clawid embed -i photo.jpg -o photo_wm.png --uid creator123 --algorithm fm
# Detect
clawid detect -i photo_wm.png
# Video
clawid embed-video -i input.mp4 -o output_wm.mp4 --uid creator123
clawid detect-video -i output_wm.mp4
# Save to SQLite store on embed, enrich on detect
clawid embed -i photo.jpg -o wm.png --uid creator123 --store sqlite:///clawid.db
clawid detect -i wm.png --store sqlite:///clawid.db
REST API
pip install "clawid[api]"
clawid serve --store sqlite:///clawid.db --port 8000
Endpoints:
| Method | Path | Description |
|---|---|---|
POST |
/embed |
Upload image, get back watermarked image + metadata |
POST |
/detect |
Upload image, get back detected metadata |
GET |
/docs |
Interactive Swagger UI |
# Embed via API
curl -X POST http://localhost:8000/embed \
-F "file=@photo.jpg" \
-F "uid=creator123" \
-F "platform=hawky.ai" \
--output photo_wm.png
# Detect via API
curl -X POST http://localhost:8000/detect \
-F "file=@photo_wm.png"
Storage Backends
from clawid.storage import from_uri
# SQLite (local)
with from_uri('sqlite:///clawid.db') as store:
store.save(meta['clawid'], meta)
record = store.get(meta['clawid'])
# PostgreSQL
with from_uri('postgresql://user:pass@localhost/mydb') as store:
store.save(meta['clawid'], meta)
# MongoDB
with from_uri('mongodb://localhost:27017/mydb') as store:
store.save(meta['clawid'], meta)
# Redis
with from_uri('redis://localhost:6379/0') as store:
store.save(meta['clawid'], meta)
How It Works
Invisible watermark (DWT-QIM)
The payload (UUID + metadata, CBOR-encoded + CRC-checked) is serialised to bits, then each bit is embedded by quantizing a coefficient in the Haar wavelet LL2 sub-band. Each LL2 coefficient represents a 4×4 pixel block, so a change of delta/16 ≈ 2+ pixels per block — above the uint8 quantization step — making the watermark survive JPEG compression and format round-trips.
| Mode | Delta | PSNR | Survives |
|---|---|---|---|
qim (default) |
32 | ~51 dB | PNG/JPEG q≥75, minor edits |
fm (resize-robust) |
192 | ~36 dB | ±35% resize, PNG/JPEG q≥75 |
Video watermarking
Each video is processed frame by frame. Every Nth frame (frame_stride, default 5) receives the same DWT-QIM watermark with the same clawid UUID. Detection samples up to 10 frames and uses majority vote — detection succeeds as long as at least one watermarked frame survives.
Docker
docker compose up
# API available at http://localhost:8000
Development
git clone https://github.com/Hawky-ai/clawID
cd clawid
pip install -e ".[dev,api,video]"
pytest
Links
| PyPI | https://pypi.org/project/clawid/ |
| GitHub | https://github.com/Hawky-ai/clawID |
| Hugging Face | https://huggingface.co/spaces/Hawky-ai/clawID |
| hawky.ai | https://hawky.ai |
License
MIT — Copyright © 2024 hawky.ai
Project details
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 clawid-0.2.0.tar.gz.
File metadata
- Download URL: clawid-0.2.0.tar.gz
- Upload date:
- Size: 37.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50852b60253be558f23f85d71e575bcb1490ee427d396acc2b0a5fcea7ef5c22
|
|
| MD5 |
c2d273c20b83539c44ebd278764cc966
|
|
| BLAKE2b-256 |
064532a4a5cd664eb4c7e8b0a634f1e7ce9c394b252ee960a0f07d0ff78f18ad
|
File details
Details for the file clawid-0.2.0-py3-none-any.whl.
File metadata
- Download URL: clawid-0.2.0-py3-none-any.whl
- Upload date:
- Size: 33.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.12.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3ef30ae89463a0c5802e3d776d6aee0d0c1579a9a6f8950a8094379bde9e0855
|
|
| MD5 |
1d83fd285e7b844f14c502fb87d58058
|
|
| BLAKE2b-256 |
e7d4501ec5adc2166f52ff63537014fcc00f75d4f6840236750f0be61dff5406
|