Skip to content

render_images(norm=LogNorm()) crashes when image contains zeros #604

@timtreis

Description

@timtreis

render_images(norm=LogNorm()) crashes when image contains zeros

Description

Calling render_images("img", norm=LogNorm()) on any image that contains zero-valued pixels crashes when drawing the colorbar. The same call with colorbar=False succeeds. colorbar="auto" is the default for norm-based image renders, so this crash is hit without any unusual arguments.

The error is ValueError: Invalid vmin or vmax from 15+ frames inside matplotlib's colorbar constructor. No spatialdata-plot frame appears in the traceback, making the failure opaque to users. The actual problem — LogNorm cannot represent zero — is straightforward to catch early.

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 Image2DModel
from matplotlib.colors import LogNorm
import spatialdata_plot

img = np.array([[[0, 0.5, 1.0, 0.3, 0.8]] * 5] * 5, dtype=np.float32).T[:1]
sdata = sd.SpatialData(images={"img": Image2DModel.parse(img, c_coords=["DAPI"])})

# Works — zeros are masked, colorbar suppressed
fig, ax = plt.subplots()
sdata.pl.render_images("img", norm=LogNorm(), colorbar=False).pl.show(ax=ax)

# Crashes — colorbar="auto" is the default for norm-based renders
fig, ax = plt.subplots()
sdata.pl.render_images("img", norm=LogNorm()).pl.show(ax=ax)

Actual Output

ValueError: Invalid vmin or vmax

Full traceback is 15+ frames inside matplotlib's colorbar constructor (lib/matplotlib/colorbar.py). No spatialdata-plot frame visible. No mention of LogNorm, zeros, or how to work around the issue.

Expected Output

Either:

  • Colorbar skipped with a UserWarning: "Data contains zeros or negative values; colorbar suppressed for LogNorm. Use colorbar=False to silence this warning."
  • Or a clear ValueError raised at the point spatialdata-plot requests the colorbar: "Cannot draw colorbar for LogNorm when data contains zeros or negative values. Set colorbar=False or clip the data."

Fix Sketch

Option A (preferred): early guard in _draw_colorbar (basic.py ~line 1162)

Before calling fig.colorbar(...), check whether the norm is log-scale-sensitive and data contains non-positive values:

from matplotlib.colors import LogNorm, SymLogNorm

if isinstance(norm, (LogNorm, SymLogNorm)) and data.min() <= 0:
    warnings.warn(
        "Data contains zeros or negative values; colorbar suppressed for LogNorm. "
        "Use colorbar=False to silence this warning.",
        UserWarning,
        stacklevel=3,
    )
    return

Option B: wrap in try/except with re-raise

try:
    fig.colorbar(...)
except ValueError as e:
    raise ValueError(
        f"Failed to draw colorbar: {e}. "
        "If using LogNorm, ensure data contains no zeros or negative values, "
        "or set colorbar=False."
    ) from e

Option A provides a better user experience by surfacing the problem before entering matplotlib internals.


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