Skip to content

Ignore class_values entries absent from the input array#2

Merged
wrignj08 merged 1 commit into
mainfrom
fix/absent-class-values
Jul 21, 2026
Merged

Ignore class_values entries absent from the input array#2
wrignj08 merged 1 commit into
mainfrom
fix/absent-class-values

Conversation

@wrignj08

Copy link
Copy Markdown
Contributor

Problem

clean_array raised KeyError when class_values named a class that does not occur in the input array — but only when smooth_edge_size > 0:

a = np.full((64, 64), 254, np.uint8); a[10:50, 10:50] = 1   # only class 1 present
clean_array(a, class_values=[0, 1], smooth_edge_size=3, ...)  # KeyError: 0
clean_array(a, class_values=[0, 1], smooth_edge_size=0, ...)  # fine

The smooth path in smooth_edges_to_codes did an unguarded value_to_code[cv_], and value_to_code is built solely from np.unique(array). The two sibling call sites already guarded this — multiclean.py filters target_codes on membership, and the background loop uses .get with a None check — so this was a missed case, not intentional strictness.

This mainly affects tiled processing, where one fixed class list is reused across tiles whose contents vary. The failure depends on the data, so it surfaces intermittently.

Fix

Filter target_class_values against the values present in the array once at the entry point. That covers all three call sites and skips the wasted cv2.morphologyEx call for a class whose mask is entirely zeros. background_class_values is derived as set(all) - set(target), so it is unaffected.

Tests

Added parametrized sweeps over dtype × smooth_edge_size × min_island_size × class_values. The existing point tests missed this because the bug lived in one corner of that grid, so the new tests assert invariants across it:

  • absent classes never raise and never change the result
  • if every requested class is absent, the array is returned untouched
  • shape/dtype preserved and output values are always a subset of input values

Verified by reverting the fix: 121 of the new cases fail without it. Suite is 322 passed in ~1.7s.

🤖 Generated with Claude Code

clean_array raised KeyError when class_values named a class that does
not occur in the array, but only when smooth_edge_size > 0. The smooth
path in smooth_edges_to_codes looked up value_to_code[cv_] unguarded,
while value_to_code is built solely from np.unique(array). The two
sibling call sites already guarded this (multiclean.py target_codes
filters on membership; the background loop uses .get), so this was a
missed case rather than intentional strictness.

Filter target_class_values against the values present in the array once
at the entry point, which covers all three call sites and skips the
wasted morphological opening for a class whose mask is all zeros.
background_class_values is derived as set(all) - set(target), so it is
unaffected.

Add parametrized sweeps over dtype x smooth_edge_size x min_island_size
x class_values. The original point tests missed this because the bug
lived in one corner of that grid. Verified by reverting the fix: 121 of
the new cases fail without it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@wrignj08
wrignj08 merged commit 1f15b53 into main Jul 21, 2026
2 checks passed
@wrignj08
wrignj08 deleted the fix/absent-class-values branch July 21, 2026 05:57
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