Skip to content

Commit ee348d8

Browse files
lguerardclaude
andauthored
refactor(napari): ♻️ use the glasbey package for label LUTs (#39)
Drop the custom colorcet/CyclicLabelColormap helper; instead build the palette with glasbey.create_palette (the approach from napari's gallery) and hand the list straight to add_labels(colormap=...). Lightness biased up so colours read on the dark canvas. Toggle via the `glasbey` arg. Swap the napari extra dep colorcet -> glasbey. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent e5cffbb commit ee348d8

2 files changed

Lines changed: 21 additions & 52 deletions

File tree

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,13 @@ imaris = ["imaris-ims-file-reader"]
6363
# - ipykernel < 7: napari-console requires ipykernel < 7.
6464
# - lxml-html-clean: napari's notebook_display imports lxml.html.clean, split
6565
# into a separate package in lxml >= 5.2 (else ImportError on Viewer()).
66-
# - colorcet: glasbey label LUTs for view_in_napari.
66+
# - glasbey: distinct high-contrast label LUTs for view_in_napari.
6767
napari = [
6868
"napari[all]>=0.5.5",
6969
"numpy<2.5",
7070
"ipykernel<7",
7171
"lxml-html-clean",
72-
"colorcet",
72+
"glasbey",
7373
]
7474
# workflow runs the Snakemake pipeline (per-tile SLURM jobs across GPUs).
7575
workflow = ["snakemake>=8", "snakemake-executor-plugin-slurm"]

src/patchworks/plugins/napari.py

Lines changed: 19 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -191,48 +191,6 @@ def _resolve_labels(
191191
return arr.astype("int32")
192192

193193

194-
def _label_colormap(name: str | None):
195-
"""Build a cyclic napari label colormap from a colorcet glasbey palette.
196-
197-
Parameters
198-
----------
199-
name : str or None
200-
A ``colorcet`` palette attribute, e.g. ``"glasbey_dark"`` (glasbey on a
201-
dark background). ``None`` falls back to napari's default label colours.
202-
203-
Returns
204-
-------
205-
napari.utils.colormaps.CyclicLabelColormap or None
206-
The colormap to pass to ``add_labels``, or ``None`` for the default.
207-
"""
208-
if not name:
209-
return None
210-
try:
211-
import colorcet
212-
except ImportError:
213-
logger.warning(
214-
"colorcet not installed; using napari's default label colours "
215-
"(pip install colorcet, or it ships with patchworks[napari])."
216-
)
217-
return None
218-
219-
palette = getattr(colorcet, name, None)
220-
if palette is None:
221-
logger.warning("colorcet has no palette %r; using default colours.", name)
222-
return None
223-
224-
import numpy as np
225-
from napari.utils.colormaps import CyclicLabelColormap
226-
227-
def _hex_to_rgba(h: str):
228-
h = h.lstrip("#")
229-
return (int(h[0:2], 16) / 255, int(h[2:4], 16) / 255,
230-
int(h[4:6], 16) / 255, 1.0)
231-
232-
colors = np.array([_hex_to_rgba(c) for c in palette], dtype=float)
233-
return CyclicLabelColormap(colors=colors)
234-
235-
236194
def view_in_napari(
237195
image: Union[da.Array, str, Path],
238196
labels: Union[da.Array, str, Path, None] = None,
@@ -241,7 +199,7 @@ def view_in_napari(
241199
labels_component: str = "labels",
242200
image_name: str = "image",
243201
labels_name: str = "labels",
244-
label_colormap: str | None = "glasbey_dark",
202+
glasbey: bool = True,
245203
show: bool = True,
246204
**add_image_kwargs: Any,
247205
):
@@ -266,11 +224,10 @@ def view_in_napari(
266224
matching ``tile_process``'s ``output_component``).
267225
image_name, labels_name : str, optional
268226
Layer names shown in napari.
269-
label_colormap : str or None, optional
270-
``colorcet`` palette for the label LUT; default ``"glasbey_dark"``
271-
(glasbey on a dark background — many distinct, high-contrast colours).
272-
Any colorcet name works (e.g. ``"glasbey_light"``); ``None`` uses
273-
napari's default label colours. Needs ``colorcet`` (ships with
227+
glasbey : bool, optional
228+
Colour the labels with a glasbey palette (many distinct, high-contrast
229+
colours, tuned to read on the dark canvas) instead of napari's default.
230+
Default ``True``. Needs the ``glasbey`` package (ships with
274231
``patchworks[napari]``).
275232
show : bool, optional
276233
Start the napari event loop (blocking). Set ``False`` in scripts/tests
@@ -299,8 +256,20 @@ def view_in_napari(
299256
**add_image_kwargs,
300257
)
301258

302-
cmap = _label_colormap(label_colormap)
303-
label_kwargs = {"colormap": cmap} if cmap is not None else {}
259+
label_kwargs: dict[str, Any] = {}
260+
if glasbey:
261+
try:
262+
import glasbey as _glasbey
263+
264+
# bias lighter so colours read on napari's dark canvas
265+
label_kwargs["colormap"] = _glasbey.create_palette(
266+
256, lightness_bounds=(40, 100)
267+
)
268+
except ImportError:
269+
logger.warning(
270+
"glasbey not installed; using napari's default label colours "
271+
"(pip install glasbey, or it ships with patchworks[napari])."
272+
)
304273

305274
if labels is not None:
306275
lab = _resolve_labels(labels, labels_component)

0 commit comments

Comments
 (0)