Skip to content

Fix: labels_to_shapes never drops a label that simplification collapses (F14) - #43

Merged
guijacquemet merged 2 commits into
mainfrom
claude/fix-shapes
Oct 8, 2026
Merged

guijacquemet merged 2 commits into
mainfrom
claude/fix-shapes

Conversation

@guijacquemet

Copy link
Copy Markdown
Contributor

With the default simplify=0.5, objects whose outline collapsed under simplification (a single pixel, 1x3 bars at simplify=1.0) were silently dropped, so labels_to_shapes returned [] for an image of single-pixel objects.

Now every label with at least min_area pixels gets exactly one feature. In _rings_of, a ring whose simplified version has fewer than 4 points keeps its un-simplified contour; this is per ring, so a collapsed hole stays a hole. If a label somehow has no polygon, labels_to_shapes raises ToolError("bad_return", ...) instead of skipping it (unreachable today; a safety net against silent drops).

One deviation from the brief, for your decision: a single pixel is a 4-vertex diamond (vertices (x, y±0.5) and (x±0.5, y), area 0.5), not a unit square, because find_contours at level 0.5 always cuts convex corners. That is already what simplify=0 produced, so one image can mix cut-corner and pixel-corner outlines. Documented in the docstring. Exact unit squares would mean changing the ring tracing for every outline (coordinates would change for all users), so it is not done here.

Docs: MANIFEST.md (ShapesOut row) and REGRESSION_LEDGER.md. New tests/test_shapes_every_label.py (5 tests: a Hypothesis property over simplify in {0, 0.5, 1, 3} and min_area 1..6 that every label with at least min_area pixels appears exactly once with the right area and closed rings; unsimplified area within the corner-cut tolerance and the outline rasterises back to the mask exactly; the single-pixel diamond; 25 single-pixel labels; a collapsed hole is kept). F14 is removed from the known failures, so the matrix case outline_labels/single-pixel-default-simplify now runs. No existing test edited (a known_failure decorator and its now-unused import were removed from one test).

Host checks to do in the host-parity work: QuPath: a diamond has JTS area 0.5 while the area property says 1 (read the property for pixel counts); Fiji: drop the closing duplicate vertex before building the PolygonRoi; Napari: a shapes layer takes a 4-vertex polygon; all hosts: images with many tiny objects now produce one feature per label, so apply the 50 000 cap and "left out" message the same way everywhere. Separate docs note: docs/HOST_FEATURES.md line 30 ("Not yet in any host: shapes outputs, ...") contradicts line 25 and is stale; it goes to the documentation audit.

Verified by the author on Linux, Python 3.11: black, ruff, mypy; the full suite has only the two environmental sandbox failures; I re-ran lint and the shapes, geometry, known-failure and test_shapes suites (48 tests OK) and checked the single-pixel output. Not verified: coverage floor (CI runs it), Windows, macOS.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Y2SN8SuEakTdiWy3F2WUqN


Generated by Claude Code

claude added 2 commits October 8, 2026 13:32
…ollapses (F14)

A ring that simplification would leave with fewer than 3 vertices keeps its un-simplified outline.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2SN8SuEakTdiWy3F2WUqN
@guijacquemet
guijacquemet merged commit 401357d into main Oct 8, 2026
14 checks passed
@guijacquemet
guijacquemet deleted the claude/fix-shapes branch October 14, 2026 16:09
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.

2 participants