feat: anisotropic yx pixel size for fluorescence and phase#568
Draft
talonchandler wants to merge 12 commits into
Draft
feat: anisotropic yx pixel size for fluorescence and phase#568talonchandler wants to merge 12 commits into
talonchandler wants to merge 12 commits into
Conversation
Introduces a frozen pydantic model representing a lateral pixel size that may be isotropic or anisotropic. The from_value classmethod normalizes a scalar, mapping, or existing YXPixelSize into a single canonical form, intended for use at internal function boundaries that accept either a plain float (treated as isotropic) or this model.
generate_frequencies, generate_radial_frequencies, generate_sphere_target and gen_coordinate now normalize their pixel size argument through YXPixelSize.from_value at entry, so callers may pass either a scalar (legacy isotropic shorthand) or a YXPixelSize. fftfreq uses .y and .x separately, which produces identical output to the old scalar path when y == x.
phase_thick_3d and isotropic_thin_3d normalize their yx_pixel_size argument through YXPixelSize.from_value and compute independent y and x Nyquist upsampling factors. The upsampled YX shape becomes (Y * y_factor, X * x_factor); under isotropic input (y == x) this collapses to the legacy single-factor path and the output matches exactly. api/phase.py simulate normalizes once at entry so the generated coordinate arrays use the y and x spacings independently. Tests add isotropic-equivalence cases (anisotropic input with y == x produces identical transfer functions to the legacy scalar call) and anisotropic smoke cases for both phase models and the reconstruct wrappers.
isotropic_fluorescent_thick_3d and isotropic_fluorescent_thin_3d normalize their yx_pixel_size argument through YXPixelSize.from_value and compute independent y and x Nyquist upsampling factors. With isotropic input (y == x) the output exactly matches the legacy scalar path. api/fluorescence.py simulate normalizes once at entry so the generated coordinate arrays use the y and x spacings independently. Tests add isotropic-equivalence and anisotropic smoke cases for both fluorescence models and the reconstruct wrappers.
apply_inverse_transfer_function normalizes the configured yx_pixel_size through YXPixelSize.from_value before composing the output zarr scale, so anisotropic configs write distinct y and x spacings. The pixel-size-mismatch warning also reports y and x independently, with the x line suppressed when only y differs (and vice versa). Tests cover the anisotropic warning path and confirm an isotropic config emits no warning when the input scale matches.
inplane_oriented_thick_pol3d_vector.calculate_transfer_function and the api/birefringence_and_phase simulate routine now normalize the incoming yx_pixel_size and raise NotImplementedError when y != x. A shared config with equal y and x continues to run through both paths unchanged. This keeps anisotropic-aware fluorescence and phase reconstructions behind a clean guard, so a config that accidentally routes to birefringence with anisotropic spacing fails loudly instead of producing a quietly-wrong result.
FourierTransferFunctionSettings.yx_pixel_size is now a YXPixelSize. A
before-validator normalizes scalar or {y, x} mapping forms into a
YXPixelSize, so existing YAML configs (yx_pixel_size: 0.3) continue
to parse unchanged and the new {y, x} mapping form is accepted.
A model_serializer on YXPixelSize emits scalar form when isotropic
so dumped configs stay tidy. The pixel-size and emission-wavelength
ratio warnings now report y and x independently and self-heal when
model_copy(update=...) bypasses validation. A field_serializer makes
serialization robust through the same bypass.
Tests cover scalar and dict YAML parsing, isotropic-equals-mapping
equivalence, YAML round-trip in both forms, end-to-end CLI write of
distinct y and x scales into the output zarr, and validation of typo
and negative inputs.
Documents the dict form of yx_pixel_size end-to-end for callers working on rigs with non-square pixels. The scalar form continues to work everywhere; this example just shows the new option.
Picks up the updated yx_pixel_size description from
FourierTransferFunctionSettings (scalar or {y, x} mapping for
anisotropic). Generated by tests/cli_tests/test_settings.py::
test_generate_example_settings.
Collaborator
Author
|
Hi @srivarra, give this a try on your next zebrafish reconstruction trial. |
Contributor
|
@talonchandler Cool, I will give it a shot. |
CI surfaced callers (napari widget reading zarr scale, the legacy 2D_QLIPP and PTI example scripts) that pass numpy floats or 0-d ndarrays where a Python float was expected. Switch the scalar branch to a float() cast so any float-castable input works (numpy scalars, 0-d ndarrays, 0-d torch tensors). Explicit rejects for bool and str preserve the previous error behavior. Multi-element arrays still raise. Tests cover np.float64, 0-d ndarray, 0-d tensor, and a multi-element array rejection.
The napari tab_recon widget recurses into nested BaseModel fields to build a sub-container, reading each inner field's default. Without defaults on y and x, the widget rendered them as undefined, and the form submit produced y=0 / x=0, failing PositiveFloat. Matching the legacy scalar default (0.1) gives the GUI a sensible starting value and keeps the existing yx_pixel_size=0.1 default on FourierTransferFunctionSettings consistent with what users see when they click New.
fix(fluorescence): pass confocal_pinhole_diameter into compute_transfer_function The setting is defined on the fluorescence Settings but was never forwarded to calculate_transfer_function (both recon_dim 2 and 3 branches), so a configured confocal pinhole was silently ignored and every reconstruction ran widefield. Thread s.confocal_pinhole_diameter through both branches. Co-authored-by: Claude Opus 4.8 <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.
Closes #567.
Adds support for non-square pixels in fluorescence (2D + 3D) and phase (2D + 3D) reconstructions. Birefringence paths raise
NotImplementedErrorwheny != x. Scalar configs andy == xconfigs keep working everywhere.Config (backwards compatible).
Both forms parse to a normalized
YXPixelSize(y, x)internally.What changed. New
YXPixelSizevalue type with afrom_valueconstructor that accepts scalar, mapping, or itself. Pydantic settings field flipped toYXPixelSizewith a before-validator and a serializer that emits a scalar when isotropic so YAML stays tidy.utilhelpers, all phase and fluorescence models, theapi/phaseandapi/fluorescenceconsumers, and the CLI output metadata path all acceptYXPixelSize | float. Anisotropic Nyquist upsampling (separatey_factorandx_factor) in the four affected models. One new anisotropic example atdocs/examples/api/phase_3d_anisotropic.py.Tests (de-risking). 46 new tests. Settings: scalar and dict YAML parsing, YAML round-trip in both forms, typo and negative rejection. Utilities: isotropic equivalence vs legacy scalar, anisotropic frequency and sphere generation. Each modality (phase 2D/3D, fluor 2D/3D): isotropic equivalence (
y == xreproduces legacy output bit for bit) plus anisotropic smoke. Birefringence: anisotropic input raisesNotImplementedError. CLI: anisotropic config writes distinct y and x scales to the output zarr, mismatch warning reports y and x independently. Full regression benchmark suite passes with identical metrics.Out of scope. Birefringence anisotropic support. Shift-variant fluorescence. Z pixel size stays scalar.
Sri to validate on DaXi zebrafish data before this merges.