Conversation
PaulHax
added this pull request to stack #965
September 21, 2026 20:08
✅ Deploy Preview for volview-dev ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
This was
linked to
issues
Sep 21, 2026
PaulHax
force-pushed
the
processing-labelmap-packing
branch
from
September 21, 2026 21:53
16f7be4 to
30b6191
Compare
Composed labelmaps use unsigned 8-bit voxels for up to 255 labels and unsigned 16-bit voxels for 256 through 65535. An imported 16-bit labelmap splits into byte masks in one sweep. Fractional, NaN and infinite voxel values are excluded during import.
A labelmap input stages the overlap-free parts of a segmentation, one file each. An input that accepts multiple files receives every mask across as many files as overlap requires. A single-file input receives the non-conflicting subset that fits one file, starting from a segment the image holds, and the user is told which masks were left out. No mask is clipped. An empty segmentation is left unbound, an unconfirmed preview survives a task that stages no labelmap, and the overlap sweep no longer reruns on every segment selection.
Every segment actor is drawn at an opacity just under 1, so vtk.js puts it in the order-independent translucent pass with depth writes off. Overlapping segments therefore blend to the same colour whichever one comes first in the registry, and no amount of reordering makes one occlude the other. The README's "Ordering and overlap" section, the reorder handle's tooltip and the eyedropper comment all told the user that registry order decides what is drawn on top. They now describe what the order really controls: the sidebar, shortcuts, selection and picking, and flattened export precedence, and they say plainly that overlaps blend in the slice view. A new spec reads the README section and scans the segmentation and component sources so the claim cannot come back.
A result's segment list names the bins its run looked for; the labelmap carries only the ones it filled. convertImageToLabelmap merged the wire descriptions onto the descriptors the decode found in the voxels, so a declared value with no voxels and no header block created nothing, and a run that found no spleen read exactly like a run that never looked for one. Append the declared values the decode did not cover, guarding 0 for background. The seg.nrrd header path already does this, so both import paths now keep the same segments. They join the list at the decode rather than at the split, because the import pairs the masks the split returns with the descriptors it decoded by position. The declared empty gets a mask whose extent covers nothing: it draws nothing and survives the state file, which already restores a mask that covers nothing as one that covers nothing. The existing assertion that pinned the dropping is rewritten to pin the new behaviour, and now asserts the apply succeeded rather than discarding it. A segment that is declared both in the header and on the wire, or across several components, is declared once.
…file A saved labelmap read straight from the archive was decoded with no name to fall back on, so its segments came back as 'Segment 1', 'Segment 2' while the same labelmap converted live, or restored through a loaded dataset, was named after the file it arrived in. The decode now takes a base name from the caller when no loaded image can give one, and the restore passes the labelmap's own name. The parity spec compares the archive-backed catalog with the live one whole, rather than pinning the name that said nothing.
The save dialog listened for Enter on the window, so any keystroke reached it: opening the format menu with the keyboard and pressing Enter to choose an option also started the export, with the format the select had not applied yet. Nothing checked whether a save was already running either, and one keystroke arrives twice when the form submits as well, so a second run composed the same masks again and downloaded the archive a second time. The listener now sits on the dialog's card, so it hears the keystrokes that belong to this dialog and not the ones an overlay above it owns, and the save returns early while one is in flight. A new spec mounts the dialog attached to the document and stops the save where it hands its parts over, so it can count the runs one keystroke starts.
…describe A legacy manifest can describe fewer values than its labelmap carries. Restore split out only the described ones and dropped the rest. The others now get a default segment, as a live import of the same labelmap gives them.
Auto-loading skipped any job result whose declared intent the applier could not route, and nothing reached the user: the completion toast had already announced the results, and the Jobs panel still offered the file as a plain download. The skip now surfaces a warning naming the result and pointing at the download that is still available. An intent name this client does not know gets the version wording. A known name whose payload fails the strict shape union says the result does not carry the payload that intent requires. Results that declare no intent stay quiet, since there is nothing to route for them. The contract README describes the intent vocabulary version accordingly.
A 0.2.0 producer still emits the earlier intent name. The client reads it as the current one, so the producer can move to the new name afterwards.
A segmentation result is recognized as already applied through the `source` the producer stamps on it, and that field is optional on the wire. A backend that omits it left the masks with no receipt, so after a reload the re-adopted job's Load button imported every mask again, arriving as Tumor (2), Tumor (3) and so on. The client now mints the key when the result carries none, from the provider and job it submitted plus the result row's id. Nothing new goes on the wire: the minted key is scene provenance, stored on the mask binding and restored like a producer's own, and a producer-sent source still wins. A case applies a source-less result, hands its receipt back as a restore would, and shows the second apply converts nothing. Annotation results get the same minted key, so a re-applied annotation result is recognized without a wire source too.
PaulHax
force-pushed
the
processing-labelmap-packing
branch
from
September 21, 2026 22:14
30b6191 to
a0419ef
Compare
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.
Builds on #963.
Renames the segmentation result instruction, chooses the labelmap bit depth from the label count, and packs processing labelmap inputs according to the input multiplicity the task declares.
Commits:
The commits after the first three are fixes, performance work, docs and tests for processing and for the segmentation code that depends on the packing in this change. Each is its own commit with its own message.