Unified image hashing — ThumbHash, BlurHash, and more — Rust-powered Python bindings
Project description
thumbleweed
Unified image hashing for Python — ThumbHash, BlurHash, and (soon) ColorThief.
Rust-powered via PyO3 + maturin. Zero mandatory dependencies.
- ✅ ThumbHash — compact image placeholder hashes (drop-in for
thumbhash&fast-thumbhash) - ✅ BlurHash — smooth gradient placeholders (drop-in for
blurhash-python) - 🔜 ColorThief — dominant colour extraction (placeholder, not yet implemented)
- ✅ Python 3.10 – 3.14 (including free-threaded
3.13t/3.14t) - ✅ Pillow > 11 integration (optional)
- ✅ Typed (
py.typed+.pyistubs) - ✅ Pure-Rust core — no C extensions, no NumPy required
Installation
pip install thumbleweed
# with Pillow helpers:
pip install "thumbleweed[pillow]"
Development with uv
curl -LsSf https://astral.sh/uv/install.sh | sh
make sync
make test
make prepare
make dist
All import paths work:
import thumbleweed # the unified package
import thumbhash # ThumbHash only (backward-compatible)
import blurhash # BlurHash only
import colorthief # ColorThief (placeholder)
Quick-start
ThumbHash
import thumbhash as th
# Encode — rgba_bytes must be bytes/bytearray of length w*h*4 (R G B A, non-premultiplied)
hash_bytes: bytes = th.encode(w, h, rgba_bytes)
# Decode
w_out, h_out, rgba_out = th.decode(hash_bytes)
# Helpers
r, g, b, a = th.average_rgba(hash_bytes) # dominant colour [0, 1]
ratio = th.approximate_aspect_ratio(hash_bytes) # width / height
BlurHash
import blurhash as bh
# Encode
hash_str: str = bh.encode(rgba_bytes, cx=4, cy=3, width=w, height=h)
# Decode
rgba: bytes = bh.decode(hash_str, width=64, height=64)
Pillow images
from PIL import Image
# ThumbHash
import thumbhash as th
img = Image.open("photo.jpg")
hash_bytes = th.encode_image(img) # any mode, any size
placeholder = th.decode_image(hash_bytes) # → RGBA Image, ≈32 px
# BlurHash
import blurhash as bh
hash_str = bh.encode_image(img, cx=4, cy=3)
placeholder = bh.decode_image(hash_str, width=64, height=64)
thumbleweed (unified)
import thumbleweed
# ThumbHash
hash_bytes = thumbleweed.thumbhash_encode(w, h, rgba)
w, h, rgba = thumbleweed.thumbhash_decode(hash)
# BlurHash
hash_str = thumbleweed.blurhash_encode(rgba, 4, 3, w, h)
rgba = thumbleweed.blurhash_decode(hash_str, 64, 64)
Project structure
thumbleweed/
├── src/
│ ├── lib.rs # PyO3 module — Python bindings
│ ├── thumbhash.rs # Pure Rust ThumbHash encode/decode
│ ├── blurhash.rs # Pure Rust BlurHash encode/decode
│ └── colorthief.rs # Placeholder for future color extraction
├── python/
│ ├── thumbleweed/ # Main package — re-exports everything
│ ├── thumbhash/ # Backward-compatible ThumbHash shim
│ ├── blurhash/ # BlurHash shim
│ └── colorthief/ # Placeholder shim
├── tests/
│ ├── test_thumbhash.py # 70 ThumbHash tests
│ ├── test_blurhash.py # 28 BlurHash tests
│ └── test_imports.py # 13 import / version-consistency tests
└── Cargo.toml
API reference
ThumbHash (import thumbhash)
| Function | Description |
|---|---|
encode(w, h, rgba) → bytes |
Encode raw RGBA bytes → ThumbHash |
decode(hash) → (w, h, rgba) |
Decode ThumbHash → raw RGBA bytes |
average_rgba(hash) → (r,g,b,a) |
Dominant colour in [0, 1] |
approximate_aspect_ratio(hash) → float |
Width / height of the original image |
encode_image(img) → bytes |
Encode a Pillow Image (requires Pillow) |
decode_image(hash) → Image |
Decode to a Pillow Image (requires Pillow) |
BlurHash (import blurhash)
| Function | Description |
|---|---|
encode(pixels, cx, cy, w, h) → str |
Encode raw RGBA bytes → BlurHash string |
decode(hash, w, h) → bytes |
Decode BlurHash → raw RGBA bytes |
encode_image(img, cx, cy) → str |
Encode a Pillow Image (requires Pillow) |
decode_image(hash, w, h) → Image |
Decode to a Pillow Image (requires Pillow) |
thumbleweed (import thumbleweed)
All of the above, prefixed with thumbhash_ or blurhash_.
Building from source
git clone https://github.com/New-Elysium/thumbleweed.git
cd thumbleweed
make sync
make test
make prepare
Make targets
make sync— sync uv environment (dev + bench) and install the extension in editable modemake test— run Python tests and Rust testsmake prepare— run the real-image performance benchmark and inject the table intoCLAUDE.mdmake dist— build wheels / distributions intodist/make upload— uploaddist/*withtwinemake upload-testpypi— uploaddist/*to TestPyPI withtwine
The repository is uv-managed and includes a checked-in uv.lock.
Licence
MIT — see LICENCE.
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 Distributions
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 thumbleweed-0.1.3.tar.gz.
File metadata
- Download URL: thumbleweed-0.1.3.tar.gz
- Upload date:
- Size: 362.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
038d07a734217af41789a9ce29c6e96571b697b2bd1c2387a792306e3726f71e
|
|
| MD5 |
f045fca586ae5ed0dfa2d0a9d5c1f8c9
|
|
| BLAKE2b-256 |
a6888d3f7a2658ff6c5d99ff5a3f047103af76f594ecde7cc15d9f79efbce5bf
|
File details
Details for the file thumbleweed-0.1.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: thumbleweed-0.1.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 673.2 kB
- Tags: CPython 3.14t, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
46a3bae380c751cfee90b9ba40e79958e7cb8f4dd609bd38c631c0308c25310f
|
|
| MD5 |
03721973c6d9e03bff77bd4373f716bb
|
|
| BLAKE2b-256 |
038789066310f2e0c75765584180dd1a8cf63aa16ab8ded76c3cfb50dd7173de
|
File details
Details for the file thumbleweed-0.1.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: thumbleweed-0.1.3-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 673.9 kB
- Tags: CPython 3.14, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60d1b27d7510d54e3833c5f05fc7441d050154820d34bff052fb01a01795cdc2
|
|
| MD5 |
48f1074e1cf46c4ee20b0bdafd679692
|
|
| BLAKE2b-256 |
8fa0e061e4f203f7ca98cba022f6d403f61d30330eb3be97efb4ffebaedab267
|
File details
Details for the file thumbleweed-0.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: thumbleweed-0.1.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 673.3 kB
- Tags: CPython 3.13t, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
32709498316a9e27eeb311d1ea829dc537dc221025ba0dc2f648e18972834353
|
|
| MD5 |
d09f755049a981e477097436ddd5d839
|
|
| BLAKE2b-256 |
1a9844c2bf9e4da79503012f849bb08fc92a95275feac38be3beca50a7633bd5
|
File details
Details for the file thumbleweed-0.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: thumbleweed-0.1.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 674.1 kB
- Tags: CPython 3.13, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c510ba4cfb85005df1f6d52fcfe313527373dd46fefbc763630bc44ad3d538cb
|
|
| MD5 |
371ed5c8e5aa2f942ac379b60da0550d
|
|
| BLAKE2b-256 |
e26d48dfad0ff217c18c8449c0a07eed9fdb38ebd2fe6b568caeec6e7efa865c
|
File details
Details for the file thumbleweed-0.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: thumbleweed-0.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 673.7 kB
- Tags: CPython 3.12, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eefa9ed811c0b1a412156ee5aebba4f7661e740ed7e6ce0509d5c3401f97b671
|
|
| MD5 |
d0697b825350756ff1a1bdaea9a3fe47
|
|
| BLAKE2b-256 |
ecd834c7605c1e7b1e591c471253804158459980d747fdb6924835813812b285
|
File details
Details for the file thumbleweed-0.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: thumbleweed-0.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 674.2 kB
- Tags: CPython 3.11, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
77a5cbcc86576bfb0aa08cc29d35199713d18165be45e14f428ce9f45f06bf8d
|
|
| MD5 |
4b7be00f010baf5c080eb804e8c0f2bd
|
|
| BLAKE2b-256 |
4eaf63e04ee47b64aae046074c299baf1b5dd029c15264f19f572af1a86a4b26
|
File details
Details for the file thumbleweed-0.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: thumbleweed-0.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 674.4 kB
- Tags: CPython 3.10, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c36a49ad1356dbc528fd13a070a8b9b730fadb63adccfce6cbf94749f0f25f78
|
|
| MD5 |
c5af68521f0ff744bd0f6014e57b277e
|
|
| BLAKE2b-256 |
82afa7c660b90486221a11878702e22b063c44f5c891aff245758cf6129ac217
|