feat(preprocessing): Pillow resize and augmentation drivers - #793
Merged
Conversation
JArmandoAnaya
force-pushed
the
feat/preprocessing-p1-domain
branch
4 times, most recently
from
August 26, 2026 11:38
ba0f33a to
9d324ff
Compare
PreprocessingDriverNotFound, with its server rule, its api.md rows and its exposure entry. No route raises it yet; the pre-processing registry does.
A new visionset.preprocessing package outside the kernel: a registry over the visionset.preprocessing entry-point group, keyed by step kind, and the two built-in Pillow drivers. Resize orients, then stretches or letterboxes onto a pad_value canvas at the kernel's letterbox_fit numbers; augmentation reads every draw from the kernel's seed functions. Bytes are re-encoded in the source format with no metadata. Import-linter contract 1 now also forbids the kernel from importing the package.
JArmandoAnaya
force-pushed
the
feat/preprocessing-p2-drivers
branch
from
August 26, 2026 11:44
3962adb to
fa747d3
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Position 2 of the pre-processing epic (#785): the pixel side of a recipe, as a package outside the kernel with its own entry-point group.
What changed
src/visionset/preprocessing/package.registry.pyscans thevisionset.preprocessingentry-point group on every call, keeps what satisfies thePreprocessingDriverport and keys it by step kind;drivers(),pick(installed, kind),driver_for(installed, step)anddriver(kind)mirror the format registry's shape. A kind no installed driver applies is refused withPreprocessingDriverNotFound, whose message lists the installed kinds.pillow/__init__.pyships the two built-in drivers.PillowResizeDriverapplies the EXIF turn first — ingest'sPillowImageProcessorbakes the orientation in before it measures, so the manifest's dimensions already describe the oriented picture — then stretches withImage.resize(LANCZOS when the pixel count shrinks, BICUBIC when it grows) or letterboxes by pasting the resized content at the kernel'sletterbox_fitoffsets onto apad_valuecanvas.PillowAugmentDrivermirrors, brightens then contrasts, or turns by quarter turns, reading every draw from the kernel'shflip_applied,brightness_contrast_factorsandrot90_quarter_turns; variant 0 comes back untouched apart from orientation and re-encoding.PreprocessingDriverNotFoundinkernel/errors.py, mapped to500 PREPROCESSING_DRIVER_NOT_FOUNDwith an exposed message, with its rows indocs/content/api.mdandtests/server/test_errors.py. No route raises it yet; the composition points arrive in the next position.pyproject.toml: thevisionset.preprocessingentry-point group withpillow-resizeandpillow-augment, and import-linter contract 1's forbidden list gainsvisionset.preprocessing. The contract is tightened, never relaxed; the fresh-process purity probe now also asserts neithervisionset.formatsnorvisionset.preprocessingis loaded by a kernel import.Re-encode rules
Bytes come back in the source's own format: a JPEG as a JPEG at quality 95 with its chroma subsampling kept (read off the source with
get_samplingbefore the transform, since the resized image is no longer a JPEG Pillow can "keep" from); a PNG as a lossless PNG; anything else Pillow can open as a PNG. No metadata travels — the EXIF that named the orientation is dropped because the orientation is now in the pixels, and no ICC profile or text chunk is copied.media_type(bytes)in the same module answersimage/jpegorimage/pngfor the preview route to return. Palette, bilevel, 16-bit and CMYK sources are converted to RGB (or RGBA when transparent) before any transform, because Pillow silently resamples a palette image nearest-neighbour.Determinism scope
The same seed over the same bytes gives identical bytes on one machine with one Pillow and one set of codecs; the tests assert exactly that and nothing wider. Byte stability across environments is not promised, as the epic's decisions say.
Found, not fixed
letterbox_fithas no floor of one pixel on the content size, so an extreme aspect ratio — a 1×200 source onto a 32×32 canvas — rounds one side to 0. The driver refuses that case withUnsupportedMediabefore Pillow raises a bareValueError, and the geometry transform still computes a zero-width strip for the same case. The single fix is a floor insideletterbox_fit, which this position does not touch; the kernel domain is the previous position's surface.Image.resizecall, so it takes one filter; the dispatch's rule names downscaling and upscaling without saying which axis decides.Stack
Position 2 of the pre-processing stack (#785); based on
feat/preprocessing-p1-domain(PR #791).Checks
Targeted, per commit:
uv run pytest tests/preprocessing tests/architecture tests/kernel tests/server/test_errors.py tests/formats/test_registry.py(2119 passed, 6 skipped),uv run lint-imports(4 kept, 0 broken),uv run mypy src/visionset(no issues in 183 files),uv run ruff check .anduv run ruff format.Full local gate on the final tree, verbatim:
Docs group, verbatim: