Skip to content

fix: read a background-inclusive one-hot correctly in _foreground - #115

Open
Hendrik-code wants to merge 1 commit into
mainfrom
hm/fix-onehot-convention
Open

Hendrik-code wants to merge 1 commit into
mainfrom
hm/fix-onehot-convention

Conversation

@Hendrik-code

Copy link
Copy Markdown
Collaborator

The defect

The package carries two one-hot conventions:

helper convention
collapse_onehot_to_index (gpu/fromSeg.py) background implicit — channel c is label c + 1, background voxels are all-zero
seg_region_masks (gpu/fromSeg.py) background included — one channel per distinct value, its own docstring says "background included"

_foreground (gpu/contrast.py) is written for the first. Given the second, the background channel covers exactly the voxels the foreground channels do not, so amax > 0 is True at every voxel:

background-inclusive one-hot: fraction foreground = 1.0     <- wrong
background-implicit one-hot : fraction foreground = 0.5
single-channel label map    : fraction foreground = 0.5

With an all-True foreground, in_seg applies the transform to the whole patch and out_seg to none of it — which is word for word the failure _foreground's own docstring says it was written to fix, in the one layout it was not checked against. unit_tests/test_seg_layout.py builds exactly this shape (for v in range(5)), and seg_region_masks produces it for every single-channel label map.

The fix

A background-inclusive one-hot is recognisable without guessing at label values: it is multi-channel and leaves no voxel unset, because the background channel covers whatever the foreground channels do not. A background-implicit one-hot always has all-zero background voxels unless the patch is labelled edge to edge, which a real segmentation patch is not. Single-channel input is never second-guessed.

Tests

unit_tests/test_foreground_layouts.py asserts all four layouts give the same answer for the same anatomy — label map, background-implicit one-hot, background-inclusive one-hot, and the actual output of seg_region_masks, since one helper feeds the other's layout. Plus an empty mask (no foreground) and a fully labelled single-channel map (all foreground, not second-guessed).

Verified failing before the fix (2 failed — the two background-inclusive cases) and passing after. Full suite unchanged against baseline; ruff check . clean.

🤖 Generated with Claude Code

The package carries two one-hot conventions. `collapse_onehot_to_index`
documents background as implicit -- channel c is label c + 1, background voxels
are all-zero. `seg_region_masks` emits one channel per distinct value for a
label map, background included, and test_seg_layout.py builds the same shape.

`_foreground` was written for the first. Given the second, the background
channel covers exactly the voxels the foreground channels do not, so `amax > 0`
is True at every voxel: measured, fraction foreground 1.0 instead of 0.5. That
made `in_seg` apply the transform to the whole patch and `out_seg` to none of
it, which is the precise failure the function's docstring says it exists to fix
-- in the one layout it was not checked against.

A background-inclusive one-hot is recognisable without guessing at values: it is
multi-channel and leaves no voxel unset. A background-implicit one-hot has
all-zero background voxels unless the patch is labelled edge to edge, which a
real segmentation patch is not. Single-channel input is never second-guessed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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