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
Metadata
Release files for clawid 0.2.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| clawid-0.2.4.tar.gz | 41.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| clawid-0.2.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 78.5 kB
Release files / clawid-0.2.4.tar.gz
| Download URL | clawid-0.2.4.tar.gz |
|---|---|
| Size | 41.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0f3d2e6379426b864a23123a5a0e2bfbde8549e9f39d51d5da2323e9792f733f
|
|
BLAKE2b-256 checksum How to use checksums |
f06bc0ecc2624930947237b4e6dcb244f80f7b002a2f2002bbb744715b7f0c8f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.2
|
Release files / clawid-0.2.4-py3-none-any.whl
| Download URL | clawid-0.2.4-py3-none-any.whl |
|---|---|
| Size | 37.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
af4d05b579b991973ba189b945a61b53f1a444b49a06be516d2b297c4159fbce
|
|
BLAKE2b-256 checksum How to use checksums |
d12d5d056fbc2ac62afc39fa8abdffcfb2cd6bbc03aa13f7579abc7bcadd9bf7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.12.2
|