MARATTO

other · Zenodo (CERN European Organization for Nuclear Research)

napari: a multi-dimensional image viewer for Python

Abstract

napari 0.7.0 ⚠️ Note: these release notes are still in draft while 0.7.0 is in release candidate testing. ⚠️ Tue, Mar 10, 2026 We're happy to announce the release of napari 0.7.0! napari is a fast, interactive, multi-dimensional image viewer for Python. It's designed for browsing, annotating, and analyzing large multi-dimensional images. It's built on top of Qt (for the GUI), vispy (for performant GPU-based rendering), and the scientific Python stack (numpy, scipy). For more information, examples, and documentation, please visit our website, https://napari.org. napari follows EffVer (Intended Effort Versioning); this is a Macro release containing awesome new features, but may require dedication of some significant time when upgrading projects to use this version. Highlights Breaking Changes Transition to npe2 plugin engine 🔌 In 0.6.0 we began the process of deprecating npe1 (napari-plugin-engine). In all 0.6.x releases, npe1 plugins were automatically converted to npe2 by default, and users could turn off the use_npe2_adaptor setting to continue using npe1 plugins without auto-conversion. In 0.7.0 this setting is being removed (PR #8448), and plugins will only continue to function if they can be auto-converted to npe2. Most plugins will be unaffected, but those that rely on import-time behaviour may not work as expected. If a plugin is relying on import-time behaviour, it may be able to replicate this using the new startup scripts functionality added in 0.6.5 (#8188). If you encounter conversion issues in a plugin you rely on, please contact the plugin authors to encourage them to migrate their plugin to the npe2 system. This change has been a long time coming, and it's allowed us to remove thousands of lines of tangled and confusing legacy code. Now that we have, it's unleashed the potential for massive improvements to file opening and saving in napari, and exciting new features for our plugin infrastructure. Stay tuned! For more details on this change and how it affects plugins, see the detailed guide. If you are a plugin author and your plugin is not yet npe2-compatible, please see our npe2 migration guide, and, if you encounter any issues, get in touch in our Plugins Zulip chat channel or by coming to one of our community meetings. Negative axis labels? A real positive If you've ever loaded data of mixed dimensionality in napari, like a TYX volume alongside a YX segmentation, you may have noticed the default axis labels didn't quite line up: | axes | 0 | 1 | 2 | |--------|---|---|---| | volume | 0 | 1 | 2 | | segmt | | 0 | 1 | That's because napari used 0-based indexing for its viewer axis labels, which breaks down when layers have different numbers of dimensions. With #8565, viewer axis labels now use negative indexing by default, combining Python's negative indexing semantics with NumPy's array broadcasting semantics. The last axis is always -1, the second-to-last is always -2, and so on: | axes | 0 | 1 | 2 | |--------|----|----|----| | volume | -3 | -2 | -1 | | segmt | | -2 | -1 | This means axis labels stay consistent as you add or remove layers of different dimensionality -- axis -1 is always your last axis. This also fixes a long-standing bug where axis labels could end up duplicated when mixing layers of different dimensionality (#6569). You'll notice this change in the dims slider labels, the axis overlay, and the dims popup widget. If you already label your axes with your own names (e.g. z, y, x), nothing's changed. For everyone else, we have consistency at last! What's in an angle? The truth! Fixed camera angles 🎥 If you've ever set up the camera to take that perfect publication-worthy photo of your data (and taken the time to query the camera angles), you may have noticed they seemed... off. That's because they were! Very... off. This was due to a long-standing bug in how we calculated our camera angles, fueled in part by some arcane vispy axis-swapping tomfoolery, and in part by napari's starting position of viewer.camera.angles = (0, 0, 90). Good news! With #8281, angles make sense again. The default camera angles are (0, 0, 0), and they move intuitively -- so viewer.camera.angles = (10, 0, 0) actually represents a 10 degree rotation around the -3rd dimension. What a time to be alive! Old versions of napari: New and sane: All rotations are now right-handed (counterclockwise when the axis points towards the viewer), with automatic sign-flipping for flipped camera views. Now for the bad news... After many (and we do mean many) attempts, we realized we couldn't provide legacy conversion functions to get you to and from the original camera angles. Therefore, this is a breaking change. If you had scripts or notebooks setting up angles for screenshots, or if you've got workshop materials or tutorials with preset angles, they'll need to be updated. Any existing code using viewer.camera.angles = (z, y, x) will now produce a different view than before. Limiting self.events callbacks In #8672, we changed how we emit events from the parent self.events group. Previously, connecting a callback to an EventedModel's top-level event group (e.g. model.events.connect(callback)) would cause that callback to fire multiple times when a single assignment triggered multiple dependent fields. For example: class MyModel(EventedModel): a: int @property def b(self): return self.a * 2 @b.setter def b(self, value): self.a = value // 2 model = MyModel(a=1) model.events.connect(my_callback) model.a = 4 # previously called my_callback twice (once for 'a', once for 'b') # now calls my_callback once The callback connected to model.events will now be called exactly once, with the event type set to the first changed field and value set to its new value. Callbacks connected to specific field events (e.g. model.events.a.connect(...)) are unaffected and continue to work as before. If you connect to model.events directly and relied on receiving one call per dependent field change, you will need to connect to the individual field events instead. New features & widgets What's my metadata? Where's my metadata? napari-metadata to the rescue With a lot of work from our community contributor, Carlos Mario Rodriguez Reza (https://github.com/carlosmariorr), and our venerable community manager Tim Monko (https://github.com/timmonko), napari now has a metadata viewing and editing plugin included in our napari[all] installation and our bundle (PR #8576). Open the Layer metadata widget from the Plugins menu and you can view File information, and view and edit Axes metadata such as axis labels, translation and scale! You can also use the widget to copy specified metadata across to other layers. Check out the README for some usage documentation, and feel free to open an issue to request new features -- we're actively improving this plugin so, more to come! (Layer) Features galore Prior to 0.7.0, our Features table widget only supported showing individual selected layer features. With #8189, courtesy of Marcelo Zoccoler (https://github.com/zoccoler), the widget will display features of all selected layers! The layer's name is displayed in an additional column, so you always know what you're looking at, and you can choose to display only the shared feature columns across all layers. Pretty slick! Smarter new layer buttons - inheriting from selected layers Prior to 0.7.0, creating a new layer Points, Shapes or Labels layer would give you a layer with extent and dimensionality equal to the union of all currently open layers, and with none of the other spatial information (scale, units, etc.) inherited. Now, with #8357 and #8702 you can create new Shapes, Points or Labels layer that inherits from a selected layer (or a combination of selected layers). Shapes & Points If you have one layer selected, your new Shapes or Points layer will copy all spatial information from its ancestor, ready for annotating! If you have multiple layers selected, only scale is copied. If you wish to recover the original behavior, select all existing layers before creating your new layer. Deselecting all layers gives you a layer with only the number of dimensions inherited, and no other properties. Labels Labels layers inherit all spatial information when a single Image or Labels layer is selected. When multiple Image or Labels layers are selected, or the selection includes any combination of other layer types, the new Labels layer will span their extent -- take note, this layer could be huge! The Labels button is disabled when layers are present in the viewer and none are selected. (You can still create a (512 x 512) Labels layer when there are no layers present). Visual cues #8723 ensures this change is not invisible! When your selection will result in full inheritance of spatial information for the new layer, the new layer button will be highlighted. The highlight color will become brighter when your selection will result in the new layer only inheriting the extent of your existing selection. If you're lost in the inheritance madness, you can also hover over the buttons to get details about the behavior. PS -- You can now also create these new layers from the File -> New Layer menu! Better text overalys 🔡 With #8236, we've not only refactored text overlays so they're easier to implement, but we've also introduced two new long-requested overlays: the layer name overlay, and an overlay for the current slice. Together, they make generating publication-ready figures much easier! Try it yourself: import napari v = napari.Viewer() v.grid.enabled = True ll = v.open_sample('napari', 'cells3d') for l in ll: l.name_overlay.visible = True v.scale_bar.visible = True v.scale_bar.gridded = True v._overlays['current_slice'].visible = True v._overlays['current_slice'].gridded = True v.dims.axis_labels = ['z', 'y', 'x'] Note: the v._overlays att

Read the original research

This page summarises published work. The authoritative version sits with the publisher.

DOI: 10.5281/zenodo.18957922

Is something wrong with this record? Report it or request removal.

Discussion

Discuss this research

Have you built on this work, tried to replicate it, or seen it applied in practice? Share what you know. Verified researchers and MARATTO™ domain experts can open a discussion, and any member can reply. Contributions are reviewed before they appear.

No discussion yet. Open the first thread.