feat(imaging): SpatialBinner + bin_size in analyze_imaging (#186)#242
Merged
KedoKudo merged 10 commits intofeature/172-2d-imagingfrom Feb 21, 2026
Merged
feat(imaging): SpatialBinner + bin_size in analyze_imaging (#186)#242KedoKudo merged 10 commits intofeature/172-2d-imagingfrom
KedoKudo merged 10 commits intofeature/172-2d-imagingfrom
Conversation
…186) Adds SpatialBinner (src/pleiades/imaging/binner.py) with: - bin_hyperspectral: (n_e,H,W) -> (n_e,H//N,W//N) via skimage.measure.block_reduce with uncertainty propagation sigma_bin = sigma_pixel / bin_size - unbin_map: nearest-neighbour upscale of 2D maps with NaN preservation - unbin_results: full Imaging2DResults upscale to original resolution Adds bin_size parameter to analyze_imaging() (default=1, fully backward-compatible). When bin_size > 1, data is binned before fitting and results are upscaled back to original shape with source_hyperspectral restored and bin_size recorded in metadata. Exports SpatialBinner from pleiades.imaging.__init__. 37 unit tests in test_binner.py; all 463 existing imaging tests still pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
29baaa7 to
a08ec8e
Compare
…propagation P1: When bin_size>1, pixel iteration now reads from the binned HyperspectralData instead of the loader's original unbinned cube, so pixel coordinates match the binned grid expected by the aggregator. P2a: Crop spatial dimensions to exact multiples of bin_size before block_reduce, avoiding zero-padded partial bins that bias edge values. P2b: Propagate uncertainty as sqrt(sum(sigma_i^2))/N instead of mean(sigma)/N, which is correct for heterogeneous per-pixel uncertainties under independent Gaussian noise. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ROI was passed in original image coordinates to the binned pixel iterator, causing IndexError on out-of-bounds access. Now remap start coords with floor division and end coords with ceiling division (clamped to binned dimensions). Also clamp ROI bounds in _iter_hyperspectral_pixels as a safety net. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… clamping Replace silent clamping with the same ValueError validation that loader.iter_pixels uses, so invalid ROIs (reversed, out-of-range) raise consistently regardless of bin_size. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move ROI bounds validation to right after image loading (before binning), so invalid ROIs raise ValueError consistently in both binned and non-binned paths. The min() cap on remapped end coords now only accounts for edge-block cropping, not missing validation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…alidation P1: NaN-padded edge pixels in success_mask are now set to False (bool(np.nan) is truthy) via nan_to_num before bool cast, preventing unfitted edges from being reported as successful. P2a: Empty remapped ROI (x1==x2 when ROI falls in cropped region) is now allowed and yields zero pixels instead of raising ValueError. P2b: bin_size larger than image dimensions now raises ValueError at bin time with a clear message instead of producing a zero-sized binned image that fails unpredictably downstream. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
P1: Move bin_size to keyword-only (after *) in analyze_imaging to preserve positional-argument compatibility with existing callers. P3: The bin_size==1 branch in unbin_map now uses the same crop/pad logic as the main path, so it returns exactly original_shape even when the input map is smaller. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When ROI edges are not aligned to bin_size, the remapped binned ROI expands to neighboring bins. After unbinning, those extra pixels are now masked to NaN (float maps) and False (success_mask) so fitted values don't leak beyond the user-requested region. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…_map For non-floating dtypes (e.g. integer count maps), np.nan cannot be represented and silently becomes 0, making padding look like valid data. Now promote to float64 when the input dtype is not floating. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adds spatial binning functionality to the imaging pipeline to improve signal-to-noise ratio by averaging N×N pixel blocks before fitting. The implementation includes a new SpatialBinner class with binning/unbinning methods, integration into the analyze_imaging() API via a backward-compatible bin_size parameter (default=1), and comprehensive test coverage (37 new unit tests).
Changes:
- Adds
SpatialBinnerclass insrc/pleiades/imaging/binner.pywithbin_hyperspectral(),unbin_map(), andunbin_results()methods for spatial resolution reduction and restoration - Integrates
bin_sizeparameter intoanalyze_imaging()with ROI coordinate remapping and result unbinning logic - Adds 37 unit tests covering binning operations, uncertainty propagation, edge cases, and integration with
analyze_imaging()
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
src/pleiades/imaging/binner.py |
New module implementing SpatialBinner class with spatial binning/unbinning and uncertainty propagation |
src/pleiades/imaging/api.py |
Adds bin_size parameter, _iter_hyperspectral_pixels() helper, ROI remapping logic, and result unbinning integration |
src/pleiades/imaging/__init__.py |
Exports SpatialBinner class |
tests/unit/pleiades/imaging/test_binner.py |
Comprehensive test suite with 37 tests covering binning operations and integration scenarios |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Add metadata (source, binned flag) to PixelSpectrum in binned path for traceability, matching loader.iter_pixels behavior. - Fix misleading uncertainty propagation docstring: now documents sqrt(sum(sigma_i^2))/N formula matching the implementation. - Simplify redundant noise description in class docstring. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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.
Summary
SpatialBinnerinsrc/pleiades/imaging/binner.py:bin_hyperspectral: reduces(n_e, H, W)→(n_e, H//N, W//N)viaskimage.measure.block_reduce; uncertainty propagationσ_bin = σ_pixel / Nunbin_map: nearest-neighbour upscale of 2D maps with NaN preservation and shape croppingunbin_results: upscales all maps inImaging2DResultsback to original resolution; restoressource_hyperspectralbin_size: int = 1parameter toanalyze_imaging()(fully backward-compatible default)SpatialBinnerfrompleiades.imagingtest_binner.pyTest plan
pixi run pytest tests/unit/pleiades/imaging/test_binner.py -v→ 37 passedpixi run pytest tests/unit/pleiades/imaging/ -v→ 463 passed, 0 regressionsanalyze_imaging(..., bin_size=1)behaves identically to before🤖 Generated with Claude Code