Skip to main content

🎨 mozyq

mozyq is a Python command-line tool (mzq) that turns a collection of photographs into a mosaic video. Each frame of the video is a mosaic: a grid of small tile images, drawn from your photo collection, arranged so that from a distance they reproduce a "master" image. The video zooms out from a single tile to the full mosaic, then the tile it started on becomes the next master — chaining several mosaics into one continuous animation.

✨ Features

  • 🖼️ Photo mosaics from any folder of images
  • 🎬 Zoom-transition videos that chain multiple mosaics together
  • 🎯 Optimal tile matching via the Hungarian algorithm (scipy.optimize.linear_sum_assignment)
  • 🌈 Perceptual color matching in LAB color space (OpenCV)
  • 📐 Square or rectangular tiles and output
  • 🛠️ Built-in image normalization to prepare a collection

🚀 Quick start

Prerequisites

  • Python 3.10–3.12

  • ffmpeg on your PATH — used to encode frames into a video:

    sudo apt install ffmpeg     # Debian/Ubuntu
    brew install ffmpeg         # macOS
    choco install ffmpeg        # Windows
    

Install

pip install mozyq

Minimal pipeline

# 1. Collect at least grid_size² images in a folder (81 for the default 9×9).
mkdir photos
seq 200 | xargs -I{} -P8 wget -q https://picsum.photos/630 -O photos/{}.jpg

# 2. Normalize them to a uniform size and format.
mzq normalize photos/ normalized/

# 3. Build the mosaic chain from a seed image (tiles come from its folder).
mzq json normalized/0000.jpg mzq.json --grid-size 9

# 4. Render the video frames.
mzq frames mzq.json frames/

# 5. Encode with ffmpeg.
ffmpeg -r 30 -i frames/%04d.jpg -c:v libx264 -pix_fmt yuv420p mosaic.mp4

📚 Commands

Run mzq --help or mzq <command> --help for the authoritative, up-to-date usage.

Supported image extensions everywhere: .jpg .jpeg .png .bmp .tiff .tif .webp.

mzq normalize

mzq normalize IN_FOLDER OUT_FOLDER [OPTIONS]

Reads every image in IN_FOLDER, resizes it to --target-width (preserving aspect ratio), center-crops it to --target-height, converts to RGB, and writes sequentially numbered JPEGs (0000.jpg, 0001.jpg, …) into OUT_FOLDER. Images smaller than --min-width × --min-height, or whose aspect ratio makes them too short to crop to the target height, are skipped.

Option Default Meaning
--min-width 630 Skip inputs narrower than this
--min-height 630 Skip inputs shorter than this
--target-width 630 Output width
--target-height 630 Output height

mzq json

mzq json MASTER [OUTPUT_JSON] [OPTIONS]

Builds a chain of mosaics and writes it to OUTPUT_JSON (default mzq.json). The tiles are taken from the folder containing MASTER. For each transition it solves an optimal tile-to-cell assignment for the current master, then picks a random tile in the result to become the next master — repeating up to --max-transitions times (stopping early if a master repeats).

Argument / option Default Meaning
MASTER Seed image; its folder supplies the tiles
OUTPUT_JSON mzq.json Output path
--grid-size 9 Tiles per row/column; must be odd
--max-transitions 10 Maximum number of mosaics in the chain

Requires at least grid_size² images in the tile folder (81 for --grid-size 9, 441 for --grid-size 21).

mzq frames

mzq frames MZQ_JSON OUT_FOLDER [OPTIONS]

Reads a JSON file produced by mzq json and renders the zoom animation as sequentially numbered JPEG frames (0000.jpg, 0001.jpg, …) in OUT_FOLDER.

Option Default Meaning
--fpt 60 Frames rendered per transition
--crossfade-frames 60 Frames spent cross-fading the mosaic into the master image at the end of each transition (higher = slower fade)

Feed the frames to ffmpeg to produce the final video:

ffmpeg -r 30 -i OUT_FOLDER/%04d.jpg -c:v libx264 -preset slow -crf 18 -pix_fmt yuv420p mosaic.mp4

mzq json-full

mzq json-full TILE_FOLDER [OUTPUT_JSON] [OPTIONS]

Computes the best tile arrangement for every image in TILE_FOLDER (each image treated as its own master) and writes a {image_stem: [tile_stem, …]} map to OUTPUT_JSON (default full_mzq.json). This is a bulk lookup table, not a video description — mzq frames does not consume it.

Option Default Meaning
--grid-size 16 Tiles per row/column

Requires at least grid_size² images in TILE_FOLDER.

🔧 How it works

  1. Normalize the collection to uniform dimensions.
  2. Vectorize each tile: downscale in LAB color space and flatten to a feature vector.
  3. Split the master into a grid_size × grid_size set of cells.
  4. Assign tiles to cells by minimizing total Euclidean distance in LAB space with the Hungarian algorithm.
  5. Animate: mzq frames renders a cosine-eased zoom out from the start tile to the full mosaic, then cross-fades toward the master image over --crossfade-frames frames at the end of each transition.

Each output frame is the size of the normalized images, so a larger mzq normalize target produces a higher-resolution video.

🐛 Troubleshooting

Message Fix
Need at least N images for GxG grid Add more images, or lower --grid-size
num_tiles must be odd mzq json needs an odd --grid-size (9, 15, 21, …)
No supported image files found Folder has no .jpg/.png/… files
Could not read image from … A file is corrupt or not a real image
ffmpeg: command not found Install ffmpeg (see Prerequisites)

Created with ❤️ by Gilad Kutiel

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mozyq-0.0.23.tar.gz (978.1 kB view details)

Uploaded Source

Built Distribution

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

mozyq-0.0.23-py3-none-any.whl (13.2 kB view details)

Uploaded Python 3

File details

Details for the file mozyq-0.0.23.tar.gz.

File metadata

  • Download URL: mozyq-0.0.23.tar.gz
  • Upload date:
  • Size: 978.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.13

File hashes

Hashes for mozyq-0.0.23.tar.gz
Algorithm Hash digest
SHA256 d258f529a8ced53c61532e79cb581bb15f4863b48780be0a979462c1cb68fb10
MD5 276e8962f6c1a3a17f4b21b29a8f99a3
BLAKE2b-256 29dcad07749c0dc3f65157f4d7690b58182dcb105a8c104d04ad3ebc0215dcc5

See more details on using hashes here.

File details

Details for the file mozyq-0.0.23-py3-none-any.whl.

File metadata

  • Download URL: mozyq-0.0.23-py3-none-any.whl
  • Upload date:
  • Size: 13.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.13

File hashes

Hashes for mozyq-0.0.23-py3-none-any.whl
Algorithm Hash digest
SHA256 678395303f2938b8023311041be5f8f12c1ecd120bc4183feae13c67fa1e76e7
MD5 80f254368cfb4fe16745be7a728334cb
BLAKE2b-256 8544500ce61dd198f457c4607602cf1f3537e7967cdb1fed0750ced28f9a1ed1

See more details on using hashes here.

Release history Release notifications | RSS feed

0.0.24

2 files

This release

0.0.23 This release

2 files

0.0.22

2 files

0.0.18

2 files

0.0.17

2 files

0.0.16

2 files

0.0.15

2 files

0.0.14

2 files

0.0.13

2 files

0.0.12

2 files

0.0.11

2 files

0.0.10

2 files

0.0.9

2 files

0.0.8

2 files

0.0.7

2 files

0.0.6

2 files

0.0.5

2 files

0.0.4

2 files

0.0.3

2 files

0.0.2

2 files

0.0.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page