Repository navigation
Scale auto-selection, skeleton radius/prune-only/morphology, dask scratch fix - #82
Merged
Merged
Conversation
ImageDataInterface now resolves a multiscale group path to a concrete scale-level array instead of failing to open it: - No target voxel size given -> finest level (s0), so a bare group path defaults to full resolution. - A target voxel size given -> the level whose voxel size best matches (exact preferred, ties broken toward finer), minimizing resampling. measure passes the input segmentation's voxel size as the target when opening raw_path, so raw intensities line up with the segmentation grid. The selected level is logged in every case (including precomputed's existing default-to-scale-0).
When resolving a multiscale group against a target voxel size, pick the coarsest level that is still at or finer than the target so the source is only ever downsampled onto the target grid, never upsampled (which would fabricate detail by repetition). Fall back to the finest level only when every level is coarser than the target.
NGFF >=0.5 nests multiscales under an 'ome' key in the attrs (attrs['ome']['multiscales']) rather than at the top level. Add a get_multiscales() normalizer that reads either layout and route both the new scale-resolution helper and the existing OME voxel-size/translation readers through it, so group paths resolve and voxel sizes are read correctly for 0.5 datasets.
The full neuroglancer skeletons now carry the EDT-sampled radius as a float32 'radius' vertex attribute (declared in the full info file), so neuroglancer can read and color by per-vertex thickness. Radii are attached in the np.argwhere voxel order that produced the vertices, so they stay aligned, and are set after prune/simplify so the simplified skeletons remain geometry-only. The single seed-vertex fallback also carries its radius.
…alse) Per-vertex radius output is now opt-in via the write_vertex_radius parameter (default False). When off, full skeletons declare no vertex attributes and carry no radius bytes; when on, full skeletons get the float32 radius attribute as before. Threaded through the worker and info writer so the declaration and the encoded bytes stay consistent.
…ites) Verify that passing a bare multiscale group path (no scale level) to the skeletonize and contact-site readers auto-resolves to s0, confirming the centralized ImageDataInterface resolution covers these tools.
set_local_directory used os.makedirs(exist_ok=True) to vet each candidate, which succeeds even when the directory already exists but isn't writable (e.g. /scratch/<user> left by another job). The code then committed to it and a dask worker later crashed creating <dir>/dask-scratch-space with PermissionError, never reaching the /tmp/<user> fallback. Probe real writability by creating and removing a temp subdir inside each candidate, so an existing-but-unwritable dir is skipped. Add an optional candidate_dirs arg for testability and a regression test.
# Conflicts: # tests/operations/test_skeletonize.py
prune_only=True makes the second skeleton output a prune-only skeleton (short terminal branches removed, every surviving original vertex kept -- no geometric simplification), written to a pruned/ directory instead of simplified/. tolerance_nm is ignored in this mode. Radii are now attached before prune(), which carries each surviving node's exact original EDT radius through (graph_to_skeleton rebuilds vertices and radii from the same node set), so the prune-only output carries faithful per-vertex radii when write_vertex_radius is set -- the full/prune-only info files declare the radius attribute, simplified does not. The second output dir name is threaded through info, segment properties, sharding, and emit.
radius_mean_nm/radius_std_nm now describe the pruned skeleton, consistent with num_branches and longest_shortest_path_nm (also pruned). Radii are attached before prune() unconditionally so the pruned radii are available for stats regardless of write_vertex_radius (which still only controls whether radii are written into the skeleton bytes). Falls back to the full sampled radii if pruning dropped them.
Add a morphological_operations parameter: an ordered sequence of ops
applied to each object's mask before skeletonization. Supports erosion,
dilation, opening, and closing (with iterations + 6/18/26 connectivity),
plus the existing corner-bridge removal ('6'/'18'). Supersedes the legacy
erosion shorthand, which is mapped into the equivalent op list for
backward compatibility.
Radii are measured on the union of the original and processed masks, so
shrinking ops (erosion/opening) keep true-object radii while growing ops
(dilation/closing) reflect the grown/hole-filled structure. This lets you
fill holes (closing), drop thin protrusions/specks (opening), or thicken
thin structures (dilation) per object, addressing cases erosion alone
couldn't.
The morphological-op connectivity field now uses the same 1/2/3 rank convention as the rest of the codebase (1=faces/6, 2=faces+edges/18, 3=faces+edges+corners/26) instead of a third 6/18/26 convention. Default is 1 (faces), preserving the legacy erosion=True behavior (6-connectivity cross) and matching the standard minimal structuring element for erosion/dilation. The corner-bridge ops keep their 6/18 names, since there the number is the connectivity being enforced (what remove_unbridged_adjacencies accepts), not a structuring-element rank.
Group.create_dataset was removed in newer zarr (3.2.x on CI); switch the test helper to create_array.
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.
@stuarteberg
Summary
Four independent improvements, bundled on one branch.
1. Auto-select scale level when reading a multiscale group path
/s0) to a reader now resolves to a concrete scale level instead of failing, and logs the choice.measure'sraw_path→ the level whose voxel size best matches the segmentation, preferring a finer level over upsampling.attributes.ome.multiscales, not only top-levelmultiscales).2. Per-vertex skeleton radius (opt-in)
write_vertex_radius(defaultFalse). When enabled, thefull/(andpruned/) skeletons carry a float32radiusvertex attribute (declared ininfo) that neuroglancer can color by. The geometrically-simplified output stays attribute-free.3. Skeletonize:
prune_onlyoutput + generalized morphologyprune_only(defaultFalse): the second output becomes a prune-only skeleton written topruned/(short terminal branches removed, every surviving original vertex + its exact radius kept, no geometric simplification).tolerance_nmis ignored in this mode.radius_mean_nm/radius_std_nm) are now computed on the pruned skeleton, consistent withnum_branchesandlongest_shortest_path_nm.morphological_operations: an ordered sequence applied to each object's mask before skeletonization —erosion/dilation/opening/closing(withiterationsandconnectivityrank 1/2/3) plus the existing corner-bridge removal ("6"/"18"). Supersedes the legacyerosionshorthand, which is mapped into the equivalent op list for backward compatibility. Radii are measured on the union of the original and processed masks, so shrinking ops keep true-object radii while growing ops reflect the grown/hole-filled structure.4. Dask local-directory writability fix
set_local_directorynow probes actual writability (creates/removes a temp subdir) instead of relying onos.makedirs(exist_ok=True), which silently succeeds on an existing-but-unwritable dir. This prevents committing to an unwritable/scratch/<user>and then crashing a worker ondask-scratch-space; it now falls through to/tmp/<user>.Adds tests for scale resolution, dask local-directory selection, per-vertex radius,
prune_only, and morphological operations.