Skip to main content

Extract, grid-snap, and palette-lock pose-sheet artwork into real pixel sprite assets.

Project description

PixiPix logo

PyPI Python 3.12 GitHub stars Apache 2.0 license

**Tiny poses in. Tidy pixels out.**

PixiPix is a deterministic, local-first command-line tool for extracting isolated visual frames from PNG source sheets, scaling them with one shared geometric ruler, and converting configured pseudo-pixel cells into true logical RGBA pixels.

PixiPix is content-agnostic. It operates on pixels and geometry only: it does not recognize subjects, infer frame meaning, or assign semantic names.

Note: PixiPix is in active development. APIs and output contracts may change during the alpha series.

Requirements

  • Python 3.12
  • uv
  • PNG source images

PixiPix performs no network requests, telemetry, cloud uploads, or AI inference.

Install

Clone the repository and create the locked environment:

uv sync
uv run pixipix --help

To build an installable wheel:

uv build
uv tool install dist/pixipix-0.1.0-py3-none-any.whl
pixipix --help

Quick start

Create pixipix.toml:

[project]
name = "sprite-source"
strict = true

[source]
format = "png"
expected_components = 2

[background]
mode = "alpha"
alpha_threshold = 8

[extract]
connectivity = 8
minimum_area = 4
maximum_area = 10000
padding = 1
row_tolerance = 2

[frames]
names = ["frame-a", "frame-b"]

[scale]
mode = "reference-frame-width"
reference_frame = "frame-a"
target_size = 24

[pixelize]
source_cell_size = 6
representative = "alpha-weighted-majority"
alpha_policy = "binary"
alpha_threshold = 128
remainder_policy = "pad-transparent"

Inspect the source without writing files:

uv run pixipix inspect source.png --config pixipix.toml

Extract the configured frames:

uv run pixipix extract source.png \
  --config pixipix.toml \
  --output build/extracted

uv run pixipix scale build/extracted \
  --config pixipix.toml \
  --output build/scaled

uv run pixipix pixelize build/scaled \
  --config pixipix.toml \
  --output build/pixelized

The accepted component count must match both source.expected_components and the number of configured frame names. PixiPix fails instead of guessing when they differ.

Commands

pixipix inspect

pixipix inspect INPUT --config CONFIG

Reports deterministic facts including:

  • source dimensions, input mode, and alpha presence
  • normalized RGBA mode
  • selected background behavior and foreground bounds
  • candidate, accepted, and rejected components
  • component bounds, areas, rejection reasons, and deterministic order
  • configured frame-name assignments when counts match
  • configured source cell size when present

inspect never infers a source cell size and does not write output artifacts.

pixipix extract

pixipix extract INPUT --config CONFIG --output OUTPUT [--force]

Writes one RGBA PNG per accepted component plus versioned stage.json metadata. The output is staged and validated before it is published.

pixipix scale

pixipix scale INPUT_DIR --config CONFIG --output OUTPUT [--force]

Consumes a valid extraction-stage directory and applies one global scale factor to every frame in source pixel space. Reference modes derive the factor from one named frame and set its configured width or height target exactly. Optional, explicit per-frame multipliers are recorded and always produce warnings. Scaling uses BOX over float32 premultiplied RGBA channels, then deterministically un-premultiplies and normalizes transparent pixels to prevent dark fringes.

pixipix pixelize

pixipix pixelize INPUT_DIR --config CONFIG --output OUTPUT [--force]

Consumes a valid scale-stage directory and emits one logical RGBA pixel per configured source cell. The grid is anchored at bottom-left: incomplete space belongs to the top and right edges. Output is always at 1× logical resolution and is not aligned, palette-locked, or packed.

Other commands

pixipix --help
pixipix version
python -m pixipix

Configuration reference

PixiPix parses TOML strictly. Unknown keys, unsupported sections, invalid values, duplicate names, unsafe filenames, and inconsistent counts are configuration errors.

Source limits

[source]
format = "png"             # only png is supported
expected_components = 2    # optional, but recommended
max_width = 4096
max_height = 4096
max_pixels = 16777216
max_components = 128

The limits are checked before expensive image allocations where possible. max_pixels has a fixed ceiling of 16,777,216 pixels, and max_components may not exceed max_pixels; configuration cannot silently disable those resource bounds. RGB, RGBA, indexed, grayscale, and grayscale-alpha PNGs are accepted and normalized to an owned uint8 RGBA buffer. Malformed, truncated, and decoder-limit inputs fail as unsupported input.

Background modes

Transparent source:

[background]
mode = "alpha"
alpha_threshold = 8

Known solid color:

[background]
mode = "explicit-color"
color = "#f4e46a"
tolerance = 0.02
alpha_threshold = 8

Color sampled from all four corners:

[background]
mode = "corner-color"
tolerance = 0.02
alpha_threshold = 8
sample_corners = true

Color modes use normalized maximum per-channel distance. The largest absolute channel difference divided by 255 must be less than or equal to tolerance. Six-digit colors compare RGB; eight-digit colors compare RGBA. Corner mode compares RGBA samples and fails if the corners disagree beyond tolerance. Pixels below alpha_threshold are always background.

Extraction

[extract]
connectivity = 8     # 4 or 8
minimum_area = 4
maximum_area = 10000 # optional
padding = 1
row_tolerance = 2

Components smaller than minimum_area or larger than maximum_area remain visible in inspection and stage metadata as rejected components.

Frame names

[frames]
names = ["frame-a", "frame-b"]

Names are preserved unchanged in metadata and assigned only after deterministic ordering and count validation. Path separators, traversal and absolute-path syntax, controls, surrounding whitespace, trailing dots, Windows-reserved basenames, and overlong filenames are rejected. Filename normalization is deterministic, and normalized filenames must remain unique even on case-insensitive filesystems.

Global scale

Choose exactly one scale mode:

[scale]
mode = "explicit-factor"
factor = 0.75
[scale]
mode = "reference-frame-width" # or reference-frame-height
reference_frame = "frame-a"
target_size = 24                # logical pixels

Reference modes use target_size × pixelize.source_cell_size as the exact source-space target. All other dimensions use the same factor with round-half-away-from-zero; non-empty dimensions remain at least one source pixel. A reference frame cannot have an override. An exceptional non-reference correction is explicit and warned:

[frame_overrides.frame-b]
scale_multiplier = 0.96

PixiPix never infers or suggests per-frame normalization.

Logical pixelization

[pixelize]
source_cell_size = 6
representative = "alpha-weighted-majority"
alpha_policy = "binary"
alpha_threshold = 128
remainder_policy = "pad-transparent"

source_cell_size is required by pixelize and by reference scaling. Representative strategies are exact RGBA majority with first row-major tie-breaking, locked center sampling, and the default alpha-weighted-majority, which ignores transparent RGB and weights visible RGB groups by alpha. Alpha is either binary at the configured inclusive threshold or explicitly preserved as the selected-color opacity.

Remainder policies are pad-transparent (minimal top/right transparent-black padding), error, and crop-with-warning (top/right incomplete strips only). Cropping that would reduce a non-empty frame to zero dimensions is rejected.

Output

build/extracted/
├── .pixipix-output
├── frames/
│   ├── frame-a.png
│   └── frame-b.png
└── stage.json

build/scaled/
├── .pixipix-output
├── frames/
│   ├── frame-a.png
│   └── frame-b.png
└── stage.json

build/pixelized/
├── .pixipix-output
├── frames/
│   ├── frame-a.png
│   └── frame-b.png
└── stage.json

stage.json records:

  • schema and PixiPix versions
  • exact-source and effective-configuration SHA-256 hashes
  • normalized source and background-removal facts
  • candidate, accepted, and rejected components
  • ordered frame names and relative paths
  • original and padded source bounds
  • component areas and deterministic source order
  • warnings and successful status

Public artifacts contain no timestamps, absolute machine paths, or temporary paths. Scale metadata records prior-stage identity, config hashes, the shared global factor, reference measurements, overrides, effective frame factors, dimensions, and warnings. Pixelize metadata records prior-stage identity, the bottom-left grid origin, cell size, selection and alpha policies, per-frame top/right padding or crop, logical dimensions, and warnings. Frame order always comes from stage.json, never directory enumeration.

Output safety

PixiPix does not merge new files into stale output:

  • a non-empty output directory is rejected by default
  • an existing empty output directory is safely replaced without requiring --force
  • --force replaces only output with a valid ownership marker plus a coherent, successful stage.json contract and all referenced RGBA PNG frames
  • unowned directories, symlink targets, and untrusted symlink parents are not destructively replaced; the root-owned standard /tmp alias is supported
  • output is built in a temporary sibling directory
  • a previous owned output is restored if atomic publication fails where practical

Determinism

Component discovery scans pixels in row-major order. Four-connectivity visits up, left, right, then down. Eight-connectivity visits up-left, up, up-right, left, right, down-left, down, then down-right.

Accepted components are first sorted by top, left, ascending area, and discovery index. Each row is anchored to the top coordinate of its first component; another component joins the first existing row whose anchor differs by at most row_tolerance (inclusive). The fixed anchor prevents pairwise chaining from merging distant rows. Rows are ordered by anchor top; components within a row use left, ascending area, then discovery index.

JSON uses sorted keys, two-space indentation, UTF-8, finite numbers, Unix-style relative paths, and exactly one trailing newline. PNG output is RGBA, excludes source metadata, uses explicit compression settings, and zeroes RGB channels for fully transparent pixels.

Geometric and channel quantization use separate round-half-away-from-zero helpers. Scale BOX filtering operates on float32 premultiplied red, green, blue, and alpha channels; un-premultiplication occurs in float64 and channels are quantized once. Fully opaque input uses the equivalent native RGBA BOX path for ordinary BOX identity. Pixel representatives use fixed row-major tie rules. Sequential scale then pixelize is the canonical path; no fused implementation exists.

Exit codes

Code Meaning
0 Success
1 Processing or output failure
2 Configuration failure
3 Unsupported or malformed input
4 Unexpected internal error

Expected domain failures do not print tracebacks.

Current limitations

  • one PNG source sheet per command
  • strict configuration and one shared scale factor per extracted sheet
  • component filtering by minimum and optional maximum area
  • explicit source-cell size; no source-cell inference or automatic frame normalization
  • no fixed-canvas alignment, anchors, baseline placement, clipping, palette processing, recoloring, atlas packing, final manifest/report, animation generation, or editor integration
  • no end-to-end build command yet

Development

See DEVELOPMENT.md for architecture, fixture provenance, and the full verification workflow.

PixiPix is licensed under the Apache License 2.0.

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

pixipix-0.1.0a3.tar.gz (1.0 MB view details)

Uploaded Source

Built Distribution

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

pixipix-0.1.0a3-py3-none-any.whl (47.3 kB view details)

Uploaded Python 3

File details

Details for the file pixipix-0.1.0a3.tar.gz.

File metadata

  • Download URL: pixipix-0.1.0a3.tar.gz
  • Upload date:
  • Size: 1.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for pixipix-0.1.0a3.tar.gz
Algorithm Hash digest
SHA256 62517815ac0480a0d92a91bf3ea14e814cfd8e2ada50a7d4083db0191427a59b
MD5 f16c8f336bdda6797b7cb25417f90965
BLAKE2b-256 a394a1f32cd1e8582c301ba18b170394c9c5a6ec5021e91425f99c2c74f60ad2

See more details on using hashes here.

Provenance

The following attestation bundles were made for pixipix-0.1.0a3.tar.gz:

Publisher: publish.yml on saraeloop/pixipix

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

File details

Details for the file pixipix-0.1.0a3-py3-none-any.whl.

File metadata

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

File hashes

Hashes for pixipix-0.1.0a3-py3-none-any.whl
Algorithm Hash digest
SHA256 67ecb6650afed3a3606b27cad5e79005cef3aa883bc010988d7a1249875308a4
MD5 e6c58ce01fd568984a471cc6c899152d
BLAKE2b-256 818eca6b9dc99b0bf3a30f5ae638f88884076b7efb4725bd85eaa4a686cc729b

See more details on using hashes here.

Provenance

The following attestation bundles were made for pixipix-0.1.0a3-py3-none-any.whl:

Publisher: publish.yml on saraeloop/pixipix

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