A Python photomosaic generator that runs every step of the pipeline in a perceptual colour space, and makes every cell of the output a distinct photograph.
The whole image is a mosaic of Vermeer's Girl with a Pearl Earring; zoom into any square and it's a different animal photograph.
mosaicraft rebuilds a target image as a grid of smaller tile photographs. Unlike most photomosaic libraries — which match mean colour in RGB/HSV and reuse the same tile dozens of times — mosaicraft runs Oklab for perceptual colour matching, MKL optimal transport for per-tile colour transfer, and Hungarian 1:1 assignment so every cell is a distinct photograph. Laplacian pyramid blending is also available (set --preset ultra when you want the cell seams smoothed away at the cost of some tile sharpness).
pip install mosaicraft
mosaicraft generate photo.jpg --tiles ./tiles --output mosaic.jpgA sample from the curated animal close-up pool (5,000 photographs, CC BY 2.0 / Open Images V7). Hungarian 1:1 assignment picks exactly one cell for each photograph.
pip install mosaicraft # PyPI
pip install "mosaicraft[faiss]" # with FAISS for huge tile poolsRequires Python 3.10+, NumPy ≥ 1.23, OpenCV ≥ 4.6, SciPy ≥ 1.10, scikit-image ≥ 0.20. No GPU required.
mosaicraft generate photo.jpg --tiles ./tiles --output mosaic.jpg -n 5000from mosaicraft import MosaicGenerator
MosaicGenerator(tile_dir="./tiles").generate("photo.jpg", "mosaic.jpg", target_tiles=5000)Why Oklab? CIELAB was calibrated on small colour differences; it underestimates perceptual distance for the large jumps a photomosaic routinely makes. Oklab (Björn Ottosson, 2020) was rebuilt on modern colour-difference data and is noticeably more uniform on the saturated regions a photomosaic spends most of its compute budget matching. Dropping it into the cost function is free and visibly improves matches on saturated photos.
Why MKL optimal transport? Reinhard color transfer matches the first and second moments of the LAB distribution. MKL (Pitié et al., 2007) matches the full covariance, so the shape of the tile's color distribution is preserved as its statistics slide toward the target cell. Details survive; averages don't win.
The zoom-detail and tile-pool images above are regenerated by python scripts/generate_readme_figures.py after running scripts/download_demo_assets.py (one-time, ~450 MB). The tile pool comes from the Open Images V7 dataset (Google, 2022); to refresh it, run python scripts/curate_oiv7_animals.py (downloads ~180 MB of metadata into .cache/oiv7/, HEAD-validates every retained URL, and rejects rows whose Rotation flag is non-zero so sideways Flickr thumbnails do not enter the pool). The curator filters by exact-match License == https://creativecommons.org/licenses/by/2.0/ so the pool contains only CC BY 2.0 images — no CC BY-SA, no CC BY-NC, no public-domain mixing. Per-image Title, Author, Source (landing URL), and License are surfaced in docs/assets/ATTRIBUTION.md (auto-generated by scripts/generate_attribution.py per Creative Commons TASL practice) and the file is enforced in CI by scripts/verify_attribution.py.
pip install -e ".[dev]"
pytest # unit + pipeline + CLI tests
ruff check src tests # lint
bandit -r src -ll # security scanBug reports, feature requests, and pull requests are welcome. See CONTRIBUTING.md for the development workflow. Security issues: see SECURITY.md.
Releases from v_next_ (released after 2026-05-16) include a sigstore keyless signature bundle
(.sigstore per artifact) attached to the GitHub Release.
pip download <pkg-name>==<version> --no-deps -d ./verify
python -m sigstore verify github \
--cert-identity 'https://github.com/hinanohart/mosaicraft/.github/workflows/release.yml@refs/tags/v<version>' \
--cert-oidc-issuer 'https://token.actions.githubusercontent.com' \
./verify/*.whl ./verify/*.tar.gzThe corresponding .sigstore bundles can be downloaded from the GitHub Release page.
Earlier releases were published without sigstore bundles. Re-installing those versions provides no cryptographic provenance — pin to a current release if assurance matters.
MIT License for human use. See LICENSE.
AI/ML training opt-out: this repository is opted out of AI/ML training, fine-tuning, and embedding generation — see ai.txt. Training use requires separately negotiated written permission.
Image credits:
- Target painting — Johannes Vermeer, Girl with a Pearl Earring (c. 1665), public domain, via Wikimedia Commons.
- Tile pool — 5,000 animal close-up photographs from the Open Images V7 dataset, restricted to images licensed under CC BY 2.0 (no share-alike, no non-commercial, no public-domain mixing). Curated by
scripts/curate_oiv7_animals.pyfrom a hand-picked list of 80 leaf classes in the Open Images Animal subtree (Cat, Dog, Fox, Owl, Tiger, etc.), with HEAD-probe URL validation to drop dead Flickr links and a Rotation-flag filter so the un-rotated Flickr thumbnails arrive upright. Each photograph is attributed to its photographer indocs/assets/ATTRIBUTION.md(auto-generated fromanimal_tiles_index.json, ~3,500 distinct Flickr accounts, with title + source URL per Creative Commons TASL practice) as required by CC BY 2.0. Photographers may withdraw their original uploads — theMANIFEST.jsonSHA256 check reports any URL that subsequently 404s.
mosaicraft stands on the following classic and modern work:
- Björn Ottosson, A perceptual color space for image processing (2020, blog). Oklab.
- Pitié, F. et al., The linear Monge-Kantorovitch linear colour mapping for example-based colour transfer (IET-CVMP 2007). MKL.
- Reinhard, E. et al., Color transfer between images (IEEE CGA 2001).
- Zhang, R. et al., The Unreasonable Effectiveness of Deep Features as a Perceptual Metric (CVPR 2018). LPIPS.
- Wang, Z. et al., Image quality assessment: from error visibility to structural similarity (IEEE TIP 2004). SSIM.
- Tesfaldet, M. et al., Convolutional Photomosaic Generation via Multi-Scale Perceptual Losses (ECCVW 2018). Multi-scale perceptual loss for photomosaic quality assessment.
- Burt, P. & Adelson, E., A multiresolution spline with application to image mosaics (ACM ToG 1983). Laplacian pyramid blending.
- Kuhn, H. W., The Hungarian method for the assignment problem (Naval Research Logistics 1955).

