Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 

Repository files navigation

pic2bad 🎬🖼️

Picture to Bad Apple — turns any image into a tile mosaic animation, designed for the Bad Apple!! music video but works with any video, animated GIF, or even a single static image.

Each frame (or single image) is converted to a binary mask. Every cell of the mask is then filled with either a bright or darkened version of your image, producing either a playable mosaic video (with the original audio) or a single PNG mosaic.

Usage

# Default: image.jpg + bad_apple.mp4
python pic2bad.py

# Custom image, default video
python pic2bad.py cat.jpg

# Custom image and video
python pic2bad.py cat.jpg my_video.mp4

# Animated GIF as source -> output is a video
python pic2bad.py cat.jpg animation.gif

# Static image as source -> output is a PNG mosaic
python pic2bad.py cat.jpg photo.png
python pic2bad.py cat.jpg logo.jpg
python pic2bad.py cat.jpg frame.bmp

# Strip audio from output (even if source has audio)
python pic2bad.py cat.jpg my_video.mp4 --no-audio
# or short form
python pic2bad.py cat.jpg my_video.mp4 -n

# Custom tile size (default 32)
python pic2bad.py cat.jpg my_video.mp4 --tile 48

# Custom grid resolution (default 80x60)
python pic2bad.py cat.jpg my_video.mp4 --grid 120x90

# Dark tile brightness 0.0-1.0 (default 0.12)
python pic2bad.py cat.jpg my_video.mp4 --brightness 0.20

# Everything together — flags can be combined in any order
python pic2bad.py cat.jpg my_video.mp4 --tile 24 --grid 100x75 --brightness 0.20 -n

Output naming

Source type Output file
Video (mp4, mkv, mov, …) {tile_name}_video.mp4
Animated GIF {tile_name}_video.mp4
Static image (png, jpg, webp, bmp, tiff, jp2, single-frame GIF) {tile_name}_from_{source_name}_mosaic.png

Flags

Flag Short Argument Default Description
--no-audio -n off Produce a video-only output, even if the source has audio. (Video mode only — ignored for static images.)
--tile integer ≥ 2 32 Tile size in pixels.
--grid WxH (e.g. 120x90) 80x60 Grid resolution in tiles (width × height).
--brightness float 0.0–1.0 0.12 Dark tile brightness multiplier. 0.0 = black, 1.0 = original.

Without --no-audio, audio is automatically copied from the source if present, and skipped silently if the source has no audio.

Requirements

  • Python 3.8+
  • ffmpeg (required)
  • ffprobe (recommended — bundled with ffmpeg on most platforms; without it audio is always skipped, even when the source has a soundtrack)
  • NVIDIA GPU (NVENC) — auto-detected
  • AMD/Intel GPU (VAAPI) — auto-detected
  • No GPU required — CPU fallback

Install ffmpeg

Linux (Arch)

sudo pacman -S ffmpeg

Linux (Debian/Ubuntu)

sudo apt install ffmpeg

macOS

brew install ffmpeg

Windows

winget install ffmpeg
# or download from https://ffmpeg.org/download.html and add to PATH

Setup

# On many Linux distros use `python3` instead of `python`
python -m venv venv

# Linux / macOS
source venv/bin/activate
pip install opencv-python pillow numpy

# Windows PowerShell
.\venv\Scripts\Activate.ps1
pip install opencv-python pillow numpy

# On headless servers (no GUI), prefer the headless OpenCV build:
pip install opencv-python-headless pillow numpy

How it works

  1. Tile image is resized to TILE_SIZE×TILE_SIZE pixels (default 32×32, see --tile)
  2. Each source frame is downscaled to GRID_WIDTH×GRID_HEIGHT grayscale → binary mask (default 80×60, see --grid)
  3. Bright tile is placed on white mask cells, dark tile (multiplied by DARK_BRIGHTNESS) on black cells
  4. Output format depends on the source:
    • Static image → single PNG mosaic, no ffmpeg involved
    • Video / animated GIF → MP4 video; encoder auto-detected: NVIDIA NVENCVAAPI (AMD/Intel) → libx264 (CPU)
  5. Unless --no-audio is set, original audio is copied from the source video; if the source has no audio, the mux step is skipped automatically

Parameters

All rendering parameters can be set via CLI flags (see above). The defaults in the script are:

Parameter CLI flag Default Description
DEFAULT_TILE_SIZE --tile 32 Tile size in pixels
DEFAULT_GRID_WIDTH --grid WxH 80 Tiles horizontally
DEFAULT_GRID_HEIGHT --grid WxH 60 Tiles vertically
DEFAULT_DARK_BRIGHTNESS --brightness 0.12 Dark tile brightness multiplier

Error handling

  • Missing files — exits with a clear message
  • Missing ffmpeg — exits with a clear message (video mode only; image mode works without ffmpeg)
  • Invalid flag values — exits with a clear message (NaN/inf/out-of-range rejected)
  • Corrupt frames — skipped with a warning, render continues
  • No frames decoded — clear error after the read loop with hints
  • No audio in source — video-only output is produced automatically
  • Ctrl+C — gracefully stops ffmpeg and exits with code 1
  • Encoder auto-detection failure — falls back to CPU libx264
  • Pipe full (encoder can't keep up) — exits with a hint to reduce grid/tile size
  • ffmpeg stderr deadlock on long videos — prevented by a background stderr drain thread

Exit codes

Code Meaning
0 Success
1 Any error (missing file, encode failure, interrupted, invalid flag, etc.)

About

Turn any image into a tile mosaic animation over Bad Apple (or any video)

Topics

Resources

Stars

2 stars

Watchers

1 watching

Forks

Contributors

Languages