repixelizer/docs/implementation-plan.md
2026-04-22 20:17:25 +01:00

14 KiB

Repixelizer Implementation Plan

Current state

The project is bootstrapped and runnable as a standalone Python repo.

Implemented:

  • package metadata and editable install
  • CLI entrypoint with run and compare modes
  • lattice/phase inference with coherence scoring plus source-period prior
  • source-image structural analysis, now with an optional Torch/CUDA path
  • PyTorch-based continuous UV-field optimization
  • shared source-lattice reference built from actual inferred cell assignments, now with an optional Torch/CUDA path
  • source-first snap/refine scoring with explicit source-vs-representative weights
  • low-confidence phase reranking with soft size penalties instead of hard size-jump rejection
  • GPU-friendly tile-graph proposal building from lattice-assigned raw source pixels plus per-cell edge/cluster scoring
  • discrete cleanup on the output grid
  • optional palette loading and quantization modes
  • compare mode with naive and diffusion baselines
  • stage-aware diagnostics writing, including per-stage source-fidelity and rerank traces
  • synthetic test fixtures and automated tests

Verified:

  • local editable install in a dedicated venv
  • full test suite currently passing on the last completed pass (54 passed)
  • compare-mode smoke run against a real emblem image
  • focused real-fixture probe on tests/fixtures/real/ai-badge-cleaned.png now beats naive resize on source-lattice consistency under the selected lattice (0.0832 final vs 0.1304 naive with the current 126x126 / (0.0, -0.2) pick)
  • end-to-end tile-graph CUDA smoke runs on both a 24x24 emblem case and the cleaned badge fixture; the small emblem case currently drops from about 2.57s on CPU to 0.61s on CUDA on this machine

Status after adjacency-first pass

What changed:

  • src/repixelizer/source_reference.py now owns the lattice-indexed source reference used by metrics, the solver, and phase reranking
  • snap and refine now prefer sharp per-cell source evidence and keep the relaxed-mode neighborhood in the final discrete choice
  • run.json now includes source_fidelity for snap_initial, solver_target, and final_output, plus phase_rerank_candidates
  • scripts/render_focus_crop.py now prints stage-level source-fidelity when generating the guard/tip crop sheet

What that fixed:

  • the cleaned badge regression no longer loses badly to naive resize on adjacency and motif preservation once the solver starts from the selected lattice
  • the solver no longer regresses relative to its own snap stage on the added thin-feature regression case

What is still provisional:

  • the cleaned badge still lands on the conservative 126x126 lattice candidate, just with a much lower confidence than before; that means candidate selection and weight tuning are improved but not "done"
  • tuning has not been rerun yet against the new source-first hyperparameters, so the benchmark/tuning baseline should be treated as stale until the next sweep
  • the baked-checkerboard version of the badge remains a separate background-suppression problem

What is still provisional

The current implementation should be treated as a strong experimental baseline, not a final algorithm.

The most provisional areas are:

  • lattice inference on real, messy generated imagery
  • edge-cell reliability and candidate coverage in the continuous stage
  • the new experimental tile-graph reconstruction path, especially on real noisy inputs
  • low-confidence reranking once multiple plausible lattice sizes survive the new soft penalty
  • compare-mode metrics as a proxy for actual visual quality

Near-term priorities

Active pass

This pass refactors the experimental tile-graph mode into a GPU-friendly codepath.

What landed:

  • analysis.py and source_reference.py now have Torch device paths, so the expensive per-pixel reductions can run on CPU or CUDA without changing the public CLI
  • tile_graph.py no longer walks connected components and destructively consumes windows during model building
  • instead, tile-graph now builds a small per-cell proposal pool from lattice-assigned raw source pixels, sharp exemplars, edge peaks, and cluster-aware proposal scoring
  • the full tile-graph path now honors device, not just the final solver loop, and diagnostics now record both the model-build device and the solver device

Current implementation note:

  • the destructive cluster walk was the main thing fighting CUDA; replacing it with lattice-assigned per-cell proposals keeps the “one cell, one local candidate set” design while turning extraction into top-k scoring and grouped reductions
  • the new proposal builder is intentionally local-first: it keeps candidates tied to the inferred output coord and uses edge/cluster signals only to rank raw source pixels already assigned to that lattice cell

Experimental tile-graph note:

  • there is now a separate tile-graph reconstruction mode that builds literal source-pixel candidates from lattice assignments instead of cell-averaged patches or globally reusable labels
  • the raw-pixel rewrite fixed an implementation mismatch where candidates had drifted into cell-averaged patch colors instead of actual source pixels
  • the later per-cell-local rewrite fixed the repeated-label and opaque-black-background failures by making candidates output-coord-scoped
  • the newest GPU-friendly rewrite keeps that local-candidate behavior but replaces the old component walk with per-cell top-k proposal scoring, which is fast enough to run end-to-end on CUDA
  • the current badge CUDA probe under artifacts/tile-graph-cuda-pass/badge-cuda/ completes end-to-end at the selected 126x126 lattice with 24142 candidates and 0.0283 final source-fidelity

Next after that:

  • rerank low-confidence top lattice candidates with short real solver probes instead of relying only on the cheapest preview
  • improve per-cell tile-graph proposal diversity so thin contours get more than “sharp vs edge-peak” in hard cells
  • evaluate coordinated local moves when single-cell refinement still breaks thin contours
  • rerun tuning now that the tile-graph proposal and device path have changed materially
  • decide whether the tile-graph path should stay a diagnostics-only experiment, become a refine-stage candidate generator, or mature into a full alternate solver

High-value regression cases

AI badge emblem with baked checkerboard background

This locally generated AI badge has become a useful stress case because it fails in a very specific, repeatable way.

Why it is useful:

  • the global target size is broadly plausible, so the failure is not just "picked the wrong lattice"
  • the lower sword-tip region contains sharp single-pixel and near-single-pixel features at multiple orientations
  • those features are exactly the kind of local adjacency pattern Repixelizer is supposed to preserve

Current failure modes:

  • the right side of the sword tip develops a wobbling outline instead of a smooth taper
  • thin oblique outline features around the tip get collapsed into mushy transitions
  • local cell assignments near the tip look inconsistent even when the overall badge size feels right
  • the right-hand guard wing loses short dark-light-dark adjacency motifs and collapses into a chunky gold slab
  • some guard closeups look nearly identical across snap, relaxed, and final, which suggests the wrong local motif is often chosen before later refinement even starts

Current status after the adjacency-first pass:

  • on the cleaned fixture, snap, relaxed, and final now all preserve source-lattice structure substantially better than the old baseline, with final currently landing at 0.0832 source-fidelity score in the reproducible probe under artifacts/badge-final-probe/
  • that is a meaningful improvement over both the previous documented 0.1494 final score and the naive resize score under the same selected lattice
  • the remaining weakness is edge-cell trust and candidate coverage, not the old snap/refine handoff collapse

Important note:

  • the checkerboard is baked into the source image, not transparency
  • when projected onto a 122x122 output grid it aliases into a visible 2-3-2 cadence
  • that 2-3-2 background rhythm is expected from resampling the background pattern and should not be mistaken for proof that the inferred lattice itself is varying in size
  • it is still a problem, because the background is treated as real source structure and can contaminate local scoring near the blade

What future work should improve here:

  • edge-aware source reliability so high-dispersion but high-contrast cells keep trusting source evidence
  • richer candidate generation so snap/refine can reach sharp exemplars, cell edge peaks, and gradient-guided offsets instead of only a fixed local grid
  • short top-k refine probes for low-confidence lattice candidates before the final rerank decision
  • a retuning sweep after the edge-aware/source-guided changes land
  • some form of coordinated local relaxation so nearby cells can move together toward a globally better contour
  • optional background suppression or de-weighting when a baked checkerboard is clearly not semantic content
  • for the experimental tile-graph path specifically, better local proposal diversity and stronger adjacency evidence are still needed even after switching candidates back to literal source pixels
  • for the experimental tile-graph path specifically, the next useful step is pruning and richer proposal families rather than more component-walk optimization, because the old CPU walk is now gone

Current optimizer diagnosis:

  • the shared source-lattice reference, source-first snap/refine scoring, and relaxed-mode handoff are now in place, so the biggest remaining losses are no longer caused by the old representative-lattice collapse
  • the current weak spot is that difficult thin features often live in high-dispersion cells, which still makes the solver partially distrust the source exactly where the contrast is most informative
  • candidate search is also still too centered on the regular UV neighborhood; if the right edge pixel never enters the candidate set, the solver can preserve the wrong motif very consistently
  • low-confidence reranking is softer than before, but it still relies on cheap probes, so larger lattice candidates can remain underexplored even when they support thin contours better

Repository fixtures:

  • tests/fixtures/real/ai-badge-cleaned.png is the manually cleaned transparent version of the same emblem
  • tests/fixtures/real/ai-badge-cleaned.json records why it matters
  • use the cleaned fixture when isolating lattice and contour failures from background-removal failures
  • scripts/render_focus_crop.py generates reproducible docs/debug closeups from output-grid cell coordinates
  • the current docs-facing guard crop uses --cell-bbox 73 20 107 44

1. Build a real benchmark corpus

Add curated real-world cases, not just synthetic ones:

  • emblems and logos with transparency
  • icons with strong outlines
  • simple sprite-like figures
  • examples that are known to fail naive resize-and-dither

Each case should include:

  • source image
  • expected target size or plausible size range
  • short notes on known failure modes

2. Improve lattice inference

The current inference is workable but still heuristic.

Next improvements should focus on:

  • better periodicity detection for noisy or partially aligned images
  • stronger penalties against oversized candidate grids
  • confidence calibration that reflects ambiguity more honestly

3. Improve edge-cell reconstruction

The next quality gap is no longer generic cleanup; it is preserving thin, high-contrast local structure.

Next version should add:

  • edge-aware source reliability overrides for thin outlines and hard transitions
  • source-guided candidate pools that are not limited to a uniform local offset lattice
  • richer diagnostics for which candidate families win on real fixtures
  • later, coordinated block or contour moves when single-cell greedy updates still break lines

4. Make compare mode more trustworthy

The current metrics are useful but still internal.

Next work should:

  • expand score breakdowns
  • validate whether the metrics correlate with actual human preference
  • make it easier to compare runs across parameter sweeps

Suggested next milestones

Milestone 1: Real-image evaluation harness

  • add curated real-image fixtures
  • add golden metadata for expected target-size ranges
  • add a repeatable batch runner
  • emit one summary table per corpus run

Milestone 2: Solver tuning pass

  • tune continuous-stage loss weights against the benchmark corpus after the edge-aware/candidate-guided pass lands
  • test more robust thin-feature and contour preservation behavior
  • reduce cases where the solver still underperforms the naive baseline or settles on the wrong low-confidence lattice

Milestone 3: Coordinated contour moves

  • add contour-aware multi-cell refinement moves
  • protect short dark-light-dark edge motifs during block updates
  • reduce cases where locally correct cells still fail to form a globally smooth outline

Milestone 4: Human-in-the-loop v2 planning

Only after the automatic baseline is strong:

  • explore optional region hints
  • evaluate a minimal GUI or notebook layer
  • consider multi-frame support

Risks

  • The solver can still produce outputs that score well but feel over-smoothed.
  • Real generated images may contain multiple inconsistent local lattice cues.
  • Continuous optimization is flexible, but easy to overfit to the reconstruction term.
  • Metrics that reward coherence can accidentally reward blandness if not balanced carefully.

Assumptions

  • Single-image quality matters more than batch throughput in the current phase.
  • Palette constraints are optional and should stay optional.
  • The project is better served by improving automatic behavior first than by rushing into manual tooling.
  • Python plus PyTorch remains the right stack for the current research/prototyping phase.