Skip to content
Merged
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
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,13 @@ imaris = ["imaris-ims-file-reader"]
# - ipykernel < 7: napari-console requires ipykernel < 7.
# - lxml-html-clean: napari's notebook_display imports lxml.html.clean, split
# into a separate package in lxml >= 5.2 (else ImportError on Viewer()).
# - colorcet: glasbey label LUTs for view_in_napari.
# - glasbey: distinct high-contrast label LUTs for view_in_napari.
napari = [
"napari[all]>=0.5.5",
"numpy<2.5",
"ipykernel<7",
"lxml-html-clean",
"colorcet",
"glasbey",
]
# workflow runs the Snakemake pipeline (per-tile SLURM jobs across GPUs).
workflow = ["snakemake>=8", "snakemake-executor-plugin-slurm"]
Expand Down
69 changes: 19 additions & 50 deletions src/patchworks/plugins/napari.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,48 +191,6 @@ def _resolve_labels(
return arr.astype("int32")


def _label_colormap(name: str | None):
"""Build a cyclic napari label colormap from a colorcet glasbey palette.

Parameters
----------
name : str or None
A ``colorcet`` palette attribute, e.g. ``"glasbey_dark"`` (glasbey on a
dark background). ``None`` falls back to napari's default label colours.

Returns
-------
napari.utils.colormaps.CyclicLabelColormap or None
The colormap to pass to ``add_labels``, or ``None`` for the default.
"""
if not name:
return None
try:
import colorcet
except ImportError:
logger.warning(
"colorcet not installed; using napari's default label colours "
"(pip install colorcet, or it ships with patchworks[napari])."
)
return None

palette = getattr(colorcet, name, None)
if palette is None:
logger.warning("colorcet has no palette %r; using default colours.", name)
return None

import numpy as np
from napari.utils.colormaps import CyclicLabelColormap

def _hex_to_rgba(h: str):
h = h.lstrip("#")
return (int(h[0:2], 16) / 255, int(h[2:4], 16) / 255,
int(h[4:6], 16) / 255, 1.0)

colors = np.array([_hex_to_rgba(c) for c in palette], dtype=float)
return CyclicLabelColormap(colors=colors)


def view_in_napari(
image: Union[da.Array, str, Path],
labels: Union[da.Array, str, Path, None] = None,
Expand All @@ -241,7 +199,7 @@ def view_in_napari(
labels_component: str = "labels",
image_name: str = "image",
labels_name: str = "labels",
label_colormap: str | None = "glasbey_dark",
glasbey: bool = True,
show: bool = True,
**add_image_kwargs: Any,
):
Expand All @@ -266,11 +224,10 @@ def view_in_napari(
matching ``tile_process``'s ``output_component``).
image_name, labels_name : str, optional
Layer names shown in napari.
label_colormap : str or None, optional
``colorcet`` palette for the label LUT; default ``"glasbey_dark"``
(glasbey on a dark background — many distinct, high-contrast colours).
Any colorcet name works (e.g. ``"glasbey_light"``); ``None`` uses
napari's default label colours. Needs ``colorcet`` (ships with
glasbey : bool, optional
Colour the labels with a glasbey palette (many distinct, high-contrast
colours, tuned to read on the dark canvas) instead of napari's default.
Default ``True``. Needs the ``glasbey`` package (ships with
``patchworks[napari]``).
show : bool, optional
Start the napari event loop (blocking). Set ``False`` in scripts/tests
Expand Down Expand Up @@ -299,8 +256,20 @@ def view_in_napari(
**add_image_kwargs,
)

cmap = _label_colormap(label_colormap)
label_kwargs = {"colormap": cmap} if cmap is not None else {}
label_kwargs: dict[str, Any] = {}
if glasbey:
try:
import glasbey as _glasbey

# bias lighter so colours read on napari's dark canvas
label_kwargs["colormap"] = _glasbey.create_palette(
256, lightness_bounds=(40, 100)
)
except ImportError:
logger.warning(
"glasbey not installed; using napari's default label colours "
"(pip install glasbey, or it ships with patchworks[napari])."
)

if labels is not None:
lab = _resolve_labels(labels, labels_component)
Expand Down
Loading