Skip to main content

Perceptual photomosaic generator — Oklab color matching, MKL optimal transport, Hungarian placement, and Oklch tile-pool expansion

Project description

mosaicraft

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.

PyPI version Python CI License Code style: ruff

Zoom in on the mosaic - every square is one animal 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.jpg

The tile pool

Animal close-up tile pool

A 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.

Installation

pip install mosaicraft                # PyPI
pip install "mosaicraft[faiss]"       # with FAISS for huge tile pools

Requires Python 3.10+, NumPy ≥ 1.23, OpenCV ≥ 4.6, SciPy ≥ 1.10, scikit-image ≥ 0.20. No GPU required.

Quick start

mosaicraft generate photo.jpg --tiles ./tiles --output mosaic.jpg -n 5000
from mosaicraft import MosaicGenerator
MosaicGenerator(tile_dir="./tiles").generate("photo.jpg", "mosaic.jpg", target_tiles=5000)

Why Oklab / Why MKL

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.

Reproducing the figures

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.

Testing

pip install -e ".[dev]"
pytest                        # unit + pipeline + CLI tests
ruff check src tests          # lint
bandit -r src -ll             # security scan

Contributing

Bug reports, feature requests, and pull requests are welcome. See CONTRIBUTING.md for the development workflow. Security issues: see SECURITY.md.

Verification (sigstore)

Releases from v_next_ (released after 2026-05-16) include a sigstore keyless signature bundle (.sigstore per artifact) attached to the GitHub Release.

Verify a PyPI install

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.gz

The corresponding .sigstore bundles can be downloaded from the GitHub Release page.

Historic releases (pre-2026-05-16)

Earlier releases were published without sigstore bundles. Re-installing those versions provides no cryptographic provenance — pin to a current release if assurance matters.

License and image credits

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.py from 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 in docs/assets/ATTRIBUTION.md (auto-generated from animal_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 — the MANIFEST.json SHA256 check reports any URL that subsequently 404s.

References

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).

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

mosaicraft-0.4.2.tar.gz (53.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mosaicraft-0.4.2-py3-none-any.whl (39.9 kB view details)

Uploaded Python 3

File details

Details for the file mosaicraft-0.4.2.tar.gz.

File metadata

  • Download URL: mosaicraft-0.4.2.tar.gz
  • Upload date:
  • Size: 53.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for mosaicraft-0.4.2.tar.gz
Algorithm Hash digest
SHA256 63b02e4c611a697878a9a63670a3f332947b50ede6128094a660501b8aab1948
MD5 1cd0e0f869fc47804aced1e2127e5c58
BLAKE2b-256 74d1ce103801b6e147e92c37ba13c7e5a578fc0e6a0e74fe05ecff65d50e6c53

See more details on using hashes here.

Provenance

The following attestation bundles were made for mosaicraft-0.4.2.tar.gz:

Publisher: release.yml on hinanohart/mosaicraft

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mosaicraft-0.4.2-py3-none-any.whl.

File metadata

  • Download URL: mosaicraft-0.4.2-py3-none-any.whl
  • Upload date:
  • Size: 39.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for mosaicraft-0.4.2-py3-none-any.whl
Algorithm Hash digest
SHA256 a7796e3da6bd71cfd6aed8cbfca18ac94118863cccd52da9e2fdb43764964815
MD5 0cca9ba5ea831df0c7883997493f3fb4
BLAKE2b-256 bb22fd2320ebad7b73d4f5ac44b8595cfdd44df805568934155820d58d165a75

See more details on using hashes here.

Provenance

The following attestation bundles were made for mosaicraft-0.4.2-py3-none-any.whl:

Publisher: release.yml on hinanohart/mosaicraft

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page