Skip to content

Fix/degenerate object shapes - #35

Merged
annmoel merged 2 commits into
mainfrom
fix/degenerate-object-shapes
Sep 4, 2026
Merged

Fix/degenerate object shapes#35
annmoel merged 2 commits into
mainfrom
fix/degenerate-object-shapes

Conversation

@annmoel

@annmoel annmoel commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

annmoel and others added 2 commits September 4, 2026 14:35
…verage

A single isolated foreground pixel (a perfectly valid object, e.g. from a
noisy segmentation) crashed the whole pipeline: _analyze_single_object's
empty-skeleton guard only checked skeleton.any(), which is true for a lone
pixel, so it fell through into build_vessel_graph -> skan.Skeleton(),
which requires at least one graph edge and raises a low-level scipy.sparse
ValueError ("index pointer size 0 should be 1") otherwise - the same
symptom as the fully-collapsible-junction crash fixed in PR #29, but via a
different, previously-untested path.

Replaced the three "if not skeleton.any()" guards (initial thinning,
post-junction-cleanup, post-spur-pruning) with a single
_skeleton_has_no_branches() check: true whenever every connected component
in the skeleton is a single isolated pixel, i.e. there's no possible graph
edge - a strict generalization of the all-background case, so it subsumes
the existing guards rather than adding a fourth condition alongside them.

Also adds the two categories of test coverage identified as missing
without a fix needed:
- prune_spurs's pipeline integration (spur_pruning.py's own unit tests
  cover the function in isolation, but nothing exercised the repeated-
  iteration loop, the graph rebuild between iterations, or the
  pruned-everything-away early return at the pipeline level). Uses a
  "Y of Y" fixture where removing one spur exposes a second, previously
  junction-to-junction branch as a new spur only a second iteration can see.
- _iter_object_crops' bounding-box clamp, forced to actually engage by an
  object whose shape touches every edge of the array.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…split

tests/test_thin_2d.py's own docstring already pointed here ("Correctness
against scikit-image's own Lee thinning is covered separately in
tests/test_2d_skimage_comparison.py"), but the file doesn't actually exist
on main - it was deleted as collateral in e0910f0 (the vesskel->maskel
rename and three-repo split) because its old version depended on the HRF
dataset loader, which moved to maskel-evaluations in that same commit.

This is a from-scratch replacement using a bundled scikit-image sample
image (skimage.data.horse(), a real non-synthetic silhouette) instead of
the HRF dataset, so it needs no external data and runs in CI exactly like
tests/test_3d_skimage_comparison.py already does. Also fixes a bug the old
version didn't have to deal with: skimage.morphology.skeletonize defaults
to Zhang's algorithm for 2D input (Lee only for 3D), so method="lee" has
to be requested explicitly for this to actually compare against the same
algorithm lee94_thin implements - a first draft of this file without that
failed with a genuine skeleton mismatch, not a false pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@annmoel
annmoel merged commit 1adec01 into main Sep 4, 2026
1 check passed
@annmoel
annmoel deleted the fix/degenerate-object-shapes branch September 4, 2026 12:39
annmoel added a commit that referenced this pull request Sep 4, 2026
Fix test collection broken by merging PRs #32 and #35 together
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