Repository navigation
Z projection 1/6: the pure arithmetic (part of #176) - #211
Merged
Merged
Conversation
Projection (max/mean/sum, optional slice range), projection_dtype (output type and exactness bound from input type, method and slice count; refused rather than wrapped) and PlaneAccumulator (one running plane, z ascending). No I/O, no command yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Scma6W3LC11s3qjXHZuGDm
This was referenced Nov 7, 2026
# Conflicts: # CHANGELOG.md
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.
What changed
First PR of the plan for #176 (recorded Z=1 projection copy), following the design
docs/restart/designs/pp176-z-projection-design-2026-11-07.md(MorphoFeature, branchclaude/restart-doc) and the owner's answers of 2026-11-07 (extendconvert, no default method, every image goes through the method, masks never projected, one optional--z-range).New pure module
src/morphobricks_preprocessing/projection.py(no I/O, layerpure), plus tests. Nothing calls it yet; no command or record changes.Projection(method, z_range): frozen, checked in__post_init__(ValueErrorfor a bad method or range before anything is read).resolve(size)gives the slices used and refuses a range reaching past the image.projection_dtype(dtype, method, slices): the output type from the input type, method and slice count only (never the data). Max keeps the type; integer sums areuint32/int32(8/16-bit inputs) orfloat64holding the exact integer (32-bit inputs); means and float sums arefloat64. 64-bit integers, bool, float16 and complex are refused ascannot_convert_pixel_type. A sum or mean whose worst case (type extreme x slices) could not be exact is refused asprojection_cannot_be_exact, never wrapped or clipped. The returned rule carriesdtype_rule,rounding,significant_bits_droppedand the working memory (working_nbytes), for the record and the memory limit in later PRs.PlaneAccumulator: slices added in ascending z into one running plane; a slice of another type or shape is refused, never cast. NaN propagates (nonan*functions), infinities are kept, a finite sum that overflows to infinity isprojection_overflow, andinf + -infisprojection_undefined.ProjectionandProjectionRefusedare exported from the package; AGENTS.md repo map and CHANGELOG updated.Two corrections to the design's bounds
The design lists
int8sum as exact for Z <= 16 909 320 andint16for Z <= 65 538. Those only check the positive side; the negative extreme (-128 x Z, -32768 x Z) reaches theint32minimum first. The code computes both sides:int8Z <= 16 777 216 (2^24),int16Z <= 65 536;int32intofloat64is Z <= 2^22 (the design said 2^21, which is theuint32bound). A test pins every bound and the first refused slice count, and checks the extremes at the bound.One implementation choice: integer sums to an integer type accumulate in that output type (the bound makes that exact) rather than in a 64-bit accumulator, so the running plane is smaller; values are identical.
Regression test
The end-to-end regression (
test_project_max_makes_a_z_stack_analysable) needs the writer and lands in PR 3 of the series. This PR's regression for the defect that made a projection impossible to do exactly istests/test_projection.py::test_uint16_sum_does_not_wrap(65535 x 3 slices is 196605 inuint32; auint16sum gives 65533), together with the bound and table tests.main(old code): the whole module fails at collection,ModuleNotFoundError: No module named 'morphobricks_preprocessing.projection'(1 error, 0 tests run).pytest tests/test_projection.py -W error).Checks
python tools/check.pyon this branch: Ruff, formatting and code rules clean;4251 passed, 4 skipped, 2 warnings. The same command onorigin/main(ad55dd4) gave4164 passed, 4 skipped, 2 warnings, so the difference is exactly the 87 new tests. The 4 skips are the pre-existing ones (case-insensitive file system x3, file modes x1).Series: 1 pure module (this PR), 2 record v3, 3 plan and write, 4 CLI, 5 run side, 6 notebook card. PRs 2 and onward are stacked on this one.
Generated by Claude Code