Skip to main content

Image patch extraction, reconstruction, pairing and seam-aware stitching for super-resolution and dataset pipelines.

Project description

PatchCraft

A small library for encoding an image into patches and decoding it back. Built to slot into other people's torch pipelines as one transform among many — like a GaussianBlur step in a Compose([...]).

Status (2026-05-17): v0.1.0 released; v0.2.0-track is on main (not yet tagged). Public API: extract, Patchify, reconstruct, stitch, pair, resize, Cache, plus geometry helpers (num_patches, tilings, TilingSpec, scale_factor, paired_tilings, PairedTilingSpec), pixel metrics (patch_metrics, per_patch_mse, per_patch_psnr), and PatchPair/PatchMeta.

The lib vs. this repo

Think of the lib as a car and this repo as the car plus its test track.

  • The carsrc/patchcraft/ — is what gets installed by pip install patchcraft. It is a single library with one job: take one image (Tensor[C, H, W]), encode it into patches, decode patches back into the image, optionally pair LR/HR, resize, cache. One image at a time, every time. No datasets, no training, no orchestration, no batching across images. Multi-image is the caller's for loop, or torch.vmap, or a DataLoader.
  • The tracktests/, lab/, tests/_datasets.py, and the dev extras (torchvision, etc.) — is the pit crew, telemetry, driver and stopwatch that prove the car works on real images (MNIST today; more later). It downloads datasets, drives the lib through varied geometries, measures correctness. It never ships in the wheel.

The car is also acoplável — designed to drop into someone else's pipeline:

from patchcraft import Patchify
from torchvision import transforms

transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.GaussianBlur(kernel_size=3),
    Patchify(patch_size=4, stride=2),   # ← PatchCraft as one step
])

Patchify is a callable; chain it inside a Compose, let DataLoader parallelize over workers. PatchCraft gives you the primitive; the surrounding pipeline stays your code.

Visual cheat sheet

The five core operations, one diagram each. Letters mark which patch each cell came from / goes to.

extract — image → patch stack

patch_size=4, stride=4 (no overlap) on an 8×8 image:

   image (1, 8, 8)                  patches (4, 1, 4, 4)
   +-----------------+              +-----+  +-----+
   | . . . . | . . . . |             |  A  |  |  B  |
   | . A . . | . B . . |  extract    +-----+  +-----+
   | . . . . | . . . . |  -------->   patch0   patch1
   | . . . . | . . . . |
   |---------+---------|             +-----+  +-----+
   | . . . . | . . . . |             |  C  |  |  D  |
   | . C . . | . D . . |             +-----+  +-----+
   | . . . . | . . . . |              patch2   patch3
   | . . . . | . . . . |             (row-major order)
   +-----------------+

reconstruct — patch stack → image (bit-exact when stride == patch_size)

Each output pixel = sum of patch contributions / count map (= how many patches covered it). When stride == patch_size, count is all-ones and the divide is a no-op.

   stride == patch  -->  count map all 1   -->  trivial copy
   stride <  patch  -->  count map > 1     -->  weighted average

   patch=4, stride=2, image cols 0..7:
     col:    0  1  2  3  4  5  6  7
     patch0: x  x  x  x
     patch1:       x  x  x  x
     patch2:             x  x  x  x
     count:  1  1  2  2  2  2  1  1   <- divide sum by this

pair — LR <-> HR, same image region, different resolution

scale_factor=2: every k-th LR patch corresponds to the k-th HR patch; HR coords are LR coords times the integer scale.

   LR (1, 4, 4)               HR (1, 8, 8)
   +---------+                +-------------+
   | . . . . |                | . . . . . . . . |
   | .[A]. . |   k = 1  -->   | . .[A A]. . . . |
   | . . . . |                | . .[A A]. . . . |
   | . . . . |                | . . . . . . . . |
   +---------+                | . . . . . . . . |
                              | . . . . . . . . |
                              | . . . . . . . . |
                              | . . . . . . . . |
                              +-------------+

   LR patch at (row=1, col=1)  <-->  HR patch at (row=2, col=2)

stitch — same fold geometry as reconstruct, but each patch weighted by a window kernel

Use when patches were modified by a model and uniform averaging shows boundary seams. Window kernels for patch_size=4:

   weight="uniform"     weight="hann"        weight="gaussian"
   (== reconstruct)     centers > edges      centers >> edges (never 0)

   + + + +              . . . .              . o o .
   + + + +              . X X .              o X X o
   + + + +              . X X .              o X X o
   + + + +              . . . .              . o o .

   no seam attenuation  strong attenuation,  smooth attenuation,
                        image corners -> 0   corners preserved

Everything stays one-image-at-a-time

   for image in images:
       patches = extract(image, ...)       # PatchCraft primitive
       result  = model(patches)            # caller's work
       out     = stitch(result, ...)       # PatchCraft primitive

Multi-image parallelism is the caller's pipeline (torch.vmap, DataLoader workers, etc.) — see SCOPE.md §2.

Scope (what the car does)

  • Extract patches from a single image with configurable size, stride and dilation (extract, Patchify).
  • Reconstruct an image from its patches — exact and weighted-overlap (reconstruct).
  • Stitch modified patches (model output, denoised, super-resolved) back into one image with a window kernel that attenuates boundary seams (stitch, with weight="uniform"|"hann"|"gaussian").
  • Plan the geometry ahead of time: num_patches((H, W), ...) for the count, tilings((H, W), allow_overlap=...) for every full-coverage (patch_size, stride) combo (no image, no allocation — just arithmetic). For LR↔HR setups: scale_factor(...) and paired_tilings(...).
  • Pair LR and HR patches with metadata sufficient to reconstruct either (pair, PatchPair, PatchMeta).
  • Measure pixel-level error between two patch stacks: patch_metrics, per_patch_mse, per_patch_psnr.
  • Resize with pluggable backends — PIL or torch (resize).
  • Cache results on disk with content-addressed keys, OneDrive-race retry, optional zstd (Cache).

Scope (what the car does NOT do)

  • Not a dataset manager. PatchCraft does not load, download, batch, shuffle, or stream datasets. That's the track's job — tests/_datasets.py has mnist_subset(...) for dev fixtures, and torchvision is in the [dev] extra (never a runtime dep of the car).
  • Not a multi-image API. Every primitive takes one image. Use vmap or a Python loop if you need to apply it to many.
  • No SVMs, no kernels, no quantum circuits — those belong to other projects.
  • No neural network training — PatchCraft is infrastructure, not a model.

Install

From PyPI

pip install patchcraft            # core only
pip install patchcraft[cache]     # adds zstandard for compressed Cache entries

From source (development)

git clone https://github.com/LeoPR/PatchCraft.git
cd patchcraft
pip install -e ".[dev,cache]"

For GPU support, install a matching torch wheel before PatchCraft (e.g. pip install torch --index-url https://download.pytorch.org/whl/cu124).

Run tests

pytest
pytest -m "not gpu"        # skip GPU-requiring tests

Layout

PatchCraft/
├── pyproject.toml                  package metadata, build backend (hatchling)
├── README.md                       this file
├── LICENSE                         MIT
├── .python-version                 3.13
├── .gitignore                      ignores archive/, venvs, caches, outputs
├── src/patchcraft/                   library core — one-image-at-a-time primitives
│   ├── __init__.py                 re-exports the full public API
│   ├── extract.py                  patches via F.unfold; Patchify wrapper (ADR 0002)
│   ├── reconstruct.py              inverse via F.fold + count map
│   ├── geometry.py                 pre-flight: num_patches, tilings, TilingSpec
│   ├── pair.py                     LR↔HR pairing; PatchPair, PatchMeta
│   ├── resize.py                   resize with PIL or torch backends
│   └── cache.py                    content-addressed disk cache
├── tests/                          pytest suite (contract tests for src/)
│   ├── test_extract.py             extract + Patchify
│   ├── test_reconstruct.py
│   ├── test_geometry.py            num_patches + tilings
│   ├── test_pair.py
│   ├── test_resize.py
│   ├── test_cache.py
│   ├── test_datasets_helper.py     label_subset
│   ├── test_import.py
│   └── _datasets.py                dev-only fixtures (MNIST, etc) — NOT public API
├── lab/                            ephemeral experiments; see lab/README.md
│   ├── README.md                   bench rules (tracked)
│   └── .gitignore                  ignores everything else (tracked)
├── docs/
│   ├── USAGE.md                    live REPL walkthrough of every public API
│   ├── SCOPE.md                    responsibilities matrix + parallelization analysis
│   ├── AUXILIARY.md                tests/_datasets, lab/, Z:\ conventions (NOT part of the wheel)
│   ├── THEORY.md                   distilled design + §9 condition contract; §0 binding scope
│   ├── ROADMAP.md                  milestone plan
│   └── ADR/
│       ├── 0001-patch-extraction-api.md   pure function `extract`
│       └── 0002-patchify-transform.md     callable wrapper for Compose pipelines
└── archive/                        reference-only; gitignored (pruned 2026-05-17 — only HISTORY.md kept)

Validation lab

The library is "one image in, one tensor out" by design — but you only know it works once you run it end-to-end on real images. That happens in two places, neither of which is part of the shipped package:

  • tests/ — formal pytest suite that defines the contract from docs/THEORY.md §9.
  • lab/ — ephemeral scripts and notebooks for fast hypothesis-checking. See lab/README.md for the bench rules; outputs go to Z:\outputs\patchcraft\ (off-tree).

Datasets used by tests/lab are downloaded lazily into Z:\caches\datasets\<name>\ on first use; they do not ship with the package and are never bundled into the wheel.

Where to read next

If you want… Open
A hands-on tour with real REPL outputs for every public API docs/USAGE.md
The line between "PatchCraft's job" and "your pipeline's job", plus the parallelization story docs/SCOPE.md
The auxiliary test fixtures and lab conventions (not shipped) docs/AUXILIARY.md
Design decisions, math, the per-API contract docs/THEORY.md
Architecture Decision Records docs/ADR/
Milestone plan docs/ROADMAP.md
Per-release changes CHANGELOG.md

Author

Leonardo Marques de Souza

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

patchcraft-0.2.0.tar.gz (144.7 kB view details)

Uploaded Source

Built Distribution

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

patchcraft-0.2.0-py3-none-any.whl (27.3 kB view details)

Uploaded Python 3

File details

Details for the file patchcraft-0.2.0.tar.gz.

File metadata

  • Download URL: patchcraft-0.2.0.tar.gz
  • Upload date:
  • Size: 144.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for patchcraft-0.2.0.tar.gz
Algorithm Hash digest
SHA256 ab739ce0979a7abc0f8aaadb2f3918ef8c9fead250273399425f47491ed085ea
MD5 b3e4373cf158b247442e5ff193771d79
BLAKE2b-256 007aba306f3eb1bdc84a17e7d8fc83f731ef1db57aee0c457d75e53d5e71f453

See more details on using hashes here.

File details

Details for the file patchcraft-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: patchcraft-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 27.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.7 {"installer":{"name":"uv","version":"0.11.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for patchcraft-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3f022372b4863e6004ab996e4ff62805f5d3f5e598b1c49b06d861a364325999
MD5 1dd5908d81915623133a6b2bf471eb8e
BLAKE2b-256 5c8f63c71f15f3b43e9211230f5266de789db316fe8b47d69793b0a6ae4e2267

See more details on using hashes here.

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