Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test all label rendering options #207

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added tests/_images/Labels_can_render_no_fill.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
174 changes: 174 additions & 0 deletions tests/pl/test_render_labels.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import dask.array as da
import matplotlib
import numpy as np
import pandas as pd
import pytest
import scanpy as sc
import spatialdata_plot # noqa: F401
from anndata import AnnData
from matplotlib.colors import LogNorm, Normalize
from spatial_image import to_spatial_image
from spatialdata import SpatialData
from spatialdata.models import Labels2DModel, TableModel

from tests.conftest import PlotTester, PlotTesterMeta

Expand Down Expand Up @@ -71,5 +77,173 @@ def test_plot_can_stack_render_labels(self, sdata_blobs: SpatialData):
.pl.show()
)

def test_plot_can_render_no_fill(self, sdata_blobs: SpatialData):
(
sdata_blobs.pl.render_labels(
elements="blobs_labels", fill_alpha=0, outline_alpha=1, outline=True, contour_px=10
).pl.show()
)

def test_can_render_no_fill_no_outline(self, sdata_blobs: SpatialData):
# This passes only with outline_alpha=0
(
sdata_blobs.pl.render_labels(
elements="blobs_labels",
fill_alpha=0,
outline=False,
).pl.show()
)
self.compare("Labels_can_render_no_fill_no_outline", tolerance=5)

def test_plot_can_color_labels_by_continuous_variable(self, sdata_blobs: SpatialData):
sdata_blobs.pl.render_labels("blobs_labels", color="channel_0_sum").pl.show()


@pytest.fixture
def sdata():
box = np.zeros((10, 10), dtype=int)
box[2:-2, 2:-2] = 1

labels_dct = {
"empty_labels": Labels2DModel.parse(np.zeros((1, 3)), dims=("y", "x")),
"one_label": Labels2DModel.parse(np.array([[0, 0, 1]]), dims=("y", "x")),
"labels1": Labels2DModel.parse(np.arange(3).reshape((1, 3)), dims=("y", "x")),
"skipped_label": Labels2DModel.parse(np.array([[0, 1, 3]]), dims=("y", "x")),
"no_background": Labels2DModel.parse(np.arange(1, 3 + 1).reshape((1, 3)), dims=("y", "x")),
"box": Labels2DModel.parse(box, dims=("y", "x")),
}
obs = pd.DataFrame(
[
["one_label", 1, np.pi, True, "a", "cat"],
["two_labels", 1, 0.1, True, "a", "cat"],
["two_labels", 2, 0.9, False, "b", "dog"],
["skipped_label", 1, 1.0, True, "a", "rat"],
["skipped_label", 3, np.nan, False, "b", "rat"],
["no_background", 1, 1.0, True, "a", "dog"],
["no_background", 2, 10.0, False, "b", "rat"],
["no_background", 3, 100.0, True, "b", "cat"],
["box", 1, np.pi, True, "a", "cat"],
],
columns=[
"region",
"instance_id",
"continuous",
"boolean",
"string",
"categorical",
],
)
obs["categorical"] = obs["categorical"].astype("category")
return SpatialData(
labels=labels_dct,
table=TableModel.parse(
AnnData(obs=obs),
region=obs["region"].unique().tolist(),
region_key="region",
instance_key="instance_id",
),
)


@pytest.mark.parametrize(
"elements",
[
["empty_labels"], # exception
["one_label"], # correct
["two_labels"], # incorrect, empty plot
["skipped_label"], # correct (but different labels plotted with same color)
["no_background"], # internal exception, incorrect plot (label values 2+3 same color)
],
)
def test_can_handle_different_labels_images(sdata: SpatialData, elements: list[str]):
sdata.pl.render_labels(elements=elements).pl.show()
# TODO: Assertions or comparison with expected image


@pytest.mark.parametrize(
("elements", "options"),
[
# Render nothing
([], dict()), # correct
# Render with defaults
(["two_labels"], dict()), # empty plot
# Render labels with color by continuous value, defaults
(["two_labels"], dict(color="continuous")), # empty plot
# Render labels with color by continuous value and NaN
(["skipped_label"], dict(color="continuous", na_color="magenta")), # exception: Not all values are color-like.
(
["skipped_label"],
dict(color="continuous", na_color=(1.0, 0.0, 1.0, 1.0)),
), # exception: Not all values are color-like.
# Render labels with color by continuous value, fill alpha
(["two_labels"], dict(color="continuous", fill_alpha=0.0)), # empty plot
(["two_labels"], dict(color="continuous", fill_alpha=1.0)), # empty plot
# Render labels with color map, defaults
(["two_labels"], dict(color="continuous", cmap="viridis")), # empty plot
(["two_labels"], dict(color="continuous", cmap=matplotlib.colormaps["plasma"])), # empty plot
# Render labels with color map, with norm
(
["two_labels"],
dict(color="continuous", cmap="viridis", norm=Normalize(vmin=0.1, vmax=0.9)),
),
(["two_labels"], dict(color="continuous", cmap="viridis", vmin=0.1, vmax=0.9)),
(
["no_background"],
dict(color="continuous", cmap="viridis", norm=LogNorm(vmin=1, vmax=100)),
),
# Render labels with color by non-continuous values, defaults
(["two_labels"], dict(color="boolean")),
(["two_labels"], dict(color="string")),
(["two_labels"], dict(color="categorical")),
# Render labels with color by non-continuous values, defaults
(["two_labels"], dict(color="boolean")),
(["two_labels"], dict(color="string")),
(["two_labels"], dict(color="categorical")),
# Render only groups from a categorical
(["no_background"], dict(color="categorical", groups=["cat", "dog"])),
# Render categorical as continuous with colormap
(["no_background"], dict(color="categorical", cmap="viridis")),
# Render categorical with palette, one value
(["one_label"], dict(color="boolean", palette="red")),
(["one_label"], dict(color="string", palette="red")),
# Render categorical with palette, multiple values
(["two_labels"], dict(color="boolean", palette="red")),
(["two_labels"], dict(color="boolean", palette=["red", "lime"])),
(["two_labels"], dict(color="string", palette=["red", "lime"])),
(["two_labels"], dict(color="categorical", palette=["red", "lime"])),
# Render categorical with palette, one category
(["one_label"], dict(color="categorical", palette="red")),
(["skipped_label"], dict(color="categorical", palette="red")),
# Render categorical with palette, multiple categories
(["two_labels"], dict(color="categorical", palette="red")),
(["no_background"], dict(color="categorical", palette="red")),
],
)
def test_can_render_labels_fill_with_options(sdata: SpatialData, elements: list[str], options: dict):
sdata.pl.render_labels(elements=elements, **options).pl.show()
# TODO: Assertions or comparison with expected image


@pytest.mark.parametrize(
("elements", "options"),
[
# Render outline with defaults
(["box"], dict(outline=True)), # correct
# Render outline with no fill
(["box"], dict(outline=True, fill_alpha=0.0)), # correct
# Render outline with different width
pytest.param(["box"], dict(outline=True, contour_px=0), marks=pytest.mark.xfail(reason="not supported")),
(["box"], dict(outline=True, contour_px=1)),
(
["box"],
dict(outline=True, contour_px=5),
), # incorrect, black outline 1, additonal magenta outline (apparently it depends on labels image size)
# Render outline with alpha
(["box"], dict(outline=True, outline_alpha=0.0)), # incorrect, outline shown with ~0.5 alpha
(["box"], dict(outline=True, outline_alpha=0.5)), # incorrect, outline shown with ~1.0 alpha
(["box"], dict(outline=True, outline_alpha=1.0)), # correct
],
)
def test_can_render_labels_outline_with_options(sdata: SpatialData, elements: list[str], options: dict):
sdata.pl.render_labels(elements=elements, **options).pl.show(dpi=200)
# TODO: Assertions or comparison with expected image
Loading