Skip to content
joaodotworkPublic

About

AI-powered image upscaler accelerated on Apple Metal (MPS).

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

metalgrow

AI-powered image upscaler accelerated on Apple Metal (MPS).

CI Python PyTorch Apple Silicon License: MIT Status Ruff PRs Welcome

metalgrow runs super-resolution on Apple Silicon GPUs through PyTorch's MPS backend, with graceful fallbacks to CUDA and CPU. It runs learned backbones (Real-ESRGAN, SwinIR) behind one stable API, with a bicubic baseline that needs no weights, and has print-oriented tools for scans and line art: metadata preservation, descreening, ink mode and 1-bit masters.


Table of contents

Features

  • 🍎 Apple Metal first β€” PyTorch MPS backend, zero config on Apple Silicon
  • πŸ” Portable β€” automatic fallback to CUDA / CPU when MPS isn't available
  • 🧩 Pluggable backbones β€” bicubic, Real-ESRGAN (x2/x4, anime x4), SwinIR (x2/x4)
  • 🧱 Tiled inference β€” process arbitrarily large images with feathered overlap blending
  • πŸ“‚ Batch mode β€” upscale whole directories or globs with a progress bar
  • πŸ–¨οΈ Print-aware β€” --preserve-metadata keeps grayscale mode, the ICC profile, and a rescaled DPI tag intact
  • πŸ–‹οΈ Ink mode β€” --alpha-mode ink upscales ink-on-transparent line art through its coverage, so the SR model sees the drawing instead of a flat colour plane
  • ⬛ 1-bit masters β€” metalgrow bitmap writes Group 4 line-art TIFFs at 1200 ppi (resampled before thresholding, so edges stay smooth)
  • πŸ“° Descreen β€” --descreen removes halftone screens (moirΓ©) from scanned print before upscaling, leaving text and linework untouched
  • πŸ“¦ Model registry β€” metalgrow models manages cached weights with sha256 verification
  • πŸ§ͺ Tested β€” pytest suite covering the CPU baseline, tiling, batch mode, and registry

Requirements

  • Python 3.11+
  • macOS with Apple Silicon for MPS acceleration (optional β€” CPU/CUDA also work)

Install

From PyPI:

pip install metalgrow

From source:

git clone https://github.com/joaodotwork/metalgrow.git
cd metalgrow
uv venv && source .venv/bin/activate
uv pip install -e .

Or with plain pip:

python -m venv .venv && source .venv/bin/activate
pip install -e .

Usage

CLI

Single file:

metalgrow info
metalgrow upscale input.jpg out.png --scale 2
metalgrow upscale input.jpg out.png --scale 4 --device mps --backbone realesrgan-x4

Whole directory or glob (writes to a target directory, mirroring filenames):

metalgrow upscale ./photos ./photos-upscaled --scale 2 --backbone realesrgan-x2
metalgrow upscale "./photos/*.png" ./out --scale 2 --skip-existing
Flag Default Description
--scale, -s 2.0 Upscale factor (1.01–8.0); learned backbones run their native factor then resample to this exact target
--device, -d auto auto | mps | cuda | cpu
--backbone, -b bicubic bicubic | realesrgan-x{2,4} | realesrgan-anime-x4 | swinir-x{2,4}
--dtype fp32 fp32 | fp16 (fp16 is MPS-only and noisier)
--tile backbone hint Tile size in input px for tiled inference (0 disables)
--tile-pad backbone hint Context padding per tile edge (covers backbone receptive field)
--skip-existing off Batch mode: skip outputs that already exist
--preserve-metadata, -p off Keep grayscale mode, embedded ICC profile, and a rescaled DPI tag (for print / preflight workflows)
--workers, -j 4 Batch mode: parallel I/O workers (inference stays serial)
--descreen off Remove halftone screens (moirΓ©) from scanned print before the backbone runs
--descreen-strength 1.0 Notch depth, 0–1
--alpha-mode separate ink upscales single-colour line art through its alpha (and knocks out paper); auto detects it
--dpi off Write this DPI tag instead of the source's rescaled one

Descreen scanned print

Newspaper and magazine scans carry the printer's halftone dot screen, which SR backbones sharpen into moirΓ©. --descreen removes it first. Detection is local and only touches regions that actually contain a dot lattice β€” type, rules and blank paper come back unchanged. See ADR 0002 for how it works.

# descreen + upscale, keeping DPI / ICC / grayscale for print
metalgrow upscale scan.tif out.tif --descreen -p --scale 1.5 -b swinir-x2

# descreen only, no resize
metalgrow descreen scan.tif clean.tif -p

Line art for print

# ink-on-transparent drawings: 4x, coverage-aware, tagged 300 dpi
metalgrow upscale ./drawings ./png -s 4 -b realesrgan-x4 --alpha-mode ink -p --dpi 300
# then 1-bit Group 4 masters at 1200 ppi, same physical size
metalgrow bitmap ./png ./tiff

See ADR 0003 for how ink mode works.

Manage cached model weights

metalgrow models list
metalgrow models download realesrgan-x4
metalgrow models rm realesrgan-x4

Weights cache to ~/.cache/metalgrow/ by default; override with the METALGROW_CACHE_DIR env var. Every download is sha256-verified.

See docs/models.md for a quality / speed / memory comparison (with benchmark numbers) and guidance on which backbone to pick, or docs/usage.md for the full CLI / library reference.

Library

from PIL import Image
from metalgrow import Upscaler

upscaler = Upscaler(backbone="realesrgan-x2", device="auto")  # auto | mps | cuda | cpu
result = upscaler.upscale(
    Image.open("input.jpg").convert("RGB"),
    scale=2.0,
    tile=256,  # optional β€” backbone has sensible defaults
    tile_pad=16,
)
result.save("out.png")

Project layout

src/metalgrow/
  device.py            # device auto-selection (MPS β†’ CUDA β†’ CPU)
  upscaler.py          # Upscaler class + tiled inference with overlap blending
  batch.py             # directory / glob batch mode
  descreen.py          # local FFT notch filter for halftone screens
  metadata.py          # ICC / DPI / grayscale capture + reapply
  ink.py               # single-ink detection + coverage matte (ink mode)
  bitmap.py            # 1-bit line-art masters
  weights.py           # weight registry, sha256 verification, cache management
  cli.py               # typer CLI (upscale, descreen, bitmap, info, models)
  backbones/           # bicubic, realesrgan, swinir; plugin registry
tests/
docs/
  usage.md             # CLI recipes, library API, env vars, pitfalls
  models.md            # backbone comparison, benchmark snapshot, tiling
  benchmarks.md        # full (device Γ— backbone Γ— scale) tables
  architecture.md      # module layout, data flow, extension points
  adr/                 # design decisions (backbone dependency, descreen, ink mode)

Roadmap

  • Integrate a learned SR backbone (Real-ESRGAN default)
  • Tiled inference for large images
  • Batch / directory mode
  • Model registry + weight download
  • Second backbone family (SwinIR)
  • GitHub Actions CI (lint + test on macOS + Linux)
  • Benchmarks: MPS vs CPU (CUDA pending GPU runner)
  • PyPI releases (trusted publishing)
  • Print workflows: metadata preservation, descreen, ink mode, 1-bit masters
  • CUDA benchmarks
  • Integration tests against real weights

Contributing

Issues and PRs are welcome. Please run ruff and pytest before opening a PR.

License

MIT β€” see LICENSE.


Made with πŸ”© on Apple Silicon.

About

AI-powered image upscaler accelerated on Apple Metal (MPS).

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages