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.24.tar.gz (977.0 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.24-py3-none-any.whl (13.2 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for mozyq-0.0.24.tar.gz
Algorithm Hash digest
SHA256 603fb8ebe44c67e2de11cf8383a754f29b1df9688b2023126a72b296ceb28df0
MD5 b59e1998988bf2be61b6cf2d959a033c
BLAKE2b-256 9e496a84dba0fd39bd5c678e268cfec53ae011a1a90d758a36d682a03e43aa73

See more details on using hashes here.

File details

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

File metadata

  • Download URL: mozyq-0.0.24-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.24-py3-none-any.whl
Algorithm Hash digest
SHA256 eae44f26ab240765ca8c98c4564d3f79b7001e94dacce2ed31650c910ade50f5
MD5 f035f9d82f773b3843efb7f16f59b505
BLAKE2b-256 16b25814f0da214d96c4d0ad397e9f02afc453141f8ddfab2520f64d5a6f6db2

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.0.24 This release

2 files

0.0.23

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