Skip to content

fix: PSF sizing (radius cap, centering, normalization), add max_psf_radius_aus="auto" - #158

Merged
tlambert03 merged 1 commit into
mainfrom
fix-psf-radius
Oct 11, 2026
Merged

tlambert03 merged 1 commit into
mainfrom
fix-psf-radius

Conversation

@tlambert03

Copy link
Copy Markdown
Owner

This started as a one-liner (max_psf_radius_aus never actually did anything: _pick_nx was getting the wavelength in nm where it expects µm, so the cap was always ~1000x too big). But fixing it meant figuring out what the default should be, and measuring that turned up two more PSF bugs:

  • PSFs weren't centered. widefield did nz + 1, nx + 1 and confocal used sizes as-is, so depending on the inputs you'd get even-sized PSFs, which (in mode="same") shift the image by 0.5-1 voxel... and can misregister channels relative to each other. PSFs are now always odd (rounded up) and centered on a pixel. This also means make_psf(nz=49) now gives you 49 planes, not 50.
  • truncated PSFs were brightened. emission PSFs were normalized by their own (in-focus) plane sum, so a laterally truncated PSF got scaled up to make up for the light it lost (confocal was 2-7% too bright at small radii, widefield partly hid its losses the same way). Normalization now uses the in-focus plane summed over >= 64 AU (cached), so it doesn't depend on the PSF size.

Then the new default, max_psf_radius_aus="auto". This is our opinionated balance between speed and accuracy, aiming for ~1% RMSE vs an untruncated PSF (which is below shot noise unless you're collecting well over ~10^4 photons/pixel):

  • widefield: ~14 AU (for the tails of the in-focus Airy pattern) + half the radius of the out-of-focus light cone (half-angle asin(NA/n)) at half the volume depth. So it grows with how thick your volume is, which turns out to be what matters (lateral FOV doesn't really).
  • confocal: 8 AU (the pinhole takes care of the out-of-focus light).

Calibrated against None on 1024² volumes, 1-8 µm deep (EGFP, NA 1.4, 20 nm pixels):

worst RMSE worst max err per-sample speed vs None
widefield 0.75% 0.32% 1.1-1.4x
confocal 0.28% 0.08% 1.6-2x

it's a bigger deal for larger volumes: at 2048², None was 2-4x slower per sample (and up to ~7x slower to build a new confocal PSF). For small volumes (<= ~256²) auto just hits the FOV and nothing changes. Since the old cap never applied, everyone has effectively been running None all along... so for big volumes, results now move by up to ~1% RMSE, and all results move a little from the centering/normalization fixes. Bumped the PSF disk cache key to v3 so nothing stale gets reused.

fwiw, the old intended default (6 AU) would have been pretty bad for thick widefield volumes: in a 5 µm deep volume it loses ~30% of the light.

Also removes a stray breakpoint() in Spectrum validation that snuck into main.

🤖 Generated with Claude Code

…adius_aus="auto"

- `max_psf_radius_aus` never applied: `_pick_nx` got a wavelength in nm where it
  expected um.
- PSFs are now always odd-sized (rounded up) and centered on a pixel.  Even sizes
  (widefield added 1 to nz/nx, confocal used them as-is) shifted images by
  0.5-1 voxel.  `make_psf(nz=49)` now returns 49 planes.
- Emission PSF normalization uses the in-focus plane summed over >= 64 AU, so it no
  longer depends on the lateral size (truncated PSFs were brightened to compensate).
- New default `max_psf_radius_aus="auto"`: widefield 14 AU + half the radius of the
  out-of-focus cone at half the volume depth; confocal 8 AU.  Calibrated to ~1% RMSE
  vs an untruncated PSF.
- PSF disk cache key bumped to v3.
- Remove a stray `breakpoint()` in `Spectrum` validation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tlambert03
tlambert03 merged commit 0aea364 into main Oct 11, 2026
15 checks passed
@tlambert03
tlambert03 deleted the fix-psf-radius branch October 11, 2026 16:41
@codecov

codecov Bot commented Oct 11, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.15%. Comparing base (8ce5679) to head (4d77e2c).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #158      +/-   ##
==========================================
+ Coverage   88.01%   88.15%   +0.13%     
==========================================
  Files          48       48              
  Lines        3279     3292      +13     
==========================================
+ Hits         2886     2902      +16     
+ Misses        393      390       -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

tlambert03 added a commit that referenced this pull request Oct 11, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant