napari-npifile
Read and write .npi files, a lightweight format based on NumPy .npy arrays with additional napari viewer and layer metadata such as axis names and colormaps.
Allows reading and writing of .npi files.
.npi files are essentially NumPy arrays packaged together with JSON metadata that
stores napari viewer and layer configuration, such as axis names or colormaps.
The goal is to keep the simplicity of .npy files while preserving useful visualization
settings when opening arrays in napari.
Quick Example
import napari_npifile
import numpy as np
data = np.random.random(size=(10, 20, 30, 40))
# save it without napari metadata
np.save("quick_example.npy", data)
# save it with napari metadata
napari_npifile.write_npi(
"quick_example.npi",
data=data,
layer_attributes={
"name": "foo",
"colormap": "magma",
"contrast_limits": (-0.25, 1.25),
},
viewer_settings={
"axis_labels": ["T", "Z", "Y", "X"],
"scale_bar_properties": {"visible": True, "unit": "px", "font_size": 40.0},
},
)
# and drag and drop it into napari
# |
# |
# v

Motivation
When working on image analysis tasks in Python it is common to save temporary NumPy arrays such as:
- image stacks
- neural network tensors
- intermediate processing results
- ...
to .npy files, which can easily be opened in napari via
drag-and-drop.
However, .npy files contain no visualization metadata. Each time a file is opened,
display settings (colormap, contrast limits, etc.) must be configured manually.
.npi files solve this by storing the arrays together with napari viewer metadata.
Installation
pip install --upgrade napari-npifile
Note: napari is not included as a dependency, to allow writing .npi files
even if napari is not installed (e.g., on headless servers).
To view .npi files in napari, you must install napari separately:
pip install napari
See the napari installation instructions for details.
Usage
Reading .npi files in napari
Once napari-npifile is installed, .npi files can be opened in napari like any other
supported format:
- via drag-and-drop,
- via the
File -> Openmenu, - via the napari console using
viewer.open("myfile.npi", plugin="napari-npifile"), or - programmatically using:
import napari viewer = napari.Viewer() viewer.open("myfile.npi", plugin="napari-npifile") napari.run()
When loaded, each layer in the .npi file becomes a separate napari layer. Layer
settings (colormap, contrast limits, gamma, name, etc.) and viewer settings (axis
labels, camera settings, scale bar settings, etc.) are applied automatically.
Writing .npi files from Python (no napari required)
napari-npifile provides a main writer class NpiWriter_v1 for full control, as well
as the convenience wrappers write_npi, write_multilayer_npi into a .npi file.
When to use what:
The convenience wrapper functions (write_npi, write_multilayer_npi) are useful for
cases where all the data to be stored is available upfront. Use write_npi for writing
a single layer and write_multilayer_npi for multiple layers.
The class NpiWriter_v1 allows to incrementally write layers into a single .npi
file over time, making it ideal for loops.
Convenience wrapper write_npi (single-layer):
import napari_npifile
import numpy as np
data = np.random.random((10, 20, 30))
napari_npifile.write_npi(
"single.npi",
data=data,
layer_attributes={"name": "layer0", "colormap": "magma"},
viewer_settings={"axis_labels": ["Z", "Y", "X"]},
)
Convenience wrapper write_multilayer_npi (multi-layer):
import napari_npifile
import numpy as np
data_seq = [
np.random.random((10, 20, 30)),
np.random.random((10, 20, 30)),
]
layer_attributes_seq = [
{"name": "first", "colormap": "gray"},
{"name": "second", "colormap": "green"},
]
napari_npifile.write_multilayer_npi(
"multi.npi",
data_seq=data_seq,
layer_attributes_seq=layer_attributes_seq,
viewer_settings={"axis_labels": ["Z", "Y", "X"]},
)
Full general example using NpiWriter_v1:
import napari_npifile
import numpy as np
# prepare writer
writer = napari_npifile.NpiWriter_v1(
out_path="out.npi",
exist_ok=True,
)
# viewer settings are saved once and are used for all layers
writer.write_viewer_settings(
{
"axis_labels": ["T", "K", "Z", "Y", "X"],
"grid_properties": {"enabled": True, "shape": (1, -1), "spacing": 0.1},
"scale_bar_properties": {"visible": True, "unit": "px", "gridded": True},
}
)
# layers are written one at a time, each with individual attributes
data1 = np.random.random(size=(3, 10, 20, 30, 40))
writer.write_layer(data=data1, attributes={"colormap": "cyan", "name": "foo"})
data2 = np.random.random(size=(1, 10, 20, 30, 40))
writer.write_layer(data=data2, attributes={"colormap": "green", "name": "bar"})
Incremental writing
When using NpiWriter_v1, the .npi archive is created immediately during
initialization. From that point on, the file is always a valid .npi archive.
Each call to write_layer or write_viewer_settings simply appends new
entries to the ZIP archive.
Compression (optional)
.npi files are standard ZIP archives and can therefore optionally use ZIP compression.
Compression can be configured when creating an archive. The same two parameters
compression and compresslevel are available in all writer entry points and have the
identical meaning and function as defined in the underlying
zipfile Python standard library.
Below follows a short description. For more details about compression and
compresslevel, please see
https://docs.python.org/3/library/zipfile.html#zipfile-objects.
Parameter compression
Controls the ZIP compression method as supported by zipfile.
Accepted values:
None: no compression (ZIP_STORED)False: no compression (ZIP_STORED)True: standard ZIP compression (ZIP_DEFLATED)int: azipfile.ZIP_*numeric constant (e.g.,zipfile.ZIP_DEFLATEDorzipfile.ZIP_LZMA)str: case-insensitive name of azipfile.ZIP_*numeric constant, without the leading"ZIP_"(e.g.,"deflated","lzma", or"bzip2")
Parameter compresslevel
Optional compression level (int or None) used by some compression methods.
The allowed range depends on the chosen ZIP method and follows the
limits defined by zipfile.
If None (the default), the default level of the
respective compressor is
used.
Examples
Standard ZIP compression:
napari_npifile.write_npi(
out_path="compressed.npi",
data=data,
compression=True,
)
Using an explicit compression method:
import zipfile
napari_npifile.write_npi(
out_path="compressed.npi",
data=data,
compression="lzma",
)
# both are identical
napari_npifile.write_npi(
out_path="compressed.npi",
data=data,
compression=zipfile.ZIP_LZMA,
)
Specifying a compression level:
napari_npifile.write_npi(
out_path="compressed.npi",
data=data,
compression="DEFLATED",
compresslevel=9,
)
Notes
- The default behavior is no compression (
ZIP_STORED). - Compression can reduce disk usage but may increase write and read times.
- Since
.npistores raw.npyarrays, compression effectiveness depends on the structure of the data.
File Format
Overview
.npi files are ZIP archives containing raw NumPy array data (stored
as .npy files) together with napari viewer and layer metadata stored as JSON files.
Versions
Every .npi file contains a root metadata.json with a mandatory "npi_format"
field. This format versioning ensures backward compatibility: the reader uses it to
select the appropriate parser for each format.
In addition, the class NpiWriter_v1 will remain indefinitely to ensure backward
compatibility. If a new .npi format version is introduced, a new writer class will be
added alongside it, leaving existing code using NpiWriter_v1 fully functional.
Version history:
| Format Version | napari-npifile version |
Comment |
|---|---|---|
v1 |
0.1.0+ |
Initial version, current format. Supports single- and multi-layer .npi files with full napari image layer metadata and a subset of viewer settings. |
Current Version Details (v1)
Archive Layout
example.npi # this is just a ZIP archive
├── metadata.json # contains {"npi_format": "v1"}
├── viewer_settings.json # dictionary with global viewer settings - see below
└── layers
├── 00000000 # each layer is stored under a sequential numeric key
│ ├── data.npy # NumPy array as saved via np.save
│ └── attributes.json # layer attributes (as in napari.layers.Image)
└── 00000001
├── data.npy
└── attributes.json
All metadata files are JSON files which contain a dictionary.
Metadata File metadata.json
Only contains {"npi_format": "v1"}.
It is used by the reader to select the correct parser for the file.
Metadata File viewer_settings.json
Stores global napari viewer settings. All fields are optional; if missing, the reader does not change the current napari configuration.
| Field | Type | Notes |
|---|---|---|
clear_existing_layers |
bool |
If True, removes all existing layers before adding new ones |
hide_existing_layers |
bool |
If True, hides all existing layers before adding new ones |
title |
str |
Napari window title |
ndisplay |
int |
2 or 3 (for 2D or 3D mode) |
axis_labels |
list[str] |
One label per axis (e.g., ["T", "Z", "Y", "X"]) |
dims_set_point |
dict |
Optional subfields as in napari's set_point, i.e. axis: int or Sequence[int] and value: float or Sequence[float] |
camera_properties |
dict |
Optional subfields as in napari's Camera (e.g., {"center": (0.0, 10.0, 20.0)}) |
grid_properties |
dict |
Optional subfields as in napari's GridCanvas (enabled: bool, shape: tuple[int, int], stride: int, spacing: float) |
scale_bar_properties |
dict |
Optional subfields as in napari's ScaleBarOverlay (e.g., visible: bool, unit: str, gridded: bool) |
text_overlay_properties |
dict |
Optional subfields as in napari's TextOverlay (e.g., visible: bool, text: str, gridded: bool, font_size: float) |
Metadata File(s) layers/*/attributes.json
Layer-specific settings for each layer. All fields are optional; if missing, the reader uses napari defaults.
Any keyword argument accepted by napari’s Image layer
(except data) can be specified, e.g.:
name: strcolormap: str(any napari colormap)contrast_limits: tuple[float, float]gamma: floatblending: str ("translucent", "additive", "opaque", ...)- ...
Design Principles and Limitations
Key principles for the .npi format and napari-npifile are:
- Simplicity and accessibility:
.npifiles are standard ZIP archives containing NumPy arrays and JSON metadata. Thus, the files can easily be inspected, read, and created even without this library. - Minimal dependencies: Only NumPy and napari (for viewing) are required.
- Tight napari integration:
.npistores viewer and layer settings exclusively for napari, and thus allows fine-grained control over how arrays are displayed. - Backward compatibility: Every
.npiincludes a format version, ensuring future readers can correctly handle older files. - Headless-friendly writing:
.npifiles can be created without napari installed, which is useful for server-side or automated workflows.
These design choices naturally impose some limitations:
- No access optimization:
.npiuses plainnp.save/np.loadfrom ZIP archives and thus can't compete with optimized formats for large arrays. - Generic compression:
.npiallows for standard ZIP compression. Formats designed specifically for numerical arrays may achieve better compression or faster access depending on the dataset. - Viewer-centric metadata: Metadata is tailored specifically to napari; other viewers or tools require extra work to interpret it.
In short, .npi prioritizes simplicity and tight napari integration over maximal
performance. Users needing advanced storage features or highly
efficient random access may prefer formats such as Zarr or
HDF5, while
OME-TIFF provides more generic
metadata handling.
Known Issues
- Setting the viewer slice position (
viewer.dims.set_point) and camera properties does not work when there are no pre-existing layers or whenclear_existing_layersisTrue. - When using
NpiWriter_v1to write an.npifile, the methodwrite_viewer_settingscan only be called once, because the underlyingzipfilelibrary does not allow to modfify a file in an existing ZIP file.
Todo / Ideas
- Add support to write
.npifiles directly from napari. - Add CLI to convert existing
.npyfiles to.npifiles (and back).
Changelog
v0.2.0(2026-03-17)- added support for optional ZIP compression
v0.1.1(2026-03-14)- fixed missing project description and formatting errors in the readme
v0.1.0(2026-03-14)- initial release with standalone read/write and napari reader support
Related Discussions / Projects
- napari GitHub issue: Changing viewer dimension when adding a new layer #6127
- image.sc forum: Saving volumetric data with voxel size, colormap, annotations
License
napari-npifile is released under the MIT License. See LICENSE.txt for full details.
