MCPcopy Index your code
hub / github.com/KennethJAllen/proper-pixel-art

github.com/KennethJAllen/proper-pixel-art @v1.7.1

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.7.1 ↗ · + Follow
196 symbols 623 edges 22 files 135 documented · 69% updated todayv1.7.1 · 2026-07-03★ 4781 open issues
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

Proper Pixel Art

by Kenneth Allen

PyPI version Python versions CI Downloads License: MIT Hugging Face Spaces

Noisy, high resolution

Clean, true-resolution pixel art

Summary

Converts noisy, high-resolution pixel-art-style images (from generative models or low-quality web uploads) into clean, true-resolution assets. Such images often have a non-uniform grid and random artifacts, so standard downsampling fails — the usual alternatives are naive downscaling or redrawing the asset pixel by pixel. This tool automates the recovery instead. Videos and GIFs are supported too.

Contents

Installation

Install From PyPI

pip install proper-pixel-art  # CLI and Python API
pip install "proper-pixel-art[web]"  #  Include the local web UI

Or with uv:

uv add proper-pixel-art  # CLI and Python API
uv add proper-pixel-art --extra web  # Include the local web UI

Install from source

git clone git@github.com:KennethJAllen/proper-pixel-art.git
cd proper-pixel-art
uv sync --extra web

Usage

First, obtain a source pixel-art-style image (e.g. from a generative model such as OpenAI's gpt-image-2, or a web upload of pixel art).

The examples below assume you installed via pip install or uv add (commands are on your PATH). If you installed from source with uv sync, prefix each command with uv run (e.g. uv run ppa ...).

Web Interface

Try it live in your browser, no install required, on Hugging Face Spaces.

To run the same interface locally:

ppa-web
# Opens http://127.0.0.1:7860

CLI

ppa <input_path> -o <output_path> -c <num_colors> -s <result_scale> [-t]

Options

Option Description
INPUT (positional) Source image, video, or GIF in pixel-art style
-o, --output <path> Output directory or file path for result. (default: '.')
-c, --colors <int> Number of colors for output (1-256). Use 0 to skip quantization and preserve all colors. May need to try a few different values. (default 0)
-s, --scale-result <int> Width/height of each "pixel" in the output. 1 = no scaling. (default: 1)
-t, --transparent Output with transparent background. (default: off)
-u, --initial-upscale <int> Initial image upscale factor. Increasing this may help detect pixel edges. (default 2)
-w, --pixel-width <int> Width of the pixels in the input image. Use 0 to determine it automatically. (default: 0)
--config <path> YAML config file of pixelation parameters. Flags passed explicitly override values in the file. (default: none)
--intermediate-dir <path> Directory to save images visualizing intermediate algorithm steps. Useful for development. (default: none)

Example

ppa assets/blob/blob.png -c 16 -s 25

Note: --colors is the parameter most likely to need tuning. See the option table above.

Videos and GIFs

Video and GIF inputs are recognized by extension, so the same ppa command pixelates animations too (e.g. from video models such as Sora). The pixel mesh and color palette are computed once from sampled frames and applied to every frame, so the animation stays consistent with no flicker.

ppa <input.mp4|input.gif> -o <output_path> -c <num_colors>

The output format follows the output extension (e.g. -o out.mp4 converts a GIF to MP4), and all the pixelation options and --config from the table above apply:

ppa <input.mp4|input.gif> -o <output_path> --config config.example.yaml

Two extra options apply to video/GIF inputs (they are ignored for images):

Option Description
-f, --format <mp4\|gif> Output format. (default: inferred from output, then input, extension)
-n, --sample-frames <int> Frames sampled for mesh and palette detection. (default: 8)

The ppa-video command is a deprecated alias for ppa, kept for compatibility.

GIF input is decoded with full frame compositing (variable-size delta frames, per-frame durations, and transparency are preserved). GIF output uses a single global palette.

From Python:

from proper_pixel_art.video import pixelate_video

pixelate_video('input.mp4', 'output.gif', num_colors=16)

Use Without Cloning

Web Interface (without cloning)

uvx --from "proper-pixel-art[web]" ppa-web

CLI (without cloning)

uvx --from "proper-pixel-art" ppa <input_path>

Python API

For Python developers who want to integrate this tool into their own code.

from PIL import Image
from proper_pixel_art import pixelate

image = Image.open('path/to/input.png')
result = pixelate(image, num_colors=16)
result.save('path/to/output.png')

Parameters

These mirror the CLI options above.

  • image : PIL.Image.Image — the image to pixelate.
  • num_colors : int — colors in result (1-256), or 0 to skip quantization. Most likely to need tuning.
  • initial_upscale_factor : int — upscale the input first; may help detect lines.
  • scale_result : int — upscale the result; 1 = no scaling.
  • transparent_background : bool — if True, make all pixels matching the most common boundary color transparent.
  • intermediate_dir : Path | None — save visualizations of intermediate steps (for development).
  • pixel_width : int — pixel width in the input, or 0 to detect automatically.
  • config : PixelateConfig | None — a bundle of every tunable parameter, including the deeper mesh-detection (Canny, Hough, line clustering) and color (alpha/transparency thresholds, quantization method, color binning) settings not exposed as direct arguments. Load one with PixelateConfig.from_yaml(path). Explicit arguments override matching values in config.

Returns

A PIL image with true pixel resolution and quantized colors.

Configuration file

All tunable parameters can be collected in a YAML file so you can fine-tune the algorithm without changing code. See config.example.yaml for the full list of keys with their defaults. Any key you omit falls back to the default, so partial files are fine.

from PIL import Image
from proper_pixel_art import pixelate
from proper_pixel_art.config import PixelateConfig

config = PixelateConfig.from_yaml('config.yaml')
result = pixelate(Image.open('input.png'), config=config)

From the CLI, pass --config. Flags given explicitly override values from the file:

ppa input.png --config config.yaml      # use the file
ppa input.png --config config.yaml -c 8 # but override num_colors to 8

Examples

The algorithm is robust. It performs well for images that are already approximately aligned to a grid.

Here are a few examples. A mesh is computed, where each cell corresponds to one pixel.

Bat

  • Generated by GPT-4o.
Noisy, High Resolution Mesh True Pixel Resolution

Ash

  • Screenshot from Google images of Pokemon asset.
Noisy, High Resolution Mesh True Pixel Resolution

Demon

  • Original image generated by GPT-4o.
Noisy, High Resolution Mesh True Pixel Resolution

Pumpkin

  • Screenshot from Google Images of Stardew Valley asset. This is an adversarial example as the source image is both low quality and the object is round.
Noisy, High Resolution Mesh True Pixel Resolution

Real Images To Pixel Art

  • This tool can also be used to convert real images to pixel art by first requesting a pixelated version of the original image from GPT-4o, then using the tool to get the true pixel-resolution image.

  • Consider this image of a mountain

Original mountain

  • Here are the results of first requesting a pixelated version of the mountain, then using the tool to get a true resolution pixel art version.
Noisy, High Resolution Mesh True Pixel Resolution

Algorithm

Here's a step-by-step overview, applied to this GPT-4o-generated blob:

blob

  • Note that this image is high resolution and noisy.

<img src="https://raw.githubusercontent.com/KennethJA

Core symbols most depended-on inside this repo

pixelate
called by 11
proper_pixel_art/pixelate.py
from_dict
called by 8
proper_pixel_art/config.py
_case
called by 7
tests/cases.py
quantize
called by 6
proper_pixel_art/config.py
downsample
called by 6
proper_pixel_art/pixelate.py
_build
called by 4
proper_pixel_art/config.py
from_yaml
called by 3
proper_pixel_art/config.py
build_cell_map
called by 3
proper_pixel_art/pixelate.py

Shape

Function 117
Method 57
Class 22

Languages

Python100%

Modules by API surface

tests/test_video.py35 symbols
tests/test_colors.py27 symbols
tests/test_cli.py17 symbols
proper_pixel_art/video.py15 symbols
proper_pixel_art/colors.py15 symbols
tests/test_config.py13 symbols
tests/test_pixelate.py12 symbols
proper_pixel_art/mesh.py10 symbols
proper_pixel_art/config.py10 symbols
proper_pixel_art/video_io.py9 symbols
proper_pixel_art/cli.py8 symbols
proper_pixel_art/pixelate.py7 symbols

For agents

$ claude mcp add proper-pixel-art \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact