Repository navigation
fix: compute confocal excitation at the excitation wavelengths; refactor PSF pipeline - #163
Open
tlambert03 wants to merge 2 commits into
Open
tlambert03 wants to merge 2 commits into
tlambert03 wants to merge 2 commits into
Conversation
…tor PSF pipeline The confocal excitation spot was computed at the emission wavelength. It is now computed at the excitation wavelengths, each weighted by how strongly the fluorophore absorbs it, then saturated locally. To make this possible, the simulation -> modality -> PSF pipeline is restructured: - `Simulation._channel_fluorescence()` returns a `ChannelFluorescence` per (channel, fluorophore): absorption rate (photons/s by excitation wavelength) and detected fraction (photons detected per absorbed photon, by emission wavelength), always unsaturated. - Modalities combine an illumination (`WidefieldIllumination`, `FocusedIllumination`) and a detection (`CameraDetection`, `PinholeDetection`) into a system PSF (excitation spot x detection PSF), kept separate from the scalar brightness, so changing power doesn't recompute PSFs. - `microsim._psf` builds PSFs from two cached, single-wavelength primitives: `excitation_spot` and `detection_psf`. `make_psf` and `make_confocal_psf` keep their signatures. - The pinhole mask is anti-aliased (each pixel weighted by the fraction of its area inside the pinhole), so small pinholes don't snap to whole pixels. - The confocal pinhole has one physical size per channel: `pinhole_au` is in Airy units at the center of the emission filter band, or at the new `Confocal.airy_unit_wavelength_nm`. - New `Settings.excitation_bin_width_nm` (default 10). BREAKING CHANGES: - all confocal output changes (excitation wavelength, pinhole definition). - `filtered_emission_rates()` / `emission_flux()` apply saturation by default for every modality (new `saturate=` keyword). - removed `microsim.psf.cached_psf`, `Modality.psf()` and `point_scanning` (replaced by `detection_type.sums_over_pixel_area`); `Modality.render()` takes per-channel/fluorophore fluorescence instead of emission rates. - cached PSF arrays are read-only; `make_psf` returns a copy. - `Identity` output follows `settings.float_dtype` (float32 by default). - confocal warns for channels without a band-limited emission filter, and raises if a channel's pinhole detects no light. - PSF disk cache layout changed (old cached PSFs are orphaned, not reused). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #163 +/- ##
==========================================
+ Coverage 88.26% 89.14% +0.87%
==========================================
Files 49 51 +2
Lines 3316 3483 +167
==========================================
+ Hits 2927 3105 +178
+ Misses 389 378 -11 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
A (nearly) closed pinhole passes (almost) no light, so pinhole_au=0 gave an all-zero image. On main, 0 meant a one-pixel pinhole. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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.
This is the PR #161 and #162 were prep for. It started as a one-line question: does point-scanning confocal compute its PSF at the excitation or the emission wavelength? Turns out it was the emission wavelength... for both the excitation spot and the detection PSF. Fixing that properly meant pulling apart how modalities, channels and PSFs fit together, so this restructures the simulation → modality → PSF pipeline.
How images are formed now (widefield is just the case where the excitation is uniform):
Settings.excitation_bin_width_nm); the detection PSF is computed at the emission wavelengths and, for confocal, convolved with an anti-aliased pinhole mask (so small pinholes don't snap to whole pixels).E / (1 + sE)) is applied locally across the spot, so it's strongest at the center. A nice side effect: changing laser power no longer recomputes PSFs.Confocal.airy_unit_wavelength_nmoverrides that.Internally: modalities pick an illumination (
WidefieldIllumination/FocusedIllumination) and a detection (CameraDetection/PinholeDetection) from a new private_optics.py, andSimulationhands them aChannelFluorescenceper channel × fluorophore (absorption rate and detected fraction by wavelength) instead of an xarray of emission rates.Coming next: laser channels (
LASER_488etc.) and updated confocal examples/docs, then acquisition tracks (sequential vs. simultaneous channels).Breaking changes
filtered_emission_rates()/emission_flux()apply saturation by default for every modality (newsaturate=keyword).microsim.psf.cached_psf,Modality.psf(), andpoint_scanning;Modality.render()has a new signature.make_psfreturns a copy).Identityoutput followssettings.float_dtype(float32 by default).Confocal.pinhole_aumust be at least 0.05 AU: a fully closed pinhole isn't allowed (on main,pinhole_au=0meant a one-pixel pinhole; now it would pass no light at all).ConfocalandSettings, so JSON saved with this version won't load in older ones.Speed is the same as main within noise; peak memory is up ~0.1-0.2 GB (3-6%) on the README example.
Known limitations / follow-ups