Repository navigation
A real Fiji plugin, matching what the QuPath extension does - #9
Merged
Merged
Conversation
Four commands under Plugins > FLIMKit: Connect, Fetch FLIMKit images, Send ROIs to FLIMKit, Fetch ROIs from FLIMKit. SciJava commands in a Maven build against the jars Fiji already ships, so every dependency is provided and nothing is shaded in. It finds the bridge itself through ~/.flimkit/bridge.json, falling back to qupath-bridge.json, and checks protocol_version rather than comparing version strings. Regions travel as GeoJSON polygons in image-pixel coordinates, outer ring only, because FLIMKit's own importer discards interior rings. Sending refuses unless the regions were drawn on an image fetched from FLIMKit, since nothing else shares that pixel grid. Verified in a real headless Fiji: all four commands register, a drawn region reaches FLIMKit with its coordinates intact, a FLIMKit region comes back as an ImageJ Roi, and both images arrive with their units. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two more commands. Open FLIM file posts the path to the bridge and remembers the dataset id, which is the single-image stand-in for QuPath's project. Fit ROI decays sends the ROI Manager contents against that dataset and puts the results in an ImageJ Results table. The settings dialog is generated from GET /v1/fit/defaults through GenericDialog, so the fields follow the schema the server serves rather than being laid out by hand, the same way QuPath's does it. Images now come from the dataset's own planes when a file is open. /v1/images serves a live FLIMKit session and 404s for a dataset, which is what the first run against a real PTU showed. Verified in headless Fiji against bi_01.ptu: opened as ds_1, intensity plane 16x16 in photons, one region fitted to 2.258 ns over 316167 photons. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
IOPlugin.supportsOpen(Location) defaults to returning false and AbstractIOPlugin only overrides the String variant, so the IOService, which dispatches on Location, never matched. Fiji reported no opener found for a FileLocation, which is the type that was failing. Overriding both Location methods fixes it. Opening registers the dataset as well, so fitting works straight afterwards with no separate step. Verified on a 240 MB convallaria.ptu: opened 512x512 in photons through the IOService. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Fit per-pixel lifetimes runs the fit as a job, showing progress in the ImageJ status bar and cancelling on Escape, then opens every returned map as one stack with the plane names as slice labels. Phasor plot draws the density and the universal semicircle in Swing. Click to place an elliptical cursor, drag to move it, Draw region to trace an outline. Each cursor reports pixel count and both lifetimes, the settings dialog comes from GET /v1/phasor/settings, and Add to ROI Manager brings the selected pixels back as regions. The plot maths sits in PhasorPlot, away from the Swing, so the wire format and the density decoding are testable without a toolkit. Job progress is an object carrying a fraction rather than a number, which is what the first real run against a job showed. Verified against bi_01.ptu in headless Fiji: 13 planes fitted and opened as a 13 slice stack, phasor density decoded over 65536 bins, and a cursor on the population selecting all 16 pixels at tau_phi 6.30 ns. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Stitch and fit takes the container holding the tile positions, runs the pipeline as a job, reopens the output as a dataset and shows the maps, with tile_fit and stitch_fit both offered. Every fit now opens its numbers beside its image. Per-pixel shows the global summed fit that seeds it, which carries the lifetimes, the fractions, the reduced chi-squared and which IRF was used. Stitch shows its own summary. ROI fits already had their table. Verified against bi_01.ptu: the summed fit table comes back with ten columns including taus 3.630 and 0.279 ns and the machine IRF it used. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Stitch and fit offered only the pipeline choice. It now prompts from GET /v1/pipeline/defaults, so tile_fit gets the exponentials, tau range, binning, photon floor, IRF strategy and the rest, with the pipeline choice among them. The settings dialog treats a schema entry without applies_to as applying to every mode, which is how the pipeline schema is written. Reopening the output as a dataset failed with 400 not a stitched output directory, because only stitch_fit writes the photon cube that check looks for. tile_fit assembles maps instead. The summary already prepares real-unit TIFFs, so the products are opened directly and the run says so when it wrote none. Phasor regions were built as a point polygon over scattered pixels, which drew nonsense on the image. They are traced from the label mask through ThresholdToSelection now, scaled by the binning, and carry the cursor's colour. Verified: a half-filled 4x4 mask traces to a bounds 2x4 region of exactly 8 pixels, scales to 8x16 at binning 4, and a cursor that caught nothing returns nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two things stopped the second region appearing. The draw toggle cleared its own flag after each outline but stayed visually selected, so the next click only deselected it and you had to press it twice. The toggle governs now, and it stays on until you turn it off, which is what drawing several regions wants. The label image can hold one value per pixel, and label_image assigns each cursor only where nothing has claimed a pixel yet, so any region overlapping an earlier one came back empty. Each cursor's mask is requested on its own now, so overlap is fine: two cursors over the same population return 128 pixels each rather than 128 and nothing. Cursor ids are found rather than counted, so removing one and drawing another cannot reuse an id, and the region names follow the cursor. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The mask request hardcoded min_photons 1.0 while the density query used the server default of 0.01, so the plot showed pixels the mask could never select. On a dim file that is nearly all of them: Ado_1.ptu has a median of 0.015 photons per binned pixel and 65426 of 65536 below 1.0, so three cursors placed on visible density returned 78, 0 and 46 pixels where the plot had 10848, 10273 and 17299. One floor governs both now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Eighteen tests over the parts that do not need a toolkit: the GeoJSON conversion both ways, the phasor wire format, the density decoding, the cursor summary line and the query escaping. One of them pins the photon floor at 0.01 with the reason, because a higher one hides pixels the plot draws and on a dim file that is nearly all of them. A plugin job runs mvn verify on JDK 21 and keeps the jar as an artifact, so a Java break fails the pull request rather than someone's Fiji. Co-Authored-By: Claude Opus 5 <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.
The Fiji side was a 137-line Groovy script that hardcoded a 7x5 image and posted one fixed triangle. It was an integration test, not a plugin: no menu, no window a user keeps, no way to send a region anyone drew.
Closes #4.
This is nine commands under
Plugins > FLIMKit, the same set the QuPath extension carries, built one image at a time rather than around a project. Connect, Open FLIM file, Fetch FLIMKit images, Send and Fetch ROIs, Fit ROI decays, Fit per-pixel lifetimes, Phasor plot, and Stitch and fit a mosaic.File > Openhandles.ptu,.sdt,.photonsand the rest through an IOPlugin, and opening a file registers it so fitting works straight afterwards.Every fit opens its numbers beside its image. Per-pixel shows the global summed fit that seeds it, stitch shows its own summary, and ROI fits get a Results table.
The phasor draws density and the universal semicircle in Swing. Click to place a cursor, drag to move it, Draw region to trace an outline, Add to ROI Manager to bring the pixels back as traced selections.
Maven against the jars Fiji already ships, everything
provided, nothing shaded. Eighteen Java tests cover the logic that needs no toolkit, four Python tests drive a real headless Fiji, and a CI job runsmvn verifyon JDK 21 and keeps the jar as an artifact.I tested this against real files as it was built. Five bugs came out of that rather than out of seeded fixtures:
IOPlugin.supportsOpen(Location)defaults to false andAbstractIOPluginonly overrides theStringvariant, soFile > Opennever matched and reported no opener for a FileLocation.tile_fitoutput is not a stitched output directory, so it cannot be reopened as a dataset. The real-unit TIFFs in the summary are opened instead.Stitch and fit was run against a real mosaic. The rest was driven against Ado_1.ptu and convallaria.ptu.
🤖 Generated with Claude Code