From 65d48793eeb3a2dc0643e42df7b006f5bf4c85a3 Mon Sep 17 00:00:00 2001 From: d33bs Date: Mon, 24 Aug 2026 16:15:50 -0600 Subject: [PATCH 1/3] add texture distance data warning --- src/zedprofiler/featurization/texture.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/zedprofiler/featurization/texture.py b/src/zedprofiler/featurization/texture.py index 30813b6..3595002 100644 --- a/src/zedprofiler/featurization/texture.py +++ b/src/zedprofiler/featurization/texture.py @@ -7,7 +7,7 @@ We want this module to be python api callable and scalable. """ -import contextlib +import warnings import mahotas import numpy @@ -161,7 +161,7 @@ def compute_texture( # noqa: C901 continue image_object[~object_mask] = 0 image_object = scale_image(image_object, num_gray_levels=grayscale) - with contextlib.suppress(ValueError): + try: # calculates 13 Haralick features for each direction (13) # and each object, and stores them in a 3D array features[:, :, idx] = mahotas.features.haralick( @@ -170,6 +170,15 @@ def compute_texture( # noqa: C901 distance=distance, compute_14th_feature=False, ) + except ValueError: + # mahotas cannot compute GLCM features when an object's extent + # is smaller than the distance parameter; the object's texture + # values remain NaN in this case. + warnings.warn( + f"Object {label} is smaller than distance={distance}; " + "Texture features are undefined (NaN) for this object.", + stacklevel=2, + ) # iterate through the direction, feature, and object dimensions # of the features array to populate the output dictionary for direction, direction_features in enumerate(features): From 474573f127adb08fc3caa17ca21bbe4da18e4a76 Mon Sep 17 00:00:00 2001 From: d33bs Date: Mon, 24 Aug 2026 16:19:39 -0600 Subject: [PATCH 2/3] add test --- tests/featurization/test_texture.py | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/tests/featurization/test_texture.py b/tests/featurization/test_texture.py index 7f9c02e..f740ce5 100644 --- a/tests/featurization/test_texture.py +++ b/tests/featurization/test_texture.py @@ -121,6 +121,38 @@ def test_value_error_in_one_object_does_not_corrupt_others() -> None: assert np.isnan(obj2_asm), "Object 2 (single pixel) should have NaN texture values" +def test_undersized_object_warns() -> None: + """An object smaller than the distance parameter must trigger a warning. + + mahotas cannot compute GLCM features for an object below a minimum pixel + extent for the given distance; this is expected, but it must not happen + silently. Regression test for compute_texture previously swallowing the + resulting ValueError via contextlib.suppress with no warning at all. + """ + shape = (20, 20, 20) + image = np.zeros(shape, dtype=np.uint8) + label = np.zeros(shape, dtype=np.int32) + + # single pixel — no neighbour pairs → ValueError from mahotas + image[15, 15, 15] = 100 + label[15, 15, 15] = LABEL_OBJ_2 + + imgset = ImageSetLoaderModel() + loader = ObjectLoaderModel( + image=image, + label_image=label, + object_ids=[LABEL_OBJ_2], + image_set_loader=imgset, + ) + + with pytest.warns(UserWarning, match=r"Object 2 is smaller than distance=1"): + df = compute_texture(loader, distance=1, grayscale=256) + + asm_cols = [c for c in df.columns if "AngularSecondMoment" in c and "-00-" in c] + assert asm_cols + assert np.isnan(df[asm_cols[0]].iloc[0]) + + def test_texture_values_correct_per_object() -> None: """Each object must receive its own Haralick values, not another object's. From 312211160e418e9f9b3e8a72229d69c377771744 Mon Sep 17 00:00:00 2001 From: d33bs Date: Thu, 27 Aug 2026 13:16:11 -0600 Subject: [PATCH 3/3] ignore lint --- src/zedprofiler/featurization/texture.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zedprofiler/featurization/texture.py b/src/zedprofiler/featurization/texture.py index a5b8e55..5503e8c 100644 --- a/src/zedprofiler/featurization/texture.py +++ b/src/zedprofiler/featurization/texture.py @@ -112,7 +112,7 @@ def resample_to_isotropic( ) -def compute_texture( # noqa: C901 +def compute_texture( # noqa: C901, PLR0915 object_loader: ObjectLoader, distance: int = 1, grayscale: int = 256,