Repository navigation
Add uiPSF calibration plugin for learning PSF models - #5
Merged
Merged
Conversation
…ine 3D A plugin of its own (not a mode of the bead calibration window) that has uiPSF learn a PSF from bead z-stacks or from the blinking molecules of an acquisition (in situ), in one channel or two, and saves it as smappy's calibration file, which Spline 3D and Spline 3D 2C read unchanged. - smappy.uipsf: prepare (camera to photons, the split of a two-channel camera recorded so uiPSF's channel transformation can be taken back to chip pixels, background subtraction and the channel shift for uiPSF's bead finder), microscopes (YAML profiles: NA, refractive indices, wavelength, splitter; $SMAPPY_MICROSCOPES, <config>/microscopes, shipped example), runner + worker (uiPSF runs in its own Python, as a script importing nothing of smappy's; progress read from its output), convert (uiPSF's voxels splined and normalised as smappy's bead calibration does; uiPSF's T refitted as the secondary -> main projective map) - the plugin's page, and NOTES.md "uiPSF calibrations" with what was measured - tests/test_uipsf.py: conventions on hand-written uiPSF files (z sign and zero for bead and in situ models, split geometry for all four layouts, T -> transformation, joint normalisation), profiles, parameters, the runner protocol; and, when SMAPPY_UIPSF_PYTHON is set, uiPSF itself on simulated beads fitted back by Spline 3D (slow) Needs the claude/friendly-cerf-qt2sfj branch of ries-lab/uiPSF (numpy 2, current TensorFlow, channel_shift). On simulated data: single-channel beads fit with z slope 1.03, dual-channel beads with a transformation 0.02 px off and z slope 1.015. In situ learning runs but its z scale came out 0.76 on simulated data; the page says to check it against beads. --slow -n auto: 1342 passed, 4 skipped (with SMAPPY_UIPSF_PYTHON set) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GiNdULxyiqQEqWHGKTbVzV
…long - the plugin's page: "Installing uiPSF", after "What it does" -- smappy does not install it; a conda environment with uiPSF's branch, a check, and the three ways to tell smappy where it is (config.yaml per platform, the plugin's setting, SMAPPY_UIPSF_PYTHON), and what a graphics card needs. Checked in a fresh Python 3.12 venv: pip install -e of the branch, then the real-uiPSF test passes. The page's code blocks fenced, which is what the page renderer turns into code (indented ones became paragraphs) - CLAUDE.md: the plugin-shape row for a plugin that runs an external program in its own Python; the Localize tab's titles listed in test_workspace; SMAPPY_UIPSF_PYTHON enabling the uiPSF test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GiNdULxyiqQEqWHGKTbVzV
uiPSF draws its checks in its notebooks (psflearning.makeplots), which make their own figures and call plt.show(); smappy.uipsf.plots redraws them from the saved result into the figure a result window hands over, one tab each: - data vs model (showpsfvsdata / _insitu): a typical bead, or molecules averaged per model plane by fitted height, above the model, with x-z - localization bias (showlocalization): every bead refitted plane by plane, x, y, z bias in nm against stage z, the median in red; beads only - pupil (showzernike): magnitude, aberration phase from Noll 5 up in nm, and every Zernike term - beads / emitters (showcoord): found and used - channel transformation (showtransform): the pairs' residuals after T, in nm, as arrows over the field and scattered; two channels only They replace the plugin's own model and Zernike figures. The text adds the beads' median bias and the transformation's rms residual. The page's Output section describes each tab and what a good one looks like. --slow -n auto: 1346 passed, 4 skipped (with SMAPPY_UIPSF_PYTHON set) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GiNdULxyiqQEqWHGKTbVzV
- a "Zernike" tab after "pupil": the aberration phases from Noll 5 up in nm rms as bars, the channels side by side, the named terms labelled - the beads/emitters tab of a one-channel result failed to draw in the window: one panel is handed an axis, not a figure. The tests now draw every tab through Plot.draw_into, as the window does, which is what found it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GiNdULxyiqQEqWHGKTbVzV
uiPSF's main stays without a channel_shift parameter: the worker sets the measured shift (prepare.channel_shift) as the multi-channel data's shiftxy before uiPSF pairs the beads, on uiPSF's class, since prep_data makes and processes the object in one call. The shift travels in job.json. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GiNdULxyiqQEqWHGKTbVzV
- NOTES.md, "uiPSF's branch": the branch's setup.py keeps main's TensorFlow where it was pinned; on the old stack (TensorFlow 2.9.1, numpy 1.23.5) main and the branch learnt the same jobs bit-identically, and the worker drives an unchanged main as well - the page: an environment already made from uiPSF's main works too --slow -n auto: 1346 passed, 4 skipped (with SMAPPY_UIPSF_PYTHON set) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GiNdULxyiqQEqWHGKTbVzV
The page's install line clones uiPSF's main rather than the branch of ries-lab/uiPSF#17, which is to be merged first; NOTES.md speaks of #17 rather than of the branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GiNdULxyiqQEqWHGKTbVzV
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
Adds Localize/uiPSF calibration, a plugin that uses uiPSF (Liu et al. 2024) to learn a PSF model. It learns from bead z-stacks or from the blinking molecules of an acquisition (in situ), in one channel or two (split camera). The result is saved as an ordinary smappy calibration file, which Spline 3D and Spline 3D 2C read unchanged; uiPSF's own result is saved beside it.
uiPSF is optional and not installed by smappy. It runs in a Python environment of its own; the plugin's page explains how to set one up.
Merge ries-lab/uiPSF#17 first. The page's install instructions clone uiPSF's
main, and only with #17 merged doesmaininstall on Python 3.12 / current TensorFlow (numpy 2, macOS). An existing environment made from uiPSF'smainbefore #17 also works.Changes
smappy.uipsfprepare: reads the data and converts it to photons. Cuts a split camera into its channels, recording exactly how. Subtracts each bead stack's median, because uiPSF's bead threshold includes the background. Measures the channel shift by cross-correlation, because uiPSF's own guess pairs neighbours on a regular bead grid.microscopes: YAML microscope profiles (NA, refractive indices, wavelength, splitter, starting pupil, any uiPSF parameter). They are read from$SMAPPY_MICROSCOPES,<config dir>/microscopes, then the shipped example.uipsf_parameters: the profile and the settings as uiPSF's configuration.runner+worker.py: uiPSF runs as a subprocess under its own interpreter. The worker imports nothing of smappy's. They talk through a job folder and tagged stdout, and the progress bars are passed on. The worker also sets the measured channel shift on uiPSF's class, so uiPSF needs no new parameter.convert: uiPSF's voxel model is clipped, normalised and splined as smappy's bead calibration does it. Two channels are normalised jointly. uiPSF'sTis refitted as smappy's secondary → main projective map in chip pixels. In situ models are reversed and zeroed at the nominal focal plane.plots: uiPSF's notebook diagnostics as result tabs: data vs model, localization bias, pupil, a Zernike bar chart, beads/emitters used, and the channel transformation's residuals.Plugin (
plugins/uipsf.py) and its page, which includes installing uiPSF.tests/test_uipsf.py: conventions checked on hand-written uiPSF files:T→ transformation;With
SMAPPY_UIPSF_PYTHONset, one test also runs uiPSF itself and fits the result back with Spline 3D.test_workspacelists the new Localize plugin. CLAUDE.md gains a plugin-shape row and two test notes. NOTES.md has a "uiPSF calibrations" section with the decisions and measurements. The example profile ships indata/microscopes.Results on simulated data (4 CPU cores, TensorFlow 2.21)
On the old stack (TensorFlow 2.9.1, numpy 1.23.5), the worker drove uiPSF's
mainbefore #17 through all three kinds of job, with results bit-identical to #17's branch.In situ is not validated. The page says to check an in situ model against a bead calibration of the same microscope. Not tried end to end: a mirrored splitter with uiPSF (the geometry is unit-tested), two-channel in situ, real data, Windows, and macOS.
--slow -n auto: 1346 passed, 4 skipped (withSMAPPY_UIPSF_PYTHONset, so the uiPSF test ran). The last commit changes only the page's install line and NOTES.md; the page and uiPSF tests pass on it.🤖 Generated with Claude Code
https://claude.ai/code/session_01GiNdULxyiqQEqWHGKTbVzV