{"name":"napari-npifile","display_name":"napari-npifile","visibility":"public","icon":"","categories":[],"schema_version":"0.2.1","on_activate":null,"on_deactivate":null,"contributions":{"commands":[{"id":"napari-npifile.read_npi","title":"Read .npi file","python_name":"napari_npifile.napari_reader:get_reader","short_title":null,"category":null,"icon":null,"enablement":null}],"readers":[{"command":"napari-npifile.read_npi","filename_patterns":["*.npi"],"accepts_directories":false}],"writers":null,"widgets":null,"sample_data":null,"themes":null,"menus":{},"submenus":null,"keybindings":null,"configuration":[]},"package_metadata":{"metadata_version":"2.4","name":"napari-npifile","version":"0.2.0","dynamic":["license-file"],"platform":null,"supported_platform":null,"summary":"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.","description":"# Napari Plugin `napari-npifile`\n\n[![PyPI - Version](https://img.shields.io/pypi/v/napari-npifile?style=flat-square)](https://pypi.org/project/napari-npifile/)\n[![Source Code at Codeberg](https://img.shields.io/badge/codeberg.org-%20?style=flat-square&logo=codeberg&label=source%20code&color=blue&link=https%3A%2F%2Fcodeberg.org%2Fdhaase-de%2Fnapari-npifile)](https://codeberg.org/dhaase-de/napari-npifile)\n[![napari hub](https://img.shields.io/endpoint?url=https://api.napari-hub.org/shields/napari-npifile)](https://napari-hub.org/plugins/napari-npifile.html)\n\nAllows reading and writing of `.npi` files.\n\n`.npi` files are essentially NumPy arrays packaged together with JSON metadata that\nstores **napari viewer and layer configuration**, such as axis names or colormaps.\n\nThe goal is to keep the simplicity of `.npy` files while preserving useful visualization\nsettings when opening arrays in napari.\n\n## Quick Example\n\n```python\nimport napari_npifile\nimport numpy as np\n\ndata = np.random.random(size=(10, 20, 30, 40))\n\n# save it without napari metadata\nnp.save(\"quick_example.npy\", data)\n\n# save it with napari metadata\nnapari_npifile.write_npi(\n    \"quick_example.npi\",\n    data=data,\n    layer_attributes={\n        \"name\": \"foo\",\n        \"colormap\": \"magma\",\n        \"contrast_limits\": (-0.25, 1.25),\n    },\n    viewer_settings={\n        \"axis_labels\": [\"T\", \"Z\", \"Y\", \"X\"],\n        \"scale_bar_properties\": {\"visible\": True, \"unit\": \"px\", \"font_size\": 40.0},\n    },\n)\n\n# and drag and drop it into napari\n#    |\n#    |\n#    v\n```\n\n![quick_example.gif](https://codeberg.org/dhaase-de/napari-npifile/raw/branch/main/quick_example.gif)\n\n## Motivation\n\nWhen working on image analysis tasks in Python it is common to save temporary NumPy\narrays such as:\n\n* image stacks\n* neural network tensors\n* intermediate processing results\n* ...\n\nto `.npy` files, which can easily be opened in [napari](https://napari.org/) via\ndrag-and-drop.\n\nHowever, `.npy` files contain no visualization metadata. Each time a file is opened,\ndisplay settings (colormap, contrast limits, etc.) must be configured manually.\n\n`.npi` files solve this by storing the arrays together with napari viewer metadata.\n\n## Installation\n\n```console\npip install --upgrade napari-npifile\n```\n\n**Note**: napari is **not** included as a dependency, to allow writing `.npi` files\neven if napari is not installed (e.g., on headless servers).\n\nTo view `.npi` files in napari, you must install napari separately:\n\n```console\npip install napari\n```\n\nSee the\n[napari installation instructions](https://napari.org/stable/tutorials/fundamentals/installation.html)\nfor details.\n\n## Usage\n\n### Reading `.npi` files in napari\n\nOnce `napari-npifile` is installed, `.npi` files can be opened in napari like any other\nsupported format:\n\n1. via drag-and-drop,\n2. via the `File -> Open` menu,\n3. via the napari console using `viewer.open(\"myfile.npi\", plugin=\"napari-npifile\")`, or\n4. programmatically using:\n    ```python\n    import napari\n\n    viewer = napari.Viewer()\n    viewer.open(\"myfile.npi\", plugin=\"napari-npifile\")\n\n    napari.run()\n    ```\n\nWhen loaded, each layer in the `.npi` file becomes a separate napari layer. Layer\nsettings (colormap, contrast limits, gamma, name, etc.) and viewer settings (axis\nlabels, camera settings, scale bar settings, etc.) are applied automatically.\n\n### Writing `.npi` files from Python (no napari required)\n\n`napari-npifile` provides a main writer class `NpiWriter_v1` for full control, as well\nas the convenience wrappers `write_npi`, `write_multilayer_npi` into a `.npi` file.\n\n#### When to use what:\n\nThe convenience wrapper functions (`write_npi`, `write_multilayer_npi`) are useful for\ncases where all the data to be stored is available upfront. Use `write_npi` for writing\na single layer and `write_multilayer_npi` for multiple layers.\n\nThe class `NpiWriter_v1` allows to **incrementally** write layers into a single `.npi`\nfile over time, making it ideal for loops.\n\n#### Convenience wrapper `write_npi` (single-layer):\n\n```python\nimport napari_npifile\nimport numpy as np\n\ndata = np.random.random((10, 20, 30))\n\nnapari_npifile.write_npi(\n    \"single.npi\",\n    data=data,\n    layer_attributes={\"name\": \"layer0\", \"colormap\": \"magma\"},\n    viewer_settings={\"axis_labels\": [\"Z\", \"Y\", \"X\"]},\n)\n```\n\n#### Convenience wrapper `write_multilayer_npi` (multi-layer):\n\n```python\nimport napari_npifile\nimport numpy as np\n\ndata_seq = [\n    np.random.random((10, 20, 30)),\n    np.random.random((10, 20, 30)),\n]\nlayer_attributes_seq = [\n    {\"name\": \"first\", \"colormap\": \"gray\"},\n    {\"name\": \"second\", \"colormap\": \"green\"},\n]\n\nnapari_npifile.write_multilayer_npi(\n    \"multi.npi\",\n    data_seq=data_seq,\n    layer_attributes_seq=layer_attributes_seq,\n    viewer_settings={\"axis_labels\": [\"Z\", \"Y\", \"X\"]},\n)\n```\n\n#### Full general example using `NpiWriter_v1`:\n\n```python\nimport napari_npifile\nimport numpy as np\n\n# prepare writer\nwriter = napari_npifile.NpiWriter_v1(\n    out_path=\"out.npi\",\n    exist_ok=True,\n)\n\n# viewer settings are saved once and are used for all layers\nwriter.write_viewer_settings(\n    {\n        \"axis_labels\": [\"T\", \"K\", \"Z\", \"Y\", \"X\"],\n        \"grid_properties\": {\"enabled\": True, \"shape\": (1, -1), \"spacing\": 0.1},\n        \"scale_bar_properties\": {\"visible\": True, \"unit\": \"px\", \"gridded\": True},\n    }\n)\n\n# layers are written one at a time, each with individual attributes\ndata1 = np.random.random(size=(3, 10, 20, 30, 40))\nwriter.write_layer(data=data1, attributes={\"colormap\": \"cyan\", \"name\": \"foo\"})\n\ndata2 = np.random.random(size=(1, 10, 20, 30, 40))\nwriter.write_layer(data=data2, attributes={\"colormap\": \"green\", \"name\": \"bar\"})\n```\n\n#### Incremental writing\n\nWhen using `NpiWriter_v1`, the `.npi` archive is created immediately during\ninitialization. From that point on, the file is always a valid `.npi` archive.\n\nEach call to `write_layer` or `write_viewer_settings` simply appends new\nentries to the ZIP archive.\n\n### Compression (optional)\n\n`.npi` files are standard ZIP archives and can therefore optionally use ZIP compression.\n\nCompression can be configured when creating an archive. The same two parameters\n`compression` and `compresslevel` are available in all writer entry points and have the\nidentical meaning and function as defined in the underlying\n[zipfile Python standard library](https://docs.python.org/3/library/zipfile.html).\n\nBelow follows a short description. For more details about `compression` and \n`compresslevel`, please see\nhttps://docs.python.org/3/library/zipfile.html#zipfile-objects.\n\n#### Parameter `compression`\n\nControls the\n[ZIP compression method as supported by zipfile](https://docs.python.org/3/library/zipfile.html#zipfile-objects).\n\nAccepted values:\n* `None`: no compression (`ZIP_STORED`)\n* `False`: no compression (`ZIP_STORED`)\n* `True`: standard ZIP compression (`ZIP_DEFLATED`)\n* `int`: a `zipfile.ZIP_*` numeric constant (e.g., `zipfile.ZIP_DEFLATED` or\n  `zipfile.ZIP_LZMA`)\n* `str`: case-insensitive *name* of a `zipfile.ZIP_*` numeric constant, without the \n  leading `\"ZIP_\"` (e.g., `\"deflated\"`, `\"lzma\"`, or `\"bzip2\"`)\n\n#### Parameter `compresslevel`\n\nOptional compression level (`int` or `None`) used by some compression methods.\n\nThe allowed range depends on the chosen ZIP method and follows the\n[limits defined by `zipfile`](https://docs.python.org/3/library/zipfile.html#zipfile-objects).\nIf `None` (the default), the default level of the \nrespective compressor is \nused.\n\n#### Examples\n\nStandard ZIP compression:\n```python\nnapari_npifile.write_npi(\n    out_path=\"compressed.npi\",\n    data=data,\n    compression=True,\n)\n```\n\nUsing an explicit compression method:\n\n```python\nimport zipfile\n\nnapari_npifile.write_npi(\n  out_path=\"compressed.npi\",\n  data=data,\n  compression=\"lzma\",\n)\n\n# both are identical\n\nnapari_npifile.write_npi(\n  out_path=\"compressed.npi\",\n  data=data,\n  compression=zipfile.ZIP_LZMA,\n)\n```\n\nSpecifying a compression level:\n```python\nnapari_npifile.write_npi(\n    out_path=\"compressed.npi\",\n    data=data,\n    compression=\"DEFLATED\",\n    compresslevel=9,\n)\n```\n\n#### Notes\n\n* The default behavior is no compression (`ZIP_STORED`).\n* Compression can reduce disk usage but may increase write and read times.\n* Since `.npi` stores raw `.npy` arrays, compression effectiveness depends on the \n  structure of the data.\n\n## File Format\n\n### Overview\n\n`.npi` files are ZIP archives containing raw NumPy array data (stored\nas `.npy` files) together with napari viewer and layer metadata stored as JSON files.\n\n### Versions\n\nEvery `.npi` file contains a root `metadata.json` with a mandatory `\"npi_format\"`\nfield. This format versioning ensures backward compatibility: the reader uses it to\nselect the appropriate parser for each format.\n\nIn addition, the class `NpiWriter_v1` will remain indefinitely to ensure backward\ncompatibility. If a new `.npi` format version is introduced, a new writer class will be\nadded alongside it, leaving existing code using `NpiWriter_v1` fully functional.\n\nVersion history:\n\n| Format Version | `napari-npifile` version | Comment                                                                                                                                               |\n|----------------|--------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `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. |\n\n### Current Version Details (`v1`)\n\n#### Archive Layout\n\n```text\nexample.npi                  # this is just a ZIP archive\n├── metadata.json            # contains {\"npi_format\": \"v1\"}\n├── viewer_settings.json     # dictionary with global viewer settings - see below\n└── layers\n    ├── 00000000             # each layer is stored under a sequential numeric key\n    │   ├── data.npy         # NumPy array as saved via np.save\n    │   └── attributes.json  # layer attributes (as in napari.layers.Image)\n    └── 00000001\n        ├── data.npy\n        └── attributes.json\n```\n\nAll metadata files are JSON files which contain a dictionary.\n\n#### Metadata File `metadata.json`\n\nOnly contains `{\"npi_format\": \"v1\"}`.\nIt is used by the reader to select the correct parser for the file.\n\n#### Metadata File `viewer_settings.json`\n\nStores global napari viewer settings.\nAll fields are **optional**; if missing, the reader does not change the current napari\nconfiguration.\n\n| Field                     | Type        | Notes                                                                                                                                                                                                              |\n|---------------------------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `clear_existing_layers`   | `bool`      | If `True`, removes all existing layers before adding new ones                                                                                                                                                      |\n| `hide_existing_layers`    | `bool`      | If `True`, hides all existing layers before adding new ones                                                                                                                                                        |\n| `title`                   | `str`       | Napari window title                                                                                                                                                                                                |\n| `ndisplay`                | `int`       | `2` or `3` (for 2D or 3D mode)                                                                                                                                                                                     |\n| `axis_labels`             | `list[str]` | One label per axis (e.g., `[\"T\", \"Z\", \"Y\", \"X\"]`)                                                                                                                                                                  |\n| `dims_set_point`          | `dict`      | Optional subfields as in [napari's set_point](https://napari.org/stable/api/napari.components.Dims.html#napari.components.Dims.set_point), i.e. `axis: int or Sequence[int]` and `value: float or Sequence[float]` |\n| `camera_properties`       | `dict`      | Optional subfields as in [napari's Camera](https://napari.org/stable/api/napari.components.Camera.html) (e.g., `{\"center\": (0.0, 10.0, 20.0)}`)                                                                    |\n| `grid_properties`         | `dict`      | Optional subfields as in [napari's GridCanvas](https://github.com/napari/napari/blob/main/src/napari/components/grid.py) (`enabled: bool`, `shape: tuple[int, int]`, `stride: int`, `spacing: float`)              |\n| `scale_bar_properties`    | `dict`      | Optional subfields as in [napari's ScaleBarOverlay](https://github.com/napari/napari/blob/main/src/napari/components/overlays/scale_bar.py) (e.g., `visible: bool`, `unit: str`, `gridded: bool`)                  |\n| `text_overlay_properties` | `dict`      | Optional subfields as in [napari's TextOverlay](https://github.com/napari/napari/blob/main/src/napari/components/overlays/text.py) (e.g., `visible: bool`, `text: str`, `gridded: bool`, `font_size: float`)       |\n\n#### Metadata File(s) `layers/*/attributes.json`\n\nLayer-specific settings for each layer. All fields are **optional**; if missing, the\nreader uses napari defaults.\n\n[Any keyword argument accepted by napari’s Image layer](https://napari.org/stable/api/napari.layers.Image.html#napari-layers-image)\n(except `data`) can be specified, e.g.:\n\n* `name: str`\n* `colormap: str` (any napari colormap)\n* `contrast_limits: tuple[float, float]`\n* `gamma: float`\n* `blending: str (\"translucent\", \"additive\", \"opaque\", ...)`\n* ...\n\n## Design Principles and Limitations\n\nKey principles for the `.npi` format and `napari-npifile` are:\n\n* **Simplicity and accessibility**: `.npi` files are standard ZIP archives containing\n  NumPy arrays and JSON metadata. Thus, the files can easily be inspected, read, and\n  created even without this library.\n* **Minimal dependencies**: Only NumPy and napari (for viewing) are required.\n* **Tight napari integration**: `.npi` stores viewer and layer settings exclusively for\n  napari, and thus allows fine-grained control over how arrays are displayed.\n* **Backward compatibility**: Every `.npi` includes a format version, ensuring future\n  readers can correctly handle older files.\n* **Headless-friendly writing**: `.npi` files can be created without napari installed,\n  which is useful for server-side or automated workflows.\n\nThese design choices naturally impose some limitations:\n\n* **No access optimization**: `.npi` uses plain `np.save`/`np.load` from ZIP\n  archives and thus can't compete with optimized formats for large arrays.\n* **Generic compression**: `.npi` allows for standard ZIP compression. \n  Formats designed specifically for numerical arrays may achieve better \n  compression or faster access depending on the dataset.\n* **Viewer-centric metadata**: Metadata is tailored specifically to napari; other\n  viewers or tools require extra work to interpret it.\n\nIn short, `.npi` prioritizes simplicity and tight napari integration over maximal\nperformance. Users needing advanced storage features or highly\nefficient random access may prefer formats such as [Zarr](https://zarr.dev/) or\n[HDF5](https://en.wikipedia.org/wiki/Hierarchical_Data_Format), while\n[OME-TIFF](https://ome-model.readthedocs.io/en/stable/) provides more generic\nmetadata handling.\n\n## Known Issues\n\n* Setting the viewer slice position (`viewer.dims.set_point`) and camera properties does\n  not work when there are no pre-existing layers or when `clear_existing_layers` is\n  `True`.\n* When using `NpiWriter_v1` to write an `.npi` file, the method `write_viewer_settings` \n  can only be called once, because the underlying\n  [`zipfile` library does not allow to modfify a file in an existing ZIP file](https://stackoverflow.com/a/25739108).  \n\n## Todo / Ideas\n\n* Add support to write `.npi` files directly from napari.\n* Add CLI to convert existing `.npy` files to `.npi` files (and back).\n\n## Changelog\n\n* `v0.2.0` (2026-03-17)\n  * added support for optional ZIP compression\n* `v0.1.1` (2026-03-14)\n  * fixed missing project description and formatting errors in the readme\n* `v0.1.0` (2026-03-14)\n  * initial release with standalone read/write and napari reader support\n\n## Related Discussions / Projects\n\n* [napari GitHub issue: Changing viewer dimension when adding a new layer #6127](https://github.com/napari/napari/issues/6127)\n* [image.sc forum: Saving volumetric data with voxel size, colormap, annotations](https://forum.image.sc/t/saving-volumetric-data-with-voxel-size-colormap-annotations/85537/24)\n\n## License\n\n`napari-npifile` is released under the MIT License. See `LICENSE.txt` for full details.\n","description_content_type":"text/markdown","keywords":null,"home_page":null,"download_url":null,"author":null,"author_email":"Daniel Haase <git@dhaase.de>","maintainer":null,"maintainer_email":null,"license":null,"classifier":["Framework :: napari","Topic :: File Formats","Topic :: Scientific/Engineering","Topic :: Scientific/Engineering :: Image Processing","Topic :: Scientific/Engineering :: Visualization","Intended Audience :: Science/Research","Programming Language :: Python","Programming Language :: Python :: 3 :: Only","Programming Language :: Python :: 3.10","Programming Language :: Python :: 3.11","Programming Language :: Python :: 3.12","Programming Language :: Python :: 3.13","Operating System :: OS Independent","Operating System :: Microsoft :: Windows","Operating System :: POSIX","Operating System :: Unix","Operating System :: MacOS"],"requires_dist":["numpy>=1.13.3"],"requires_python":">=3.10","requires_external":null,"project_url":["homepage, https://pypi.org/project/napari-npifile/","documentation, https://codeberg.org/dhaase-de/napari-npifile/src/branch/main/README.md","repository, https://codeberg.org/dhaase-de/napari-npifile","changelog, https://codeberg.org/dhaase-de/napari-npifile/src/branch/main/README.md"],"provides_extra":null,"provides_dist":null,"obsoletes_dist":null},"npe1_shim":false}