For developers#

Operators#

All operators derive from the BaseOperator class. It is a very minimal class providing a common interface for all operators.

The second level of the inheritance hierarchy is defined by what the operator is designed to accept, and what it should produce:

1. ImageOperator#

xr.DataArrayxr.DataArray

We don’t make the difference between images, mask and label maps when we inherit from this class. For example:

  • NormalizeOperator takes an image and produces an image.

  • EDTOperator takes a binary mask and produces a distance map.

  • RemapLabelsOperator takes a label map and produces a label map.

2. MeasurementsOperator#

xr.DataArraypd.DataFrame

We use this class as root for operators that either produce measurements for a labeled image or a points cloud.

  • FindExtremaOperator takes an image and produces a points cloud.

  • MeasureLabelsOperator takes a label map and produces a table of measurements.

3. PointsOperator#

pd.DataFramepd.DataFrame

The operators deriving from this class are designed to process points clouds, and produce a new points cloud. For example:

  • PointsTrackingOperator performs the tracking of points over time.

  • MergeClosePointsOperator merges points that are closer than a given distance.

Global vs. frame-wise operators#

Operators can be global or local. Global operators (like the crop operator) handle the whole block of data in one go, since you may want to make a crop even on the time axis. Local operators (like the connected components labeling operator) have to be applied frame by frame.

If you are designing a local operator, the only method that you should have to override is _applyToFrame. It receives by default a time-less block of data and the logic in the parent class is already able to re-assemble the results into a time-aware block of data.

In the case of a global operator, you will have to override the _run method.

To update the progress bar when you pass a function to create_worker, Napari needs the function to be a generator, which implies that you have to yield something at each iteration (or just 0 at the end).