FAQ¶
General¶
What imaging platforms does UELer support?¶
UELer is designed primarily for MIBI (Multiplexed Ion Beam Imaging) data but works with any multiplexed imaging platform (e.g. IMC) that produces per-channel TIFF files organized in per-FOV subdirectories. It also loads OME-TIFF files and is compatible with data produced by ark-analysis.
Do I need a cell table to use UELer?¶
No — the cell table is optional. Without one, UELer runs as an image viewer: multi-channel rendering, mask/annotation overlays, ROI capture, and batch export all work, and the right panel shows just the ROI manager and Batch export plugins.
Loading a cell table unlocks the single-cell features — scatter plots, histograms, the cell gallery, heatmaps, clustering, and annotation. See Working with a Cell Table.
Does UELer use a GPU?¶
No. UELer is CPU-only — it uses NumPy and Dask for image processing and does not use or require a GPU. A GPU is not supported.
Getting Started & Data¶
Can I try UELer without my own data?¶
Yes. The Binder demo streams a public BioImage Archive study over the network — no installation and no local data required.
How do I load a cell table?¶
UELer builds the viewer first and attaches the cell table in a second step:
import ueler
import pandas as pd
from ueler import load_cell_table
%matplotlib widget
viewer = ueler.run_viewer(base_folder, masks_folder=masks_folder, auto_display=False)
cell_table = pd.read_csv(cell_table_path)
load_cell_table(viewer, cell_table=cell_table, auto_display=True, after_plugins=True)
run_viewer itself has no cell_table_path argument — the table is always attached with
load_cell_table. For a large table, pass a .parquet file (see
Getting Started) and its columns are read on
demand rather than all at once. cell_table= also accepts an AnnData object and cell_table_path= an
.h5ad file. See Get Started and
Working with a Cell Table.
How do I stream a study from the BioImage Archive?¶
Use run_viewer_bia with an accession id (or a direct HTTPS URL) and an optional layout descriptor:
viewer = ueler.run_viewer_bia(
"S-BIAD2557",
descriptor={
"mode": "folder",
"fov_container": "zip",
"base": "Files/spatial_murine_iCCAvsHCC/image_data",
"mask_dir": "Files/spatial_murine_iCCAvsHCC/segmentation/cleaned_mask",
"mask_glob": "{fov}_*.tiff",
"cell_table": "Files/spatial_murine_iCCAvsHCC/cell_table/pCSL005_cell_table.parquet",
},
)
# Attach the study's cell table (all 439,339 cells; a few seconds).
ueler.load_bia_cell_table(viewer)
Only the channels you open are streamed/cached; here each FOV is a <FOV>.zip read per member over HTTP ranges (fov_container). The cell_table key points at the study's cell table. This study publishes it as both .csv and .parquet; the Parquet one is used above and is read column by column over range requests without being downloaded, so no fovs= is needed. For a .csv table, fovs= restricts it to the FOVs you name, filtered while the file streams so a 361 MB table never has to be held whole. See script/run_ueler_BIA.ipynb for more examples.
Installation¶
I get ModuleNotFoundError: No module named 'ueler'¶
Activate your environment and run pip install . (or pip install -e .) from the root of the cloned
repository.
pip install fails with a dependency error¶
Make sure the correct micromamba/conda environment is active first. Create it from the repository's
environment.yml, then install UELer. For documentation or test extras, use pip install ".[docs]"
or pip install ".[dev]".
Why can't UELer install the widget frontend for me?¶
Because no package can. This comes up whenever a widget's browser half goes missing — see My editor cannot find the widget anywidget — and the instinct is that UELer should be able to declare its way out of it. It cannot, and no change to UELer's pyproject.toml would help.
A Python package declares what it needs. Where those files land is decided entirely by the pip command you run — --user, --prefix, --target, or simply which environment was active. Nothing in dependency metadata can reach across and constrain that. So the same anywidget>=0.9 line produces a working install or a broken one depending only on how you invoked pip.
Two specific ideas that look promising and are not:
- Pinning a newer
anywidget. This would only help if some release shipped no browser files at all. None does: every version from 0.9.0 through 0.11 ships bothshare/jupyter/labextensions/anywidget/andshare/jupyter/nbextensions/anywidget/. The files are always built — the only question is which directory they were copied into. - Shipping a copy of the browser files with UELer. Technically possible, genuinely harmful. It writes into another package's namespace, fighting pip and conda over who owns those files, and it goes stale by construction: the JavaScript has to match whichever
anywidgetversion resolves at runtime, and UELer deliberately accepts a range. You would be trading a loud, diagnosable failure for a quiet version mismatch.
What actually fixes it is the install itself — put anywidget in the environment your kernel runs from, rather than in a user-level directory beside it. UELer's part is to notice and say so: the viewer checks at startup and logs a warning naming the paths it found and the remedies, instead of leaving you with blank panels and a frontend notification about a package you never installed on purpose.
Viewer & Widgets¶
Nothing renders / the widgets don't show up¶
Run %matplotlib widget once per kernel session before launching the viewer — it enables the
interactive backend UELer relies on. If widgets still don't appear, restart the kernel and re-run the
cells. See Display Settings for the rest of the
launch checklist.
My editor cannot find the widget anywidget¶
The full message is usually Unable to find widget 'anywidget' version '~0.9.*' from configured widget sources ["local"]. Expected behavior may be affected.
Several parts of UELer — the channel picker, the ROI expression editor, the Mask Painter class list, the gallery tiles and the scatter plot — are built with anywidget, which has a Python half and a JavaScript half. That notification means your frontend found the Python half but not the JavaScript, so those panels come up as empty boxes. It is a packaging problem, not a bug in your data or notebook.
The usual cause is installing into one place and running the kernel from another — most often pip install --user while the kernel runs from a shared or read-only environment. The files then land in ~/.local/share/jupyter/, where JupyterLab finds them but VS Code does not: VS Code's local widget source searches only <environment>/share/jupyter/nbextensions. That is why the same environment can work in JupyterLab and fail in VS Code.
Either of these fixes it:
- Install into the environment the kernel runs from, without
--user:pip install --force-reinstall anywidget. - Let VS Code fetch widget scripts from a CDN: add
"jsdelivr.com"to thejupyter.widgetScriptSourcessetting, then reload the window.
UELer checks for this when the viewer opens and logs a warning naming the paths it found, so you do not have to work it out from the frontend notification alone. For why UELer cannot simply install the missing files itself, see Why can't UELer install the widget frontend for me?
Do I need to set UELER_SCATTER_BACKEND?¶
No. The interactive jupyter-scatter widget is the default in every environment — JupyterLab, the
classic Notebook, and VS Code alike. Earlier versions dropped to a static Matplotlib scatter under VS
Code automatically, which is why you may have seen UELER_SCATTER_BACKEND=widget in older notebooks;
that is no longer needed and can be removed.
The static scatter is still available as an opt-in fallback:
Set it before launching the viewer. The plot then carries an inline notice saying the static backend is active.
The histogram says it requires Bokeh¶
The Histogram plugin renders with Bokeh. Install bokeh and jupyter_bokeh (both are UELer
dependencies) and restart the kernel. UELer auto-loads BokehJS on first plot, so it renders in VS Code
without a priming cell.
The viewer is blank or does not update after launching¶
- Run cells one at a time to isolate the issue.
- Restart the kernel and run all cells again.
- Check that your data paths are correct.
The image loads but no cells are shown¶
Almost always the data mapping: the four keys that tell UELer which columns of your cell table hold the coordinates, the mask label and the FOV name. If one of them names a column your table does not have, UELer finds no cells to draw — and an empty overlay looks exactly the same as a dataset that genuinely has nothing in it, which is what makes this hard to spot.
Open Advanced Settings → Data mapping in the left panel and compare X key:, Y key:, Label key: and Fov key: against your table's real column names. Each field offers your own columns as suggestions, so in the normal case you can pick the right one; it also accepts a name typed by hand. Mask key: does the same for the mask suffixes in your masks folder.
The setup dialog asks for exactly these when a table first loads, and pre-fills the ones it recognises — so if you skipped it, that is the thing to check first.
A setting came back different, with a warning in the log¶
UELer saves your settings per dataset in widget_states.json (inside the .UELer folder) and restores them next time. The data can change in between — a field of view removed from the folder, a marker set deleted, a column renamed — and then a saved setting names something that is no longer there.
When that happens UELer does not fail to open. It falls back to the nearest value the control will accept — the first available FOV for Select Image:, the first option for a list, the nearest end of a slider's range, otherwise that control's default — and logs a warning naming the setting, the value it could not use, and what it used instead:
That is informational, not an error: set the control again if the setting matters for this dataset, and it will be saved as normal. A settings file that is corrupt or truncated — a kernel killed mid-write will do it — is ignored in the same spirit, with a warning, and the viewer opens with defaults. Deleting widget_states.json is always safe; you lose the saved settings for that dataset and nothing else.
Launching takes a long time on shared or network storage¶
Opening a dataset reads the FOV list and scans the masks folder, and on a shared filesystem (an HPC project mount, say) a directory holding tens of thousands of mask files can make that scan the slowest part of startup. UELer caps it: the mask-suffix scan is a single streaming pass over the directory with a two-second budget, and if the budget runs out it uses what it found and carries on. The Mask key: list then fills in as you browse FOVs, so a partial scan costs you a shorter suggestion list and nothing else — you can always type the suffix you want.
If launching is slow beyond that, the remaining cost is reading the images themselves. Lower Cache Size: if memory is the constraint, and see Display Settings for the downsampling options.
Channel images appear all-white or all-black¶
This is a contrast issue. Each channel's display range starts at the 99.9th percentile of its data, not its maximum — so a single bright artefact can push the whole image dark. Expand the Channels accordion and lower Max (for an all-black channel) or raise Min (for an all-white one). See Getting contrast right.
How do I enable Map Mode?¶
Map mode is opt-in. Set the ENABLE_MAP_MODE environment variable to a truthy value (1, true,
yes, on) and place map descriptor JSON files under <base_folder>/.UELer/maps/. When both are
present, the Map mode toggle and Select map: dropdown appear. See
Map Mode.
Set it before import ueler. The flag is read once at import time, so setting it after UELer is
imported has no effect — even if you set it before calling run_viewer. Set it in the shell before
starting the kernel, or restart the kernel after setting it.
Map mode renders slowly for large datasets¶
Map mode limits how many uncached tiles it draws per frame (default 80), keeping the ones nearest the viewport. You can lower this on the viewer object:
Single-Cell Analysis¶
FlowSOM won't run¶
The FlowSOM plugin needs the optional pyFlowSOM package. If it isn't installed, the rest of UELer
still works — only clicking Run in the FlowSOM plugin raises pyFlowSOM is required to run the
FlowSOM plugin. Install pyFlowSOM and restart the kernel.
Which features need a cell table?¶
Scatter plot, Histogram, Gallery, Heatmap, FlowSOM, Cell Annotation, Mask painter, Go to, Cell Table Editor, and the cell tooltip labels all require a cell table. ROI manager and Batch export work without one. See Working with a Cell Table.
ROI, Export & Files¶
Where does UELer store my work?¶
Per-dataset state lives in a .UELer/ folder inside your base_folder:
| Path | Contents |
|---|---|
.UELer/roi_manager.csv |
Saved ROIs |
.UELer/widget_states.json |
Remembered UI/widget state |
.UELer/export_configs/ |
Saved batch-export config templates |
.UELer/dataset_<id>/checkpoints/ |
Cell Annotation checkpoints (.h5ad) |
.UELer/maps/ |
Map descriptors (for map mode) |
Mask and annotation palettes are also saved under .UELer/.
Can I move the .UELer folder somewhere else?¶
Yes. Pass settings_path to run_viewer():
This stores settings at /your/custom/path/<base_folder name>/.UELer instead of
<base_folder>/.UELer. Use it when base_folder is read-only, or a downstream
pipeline must not see extra files under its dataset root. Leave settings_path
unset to keep the default location shown above.
If neither location can be created (for example base_folder is read-only and
settings_path was not given), run_viewer() fails immediately with a message
naming the folder it tried and suggesting settings_path=... — it does not fail
silently or partway through loading.
The scale bar is missing in my exports (or the viewer)¶
The scale bar is driven by pixel size. Set the Pixel Size (nm): value in the left panel's Advanced Settings (see the User Interface reference). If no pixel size is available, the scale bar is omitted.