Skip to content

render_labels crashes with cryptic skimage TypeError when label array has float dtype #606

@timtreis

Description

@timtreis

render_labels crashes with cryptic skimage TypeError when label array has float dtype

Description

Labels2DModel.parse accepts float32 arrays without error — this is common output from ML segmentation pipelines. When render_labels is subsequently called on such an element, it crashes deep in _map_color_seg with TypeError: The dtype of an array to be remapped should be integer. from skimage internals. The traceback contains no mention of the element name, the offending dtype, or how to fix the problem.

Environment

spatialdata-plot: 0.3.4.dev (main, 5cfedc7)
spatialdata: 0.5.0
Python: 3.13

Minimal Reproducible Example

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
import spatialdata as sd
from spatialdata.models import Labels2DModel
import spatialdata_plot

# Labels from ML model output — commonly float32
labels = np.zeros((20, 20), dtype=np.float32)
labels[3:8, 3:8] = 1.0
labels[12:17, 12:17] = 2.0

sdata = sd.SpatialData(labels={"lbl": Labels2DModel.parse(labels, dims=["y", "x"])})
fig, ax = plt.subplots()
sdata.pl.render_labels("lbl").pl.show(ax=ax)

Actual Output

TypeError: The dtype of an array to be remapped should be integer.

The full traceback runs through skimage/util/_map_array.py with no spatialdata-plot frame visible. No mention of the element name, its dtype, or what to do about it.

Expected Output

A clear, actionable error at the entry point to _render_labels:

ValueError: Label element 'lbl' has dtype float32. Label arrays must use an integer
dtype (e.g. int32 or uint16). Cast before plotting:
    sdata['lbl'] = sdata['lbl'].astype(np.int32)

Fix Sketch

In _render_labels, after the label array is loaded and squeezed, add a dtype guard before any downstream processing:

if np.issubdtype(label.values.dtype, np.floating):
    raise ValueError(
        f"Label element '{element_name}' has dtype {label.values.dtype}. "
        "Label arrays must use an integer dtype (e.g. int32 or uint16). "
        "Cast before plotting:\n"
        f"    import numpy as np\n"
        f"    sdata['{element_name}'] = sdata['{element_name}'].astype(np.int32)"
    )

Alternatively, auto-cast with a UserWarning instead of raising — this trades strictness for convenience when values are small whole-number floats. Raising is the safer default since silent casting may hide upstream pipeline bugs.


Triage tier: Tier 2

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions