skills/cell-biology/napari-image-viewer/SKILL.md
Interactive viewer for microscopy. Displays 2D/3D/4D arrays as Image, Labels, Points, Shapes, Tracks layers; supports annotation, plugin analysis, headless screenshots. Core visualization for Python bioimage workflows. Use ImageJ/FIJI for macro processing; napari for Python-native interactive visualization and DL segmentation review.
npx skillsauth add jaechang-hits/sciagent-skills napari-image-viewerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
napari is a fast, interactive multi-dimensional viewer for scientific data built on PyQt5 and VisPy. It displays NumPy arrays and zarr arrays as layered visualizations — Image layers for raw data, Labels layers for segmentation masks, Points layers for cell centroids, and Shapes layers for ROI annotations. napari integrates with scikit-image, Cellpose, and StarDist via plugins, making it the standard visualization and annotation tool in Python bioimage analysis pipelines. For headless environments (HPC, CI), napari supports offscreen rendering and viewer.screenshot() for automated figure generation.
napari, numpy, scikit-imageQT_QPA_PLATFORM=offscreennapari-cellpose, napari-stardist, napari-animation# Install with all backends
pip install "napari[all]"
# Or minimal install
pip install napari pyqt5
# Verify
python -c "import napari; print(napari.__version__)"
# 0.5.5
# Install useful plugins
pip install napari-cellpose napari-animation
import napari
import numpy as np
from skimage import data
# Open viewer with a sample image
viewer = napari.Viewer()
viewer.add_image(data.cells3d()[:, 1, :, :], name="DAPI", colormap="blue")
napari.run() # blocks until viewer closed (use in scripts)
Add and configure multi-channel image layers.
import napari
import numpy as np
from skimage import io
viewer = napari.Viewer()
# Add single grayscale image
img = io.imread("cells.tif") # shape: (H, W)
viewer.add_image(img, name="phase contrast", colormap="gray",
contrast_limits=[0, img.max()])
# Add multichannel image (3 channels)
img_mc = io.imread("multichannel.tif") # shape: (H, W, 3)
viewer.add_image(img_mc[..., 0], name="DAPI", colormap="blue", blending="additive")
viewer.add_image(img_mc[..., 1], name="GFP", colormap="green", blending="additive")
viewer.add_image(img_mc[..., 2], name="mCherry", colormap="red", blending="additive")
print(f"Layers: {[l.name for l in viewer.layers]}")
Display and edit integer label masks from Cellpose, StarDist, or scikit-image.
import napari
import numpy as np
from skimage import io
viewer = napari.Viewer()
img = io.imread("cells.tif")
masks = np.load("masks.npy") # integer label array: 0=background, 1..N=cells
# Add raw image
viewer.add_image(img, name="raw", colormap="gray")
# Add label mask (each cell gets a unique random color)
label_layer = viewer.add_labels(masks, name="cell_masks", opacity=0.5)
# Access labels for editing
print(f"Unique cells: {len(np.unique(masks)) - 1}")
print(f"Label layer data shape: {label_layer.data.shape}")
Add and style point markers for centroids, landmarks, or detected features.
import napari
import numpy as np
import pandas as pd
from skimage.measure import regionprops_table
viewer = napari.Viewer()
# Compute centroids from label mask
masks = np.load("masks.npy")
props = regionprops_table(masks, properties=["centroid", "label"])
centroids = np.column_stack([props["centroid-0"], props["centroid-1"]])
# Add centroids as Points layer
viewer.add_points(
centroids,
name=f"centroids ({len(centroids)} cells)",
size=8,
face_color="yellow",
edge_color="black",
edge_width=0.5,
)
print(f"Cells marked: {len(centroids)}")
Add bounding boxes, polygons, and line annotations.
import napari
import numpy as np
viewer = napari.Viewer()
# Add rectangles as ROIs (format: [[y1, x1], [y2, x2]])
rois = [
np.array([[50, 100], [200, 300]]), # ROI 1
np.array([[300, 150], [450, 350]]), # ROI 2
]
shapes_layer = viewer.add_shapes(
rois,
shape_type="rectangle",
name="ROIs",
edge_color="cyan",
face_color="transparent",
edge_width=2,
)
# Retrieve shapes data for analysis
for i, shape in enumerate(shapes_layer.data):
y_min, x_min = shape.min(axis=0)
y_max, x_max = shape.max(axis=0)
print(f"ROI {i+1}: y={y_min:.0f}-{y_max:.0f}, x={x_min:.0f}-{x_max:.0f}")
Display z-stacks and time series with sliders.
import napari
import numpy as np
from skimage import data
viewer = napari.Viewer()
# 3D z-stack: shape (Z, H, W)
zstack = data.cells3d()[:, 1, :, :] # nuclei channel
viewer.add_image(zstack, name="z-stack nuclei",
colormap="cyan", blending="additive")
# 4D time-lapse: shape (T, H, W) or (T, Z, H, W)
timelapse = np.random.randint(0, 65535, (10, 256, 256), dtype=np.uint16)
viewer.add_image(timelapse, name="timelapse", colormap="gray")
# napari shows axis sliders automatically for ndim > 2
print(f"z-stack shape: {zstack.shape} → slider for Z axis")
print(f"timelapse shape: {timelapse.shape} → sliders for T axis")
Export screenshots without a display (for HPC and CI environments).
import os
os.environ["QT_QPA_PLATFORM"] = "offscreen" # must be set BEFORE importing napari
import napari
import numpy as np
from skimage import io, data
import matplotlib
matplotlib.use("Agg") # also set matplotlib backend
viewer = napari.Viewer(show=False)
img = data.cells3d()[30, 1, :, :] # single z-slice
masks = (img > img.mean()).astype(int) # simple threshold mask
viewer.add_image(img, name="DAPI", colormap="blue", blending="additive")
viewer.add_labels(masks.astype(np.int32), name="masks", opacity=0.5)
# Export screenshot
screenshot = viewer.screenshot(path="napari_export.png", canvas_only=True)
print(f"Screenshot saved: napari_export.png ({screenshot.shape})")
viewer.close()
| Parameter | Module | Default | Effect |
|-----------|--------|---------|--------|
| colormap | add_image | "gray" | Colormap name (matplotlib cmaps + napari built-ins: "green", "blue", "cyan") |
| contrast_limits | add_image | auto | [min, max] intensity clipping for display |
| blending | add_image | "translucent" | "additive" for multichannel overlay; "opaque" for solid |
| opacity | add_labels | 0.7 | 0–1 transparency of label layer over image |
| face_color | add_points | "white" | Point fill color (name, hex, or RGBA) |
| size | add_points | 10 | Point radius in data coordinates (pixels) |
| edge_width | add_shapes | 1 | Shape outline width in pixels |
| show | Viewer() | True | False for headless/offscreen mode |
| ndisplay | Viewer() | 2 | 3 for 3D OpenGL rendering mode |
| canvas_only | screenshot() | False | True to exclude the napari toolbar from export |
import os
os.environ["QT_QPA_PLATFORM"] = "offscreen"
import napari
import numpy as np
from cellpose import models
from skimage import io
from skimage.measure import regionprops_table
# Segment with Cellpose
img = io.imread("cells.tif")
model = models.Cellpose(model_type="cyto3", gpu=False)
masks, _, _, diams = model.eval(img, diameter=0, channels=[0, 0])
# Visualize in napari (headless for export)
viewer = napari.Viewer(show=False)
viewer.add_image(img, name="raw", colormap="gray")
viewer.add_labels(masks, name=f"masks ({masks.max()} cells)", opacity=0.6)
# Add centroids
props = regionprops_table(masks, properties=["centroid"])
centroids = np.column_stack([props["centroid-0"], props["centroid-1"]])
viewer.add_points(centroids, name="centroids", size=6, face_color="yellow")
viewer.screenshot(path="segmentation_review.png", canvas_only=True)
viewer.close()
print(f"QC export: segmentation_review.png — {masks.max()} cells detected")
import napari
import numpy as np
from skimage import io
# Load 4-channel FISH image: DAPI + 3 RNA probes
img = io.imread("fish_4channel.tif") # shape: (H, W, 4)
viewer = napari.Viewer()
channels = [
("DAPI", "blue", img[..., 0]),
("probe_A_cy3", "yellow", img[..., 1]),
("probe_B_cy5", "red", img[..., 2]),
("probe_C_gfp", "green", img[..., 3]),
]
for name, colormap, channel in channels:
viewer.add_image(channel, name=name, colormap=colormap,
blending="additive",
contrast_limits=[channel.min(), np.percentile(channel, 99.5)])
napari.run()
import os
os.environ["QT_QPA_PLATFORM"] = "offscreen"
import napari
import numpy as np
from skimage import io
import matplotlib.pyplot as plt
viewer = napari.Viewer(show=False)
img = io.imread("cells.tif")
masks = np.load("masks.npy")
viewer.add_image(img, name="raw", colormap="gray")
viewer.add_labels(masks, name="segmentation", opacity=0.5)
# Set camera zoom and position
viewer.camera.zoom = 1.5
viewer.camera.center = (img.shape[0] // 2, img.shape[1] // 2)
screenshot = viewer.screenshot(path="figure_panel.png", canvas_only=True)
viewer.close()
# Add scalebar with matplotlib
fig, ax = plt.subplots(figsize=(6, 6))
ax.imshow(screenshot)
ax.axis("off")
plt.tight_layout()
plt.savefig("figure_final.pdf", dpi=300, bbox_inches="tight")
print("Exported: figure_final.pdf")
import os
os.environ["QT_QPA_PLATFORM"] = "offscreen"
import napari
import numpy as np
from skimage import io
from pathlib import Path
output_dir = Path("projections")
output_dir.mkdir(exist_ok=True)
for img_path in sorted(Path("zstacks").glob("*.tif")):
zstack = io.imread(img_path) # shape: (Z, H, W)
max_proj = zstack.max(axis=0)
viewer = napari.Viewer(show=False)
viewer.add_image(max_proj, name="max_projection", colormap="gray")
viewer.screenshot(path=str(output_dir / f"{img_path.stem}_maxproj.png"), canvas_only=True)
viewer.close()
print(f"Exported: {img_path.stem}_maxproj.png")
print("All z-stack projections exported.")
| Problem | Cause | Solution |
|---------|-------|----------|
| qt.qpa.plugin: Could not load the Qt platform plugin "xcb" | Missing display or Qt platform plugin | Set QT_QPA_PLATFORM=offscreen before importing napari; install libxcb-util-dev |
| napari window does not open | Running in SSH without X forwarding | Use viewer = napari.Viewer(show=False) and export via screenshot() |
| Slow rendering of large images | Image too large for GPU VRAM | Use viewer.add_image(img, multiscale=True) for pyramidal rendering |
| Labels layer shows wrong colors | Mask dtype overflow | Ensure masks are int32 not uint8 (overflow at 255 cells) |
| napari.run() blocks Jupyter notebook | Qt event loop conflict | Use %gui qt magic in Jupyter; or use viewer.show() without napari.run() |
| Screenshot is black/empty | Viewer not fully rendered before screenshot | Add viewer.update() or slight delay before screenshot() |
| Plugin not appearing in menu | Plugin not installed or wrong napari version | pip install napari-<plugin>; check napari version compatibility on napari-hub |
| 3D rendering slow | Complex geometry or large volume | Switch viewer.dims.ndisplay = 2; reduce z-stack depth |
development
Bulk RNA-seq DE with R/Bioconductor DESeq2. Negative binomial GLM, empirical Bayes shrinkage, Wald/LRT tests, multi-factor designs, Salmon tximeta import, apeglm LFC shrinkage, MA/volcano/heatmap viz. R gold standard. Use pydeseq2-differential-expression for Python; use edgeR for TMM normalization.
tools
Guide to quality filtering raw VCF files before computing summary stats (Ts/Tv ratio, variant counts, AF distributions). Covers detecting raw VCFs via FILTER column and QUAL inspection, QUAL-based filtering with bcftools, Ts/Tv interpretation, and when NOT to filter. Read before any variant-level QC task. See bcftools-variant-manipulation for advanced filters, gatk-variant-calling for caller config, samtools-bam-processing for upstream alignment QC.
tools
Annotate and filter VCF variants with SnpEff and SnpSift. SnpEff predicts functional effects (HIGH/MODERATE/LOW/MODIFIER), genes, transcripts, AA changes, HGVS; SnpSift filters and adds ClinVar/dbSNP. Java CLI with Python subprocess integration. Use ANNOVAR for multi-database annotation; Ensembl VEP for REST API; SnpEff for fast CLI with pre-built genomes.
tools
GWAS and population genetics tool. Processes PLINK (.bed/.bim/.fam), VCF, and BGEN; runs QC (MAF, HWE, missingness), IBD estimation, PCA, and linear/logistic regression GWAS. Outputs Manhattan-ready summary stats. Use regenie or SAIGE for biobanks (>100k samples) needing mixed models.