This notebook is part of the PyImageJ Tutorial Series, and assumes familiarity with the ImageJ API. Dedicated tutorials for ImageJ can be found here.

6 Working with Images

These methods can be helpful, especially if you do not know beforehand of which type your image is. First let’s initialize ImageJ, import some packages and load in some test data.

import imagej

# initialize ImageJ2
ij = imagej.init(mode='interactive')
print(f"ImageJ2 version: {ij.getVersion()}")
ImageJ2 version: 2.15.0/1.54f

6.1 Opening images with

Images can be opened with ImageJ with This returns the opened image as an ImageJ2 Dataset (a Java object). Using allows you to take advantage of the Bio-Formats image read/write and SCIFIO image conversion when using Fiji endpoints (e.g. sc.fiji:fiji). Lets load some data:

The sample images used in this notebook are available on the PyImageJ GitHub repository here.

# load local test data
dataset ='sample-data/test_timeseries.tif')

# load web test data
web_image ='')
[INFO] Populating metadata

You can also open images using the legacy ImageJ opener with ij.IJ.openImage() which will return the older ImagePlus Java object type instead of the Dataset Java object.

# open image with legacy opener
imp = ij.IJ.openImage('sample-data/test_timeseries.tif')

6.2 Opening a series image with Bio-Formats Importer

The Bio-Formats Importer can be access directly by importing the loci.plugins.BF and classes with scyjava. You can configure how Bio-Formats imports your images by setting the attributes of an initialized ImporterOptions object before passing it to the importer. For information on what options are available to be set please visit the Javadoc.

The following example below uses the Bio-Formats Importer to open all images in a series image as virtual images.

import imagej
import scyjava as sj

# initialize Fiji
ij = imagej.init('sc.fiji:fiji', mode='interactive')

# get the BioFormats Importer
BF = sj.jimport('loci.plugins.BF')
options = sj.jimport('')() # import and initialize ImporterOptions

# open the series
imps = BF.openImagePlus(options)

This returns an ImagePlus[] which can be iterated in a for loop like so:

for imp in imps:

6.3 Displaying images with PyImageJ

PyImageJ has two primary ways of displaying images after they have been loaded:

  • ImageJ’s image viewer (ij.ui().show())

    • Java

    • Supports n-dimensional image data

    • Only available when PyImageJ is initialized in interactive mode or gui mode for MacOS users

  • matplotlib’s pyplot (

    • Python

    • Supports 2D image data (e.g. X and Y only)

Let’s display the images we loaded.

# show the web image

The function also supports passing a custom color map e.g. cmap='hsv' with the same syntaxes as matplotlib.

# show the web image with 'hsv' cmap, cmap='hsv')
# show the 4D dataset in ImageJ's viewer

Note that ImageJ’s viewer only works locally.

6.4 Displaying images dynamically with ipywidgets

The ipywidgets library lets us add interactive elements such as sliders to our notebook. Let’s build a plane viewer for N-dimensional data.

def plane(image, pos):
    Slices an image plane at the given position.
    :param image: the image to slice
    :param pos: a dictionary from dimensional axis label to element index for that dimension
    # Convert pos dictionary to position indices in dimension order.
    # See
    p = tuple(pos[image.dims[d]] if image.dims[d] in pos else slice(None) for d in range(image.ndim))
    return image[p]

This plane function lets us slice an image using its dimensional axis labels. If you’re unsure what the axis labels are, you can usually get them by printing the dims attribute of the image object.

# get image axis attributes
('X', 'Y', 'Channel', 'Time')
# display channel 1, slice 10, {'Channel': 1, 'Time': 10}))

Now that we have this ability, we can script some ipywidget sliders around it:

import ipywidgets

def _axis_index(image, *options):
    axes = tuple(d for d in range(image.ndim) if image.dims[d].lower() in options)
    if len(axes) == 0:
        raise ValueError(f"Image has no {options[0]} axis!")
    return axes[0]

def ndshow(image, cmap=None, x_axis=None, y_axis=None, immediate=False):
    if not hasattr(image, 'dims'):
        # We need dimensional axis labels!
        raise TypeError("Metadata-rich image required")

    # Infer X and/or Y axes as needed.
    if x_axis is None:
        x_axis = _axis_index(image, "x", "col")
    if y_axis is None:
        y_axis = _axis_index(image, "y", "row")

    # Build ipywidgets sliders, one per non-planar dimension.
    widgets = {}
    for d in range(image.ndim):
        if d == x_axis or d == y_axis:
        label = image.dims[d]
        widgets[label] = ipywidgets.IntSlider(description=label, max=image.shape[d]-1, continuous_update=immediate)

    # Create image plot with interactive sliders.
    def recalc(**kwargs):
        print("displaying"), kwargs), cmap=cmap)
    ipywidgets.interact(recalc, **widgets)
# display data with ipywidgets

6.5 Displaying images via itkwidgets

The itkwidgets package provides a powerful viewer for 2D and 3D images, point sets, and geometry. Using it is as simple as importing the view function from itkwidgets, then calling view(image).

Note: itkwidgets only works with Python images, not Java images. Fortunately, you can use to convert a Java image to Python before displaying it.

Note: To run itkwidgets, please install it manually. This can be done with pip (pip install itkwidgets) or on conda by updating environment.yml.

import itkwidgets


6.6 Displaying images via napari

This section does not currently work on macOS. See imagej/pyimagej#197 for details.

The napari package provides an external viewer for N-dimensional image data. Using it is as simple as importing the view_image function from napari, then calling view_image(image).

Note: Like itkwidgets, napari only supports Python images, not Java images. You will need to convert them from Java to Python using

Also be aware that as of this writing, napari does not recognize xarray dimensional axis labels, and it also wants a different dimension order from the scikit-image convention; for cells, using (ch, pln, row, col) works well. You can use numpy.moveaxis to adjust the dimension order.

NOTE: To run napari, please install it manually. This can be done with pip (pip install napari) or on conda by updating environment.yml.

import napari

Viewer(camera=Camera(center=(0.0, 203.5, 202.5), zoom=1.2189469046045385, angles=(0.0, 0.0, 90.0), perspective=0.0, mouse_pan=True, mouse_zoom=True), cursor=Cursor(position=(1.0, 1.0), scaled=True, size=1, style=<CursorStyle.STANDARD: 'standard'>), dims=Dims(ndim=2, ndisplay=2, last_used=0, range=((0.0, 408.0, 1.0), (0.0, 406.0, 1.0)), current_step=(203, 202), order=(0, 1), axis_labels=('0', '1')), grid=GridCanvas(stride=1, shape=(-1, -1), enabled=False), layers=[<Image layer 'Image' at 0x7be81b13f650>], help='use <2> for transform', status='Ready', tooltip=Tooltip(visible=False, text=''), theme='dark', title='napari', mouse_over_canvas=False, mouse_move_callbacks=[], mouse_drag_callbacks=[], mouse_double_click_callbacks=[], mouse_wheel_callbacks=[<function dims_scroll at 0x7be83029dbc0>], _persisted_mouse_event={}, _mouse_drag_gen={}, _mouse_wheel_gen={}, keymap={})

6.7 Inspecting image data

The sample data test_timeseries.tif is a 4D (X, Y, Channel, Time) dataset of a HeLa cell infected with a modified HIV-1 fluorescent reporter virus. This reporter virus is designed to express two flourscent proteins upon viral gene expression. Here is the breakdown of the channels:






Nuclear marker



Early viral gene expression



Late viral gene expression / viral particle formation

Let’s first take a look at the test_timeseries.tif image data and get important image information such as dimension and shape:

def dump_info(image):
    """A handy function to print details of an image object."""
    name = if hasattr(image, 'name') else None # xarray
    if name is None and hasattr(image, 'getName'): name = image.getName() # Dataset
    if name is None and hasattr(image, 'getTitle'): name = image.getTitle() # ImagePlus
    print(f" name: {name or 'N/A'}")
    print(f" type: {type(image)}")
    print(f"dtype: {image.dtype if hasattr(image, 'dtype') else 'N/A'}")
    print(f"shape: {image.shape}")
    print(f" dims: {image.dims if hasattr(image, 'dims') else 'N/A'}")

 name: test_timeseries.tif
 type: <java class 'net.imagej.DefaultDataset'>
dtype: <java class 'net.imglib2.type.numeric.integer.UnsignedShortType'>
shape: (250, 250, 3, 15)
 dims: ('X', 'Y', 'Channel', 'Time')

The same methods can also be used on Python objects as well (where dimension information is available):

# convert the ImageJ image (Java) to an xarray.DataArray (Python)
xarr =

# dump info on xarray.DataArray result from
 name: test_timeseries.tif
 type: <class 'xarray.core.dataarray.DataArray'>
dtype: uint16
shape: (15, 250, 250, 3)
 dims: ('t', 'row', 'col', 'ch')

Some important points to note here!

  1. The datasets type is net.imagej.DefaultDataset, a Java class. Notably, this class implements the net.imagej.Dataset interface, the primary image data type of ImageJ2.

  2. The datasets’s dtype (i.e. the type of its elements, i.e. what kind of sample values it has) is UnsignedShortType, the ImgLib2 class for unsigned 16-bit integer data (i.e. uint16) with integer values in the [0, 65535] range.

  3. The image is 4-dimensional, 250 × 250, 3 channels, and 15 time points.

  4. Note the difference in shape and dimension order between the ImageJ and xarray images. ImageJ images use [X, Y, Channel, Z, Time] dimension labels and order. NumPy images use [t, pln, row, col, ch] where row and col correspond to Y and X respectively.

💡 You can save your data with, filepath) to save your image data to disk.

6.8 Passing image data from Python to Java

In many cases, you will have image data in Python already as a NumPy array, or perhaps as an xarray, and want to send it into Java for processing with ImageJ routines. This PyImageJ tutorial assumes you are somewhat familiar with NumPy already; if not, please see e.g. this W3Schools tutorial on NumPy to learn about NumPy before proceeding.

In the next cell, we will open an image as a NumPy array using the scikit-image library.

import skimage
cells =
 name: N/A
 type: <class 'numpy.ndarray'>
dtype: uint16
shape: (60, 2, 256, 256)
 dims: N/A

Because this sample image is a NumPy array, but not an xarray, it does not have dimensional axis labels. However, scikit-image has defined conventions for the order of dimensions as follows:

(t, pln, row, col, ch)

Where t is time, pln is plane/Z, row is row/Y, col is column/X, and ch is channel.

Now that we are armed with that knowledge, notice that cells actually has a slightly different dimension order, with planar rather than interleaved channels: (pln, ch, row, col). Let’s construct an xarray from this image that includes the correct dimensional axis labels:

import xarray
xcells = xarray.DataArray(cells, name='xcells', dims=('pln', 'ch', 'row', 'col'))
 name: xcells
 type: <class 'xarray.core.dataarray.DataArray'>
dtype: uint16
shape: (60, 2, 256, 256)
 dims: ('pln', 'ch', 'row', 'col')

Now to wrap this image as a Java object. We will use PyImageJ’s function, which wraps the image without making a copy. This means that you can modify the original NumPy array values, and the same changes will automatically be reflected on the Java side as well.

# send image data to Java
jcells =
jxcells =

# dump info
 name: N/A
 type: <java class 'net.imagej.DefaultDataset'>
dtype: <java class 'net.imglib2.type.numeric.integer.UnsignedShortType'>
shape: (256, 256, 2, 60)
 dims: ('X', 'Y', 'Unknown', 'Unknown')

 name: xcells
 type: <java class 'net.imagej.DefaultDataset'>
dtype: <java class 'net.imglib2.type.numeric.integer.UnsignedShortType'>
shape: (256, 256, 2, 60)
 dims: ('X', 'Y', 'Channel', 'Z')

Let’s take a look at what happened here:

  • The cells, a numpy array, became an Img<UnsignedShortType>, an ImgLib2 image type without metadata.

  • The xcells, an xarray, became a Dataset, an ImageJ2 rich image type with metadata including dimensional labels.

In both cases, notice that the dimensions were reordered. PyImageJ makes an effort to respect the scikit-image dimension order convention where applicable, including reordering dimensions between ImageJ2 style and scikit-image style when translating images between worlds.

In ImageJ-style terminology, the ZCYX order becomes XYCZ, because ImageJ2 indexes dimensions from fastest to slowest moving, whereas NumPy indexes them from slowest to fastest moving. In both worlds, iterating over this image’s samples will step through columns first, then rows, then channels, then focal planes—the only difference is the numerical order of the dimensions.

These differences between conventions can be confusing, so pay careful attention to your data’s dimensional axis types.

Note that supports various data types beyond only images:

Python type

Java type




An Img is both a RandomAccessibleInterval and an IterableInterval.



A Dataset is an Img with additional metadata including dimensional axis labels.


Integer, Long, or BigInteger

Destination Java type depends on magnitude of the integer.


Float, Double, or BigDecimal

Destination Java type depends on magnitude and precision of the value.







Converted recursively (i.e., elements of the dict are converted too).



Converted recursively (i.e., elements of the set are converted too).



Converted recursively (i.e., elements of the list are converted too).

But only images are wrapped without copying data.

6.9 Passing image data from Java to Python

To translate a Java image into Python, use:

python_image =

It’s the opposite of

  • Metadata-rich images such as Dataset become xarrays.

  • Plain metadata-free images (e.g. Img, RandomAccessibleInterval) become NumPy arrays.

  • Generally speaking, Java types in the table above become the corresponding Python types.

Let’s compare xcells (original Python image), jxcells (image converted to Java) and a new conversion xcells_2 (jxcells converted back to Python with

# convert jxcells back to Python
xcells_2 =

print("[xcells - original Python image]")
print("\n[jxcells - after conversion to Java]")
print("\n[xcells_2 - conversion back to Python]")
[xcells - original Python image]
 name: xcells
 type: <class 'xarray.core.dataarray.DataArray'>
dtype: uint16
shape: (60, 2, 256, 256)
 dims: ('pln', 'ch', 'row', 'col')

[jxcells - after conversion to Java]
 name: xcells
 type: <java class 'net.imagej.DefaultDataset'>
dtype: <java class 'net.imglib2.type.numeric.integer.UnsignedShortType'>
shape: (256, 256, 2, 60)
 dims: ('X', 'Y', 'Channel', 'Z')

[xcells_2 - conversion back to Python]
 name: xcells
 type: <class 'xarray.core.dataarray.DataArray'>
dtype: uint16
shape: (60, 256, 256, 2)
 dims: ('pln', 'row', 'col', 'ch')

Notice the difference? The xcells dimension order is ZCYX, which flipped to XYCZ when passed to Java. But when passed back to Python, the C dimension was shuffled to the end of the list (i.e. top of the stack). The reason is:

PyImageJ makes a best effort to reorganize image dimensions to match scikit-image conventions. The dimensions TZYXC are shuffled to the top with that precedence; any other dimensions are left in place behind those. This behavior is intended to make it easier to use converted Python images with Python-side tools like scikit-image and napari.

So again: pay careful attention to your data’s dimensional axis types.

6.10 Direct image type conversions

PyImageJ’s to_java() and from_java() Python helper methods will attempt to provide you with the best conversion possible for the image object in question. While this allows for quick conversions between Java and Python objects, you lose the option to specify what output image type to create. For scenarios where a specific image output type is desired its best to use the direct image converters: to_dataset(), to_img(), to_imageplus() and to_xarray(). The direct image converts accept both Java and Python image objects (e.g. net.imagej.Dataset, numpy.ndarray, xarray.DataArray, ij.ImagePlus, etc…) and optionaly a dim_order (a list of dimensions as strings) keyword argument. The dim_order option can be used by a direct image converter to poperly shuffle the dimensions into their expected positions.

For example, RGB images as NumPy arrays do not have any axes or dimension metadata and get mangled during the conversion process to an ImageJ Java object (like a net.imagej.Dataset). This is because the PyImageJ’s default behavior for converting images without axes/dimension information is to assume a general ('t', 'pln', 'row', 'col', 'ch') organization to the data and inverts the order. An RGB image with shape (100, 250, 3) converted to a net.imagej.Dataset without dimension order information will return with shape (3, 250, 100). Attempting to display this in ImageJ’s viewer will produce crazy results as ImageJ expects the first two dimensions to be X and Y. But in this case the first two dimensions are Channel and Y! This can be resolved by performing the same image conversion but with a dim_order=['row', 'col', 'ch'].

Example direct image conversion

# dataset is some n-dimensional net.imagej.Dataset
xarr =

Example direct image conversion with dim_order

# rgb_image is some 3 channel image as a Numpy array
rgb_dataset =, dim_order=['row', 'col', 'ch'])

Converting images between Java, Python and the various image object types is confusing (especially when the dimensions and array shape are likely to change). Below are tables that outline the expected behavior of the direct image converters. For the following tables the input example data is cropped from the test_timeseries.tif example dataset, found in the PyImageJ repository, to shape (250, 100, 3, 15) with dimensions('X', 'Y', 'Channel', 'Time').

Python -> Java direct image conversions

Input type

Input dimensions/shape



Output type

Output dimensions/shape


No dims attribute / (15, 100, 250, 3)




('dim_3', 'dim_2', 'dim_1', 'dim_0') / (3, 250, 100, 15)


No dims attribute / (15, 100, 250, 3)

dim_order=['t', 'row', 'col', 'ch']



('X', 'Y', 'Time', 'Channel') / (250, 100, 15, 3)


No dims attribute/ (15, 100, 250, 3)

dim_order=['t', 'row']



('dim_1', 'dim_0', 'Y', 'Time') / (3, 250, 100, 15)


No dims attribute / (15, 100, 250, 3)




No dims attribute / (3, 250, 100, 15)


No dims attribute / (15, 100, 250, 3)

dim_order=['t', 'row', 'col', 'ch']



No dims attribute / (250, 100, 15, 3)


No dims attribute / (15, 100, 250, 3)

dim_order=['t', 'row']



No dims attribute / (3, 250, 100, 15)


('t', 'row', 'col', 'ch') / (15, 100, 250, 3)




('X', 'Y', 'Time', 'Channe') / (250, 100, 15, 3)


('t', 'row', 'col', 'ch') / (15, 100, 250, 3)

dim_order=['t', 'row', 'col', 'test']



('test', 'X', 'Y', 'Time') / (3, 250, 100, 15)


('t', 'row', 'col', 'ch') / (15, 100, 250, 3)

dim_order=['t', 'row']



('dim_1', 'dim_0', 'Y', 'Time') / (3, 250, 100, 15)


('t', 'row', 'col', 'ch') / (15, 100, 250, 3)




No dims attribute / (250, 100, 15, 3)


('t', 'row', 'col', 'ch') / (15, 100, 250, 3)

dim_order=['t', 'row', 'col', 'test']



No dims attribute / (3, 250, 100, 15)

Java -> Python direct image conversions

Input type

Input dimensions/shape



Output type

Output dimensions/shape


('X', 'Y', 'Channel', 'Time') / (250, 100, 3, 15)

Not supported



('t', 'row', 'col', 'ch') / (15, 100, 250, 3)


('X', 'Y', 'Channel', 'Time') / (250, 100, 3, 15)

Not supported



('t', 'row', 'col', 'ch') / (15, 100, 250, 3)


No dims attribute / (250, 100, 3, 15)

Not supported



('dim_0', 'dim_1', 'dim_2', 'dim_3') / (15, 3, 100, 250)


('X', 'Y', 'C', 'T') / (250, 100, 3, 15)

Not supported



('t' ,'row', 'col', 'ch') / (15, 100, 250, 3)

6.11 Slicing Java and Python images

NumPy supports a powerful slicing operation, which lets you focus on a subset of your image data. You can crop image planes, work with one channel or time point at a time, limit the range of focal planes, and more.

If you are not already familiar with NumPy array slicing, please peruse the NumPy slicing tutorial on W3Schools to get familiar with it.

Here are some quick examples using the test_timeseries.tif 4D dataset with dimensions (X, Y, Channel, Time):





the image without any slicing

(250, 250, 3, 15)

image[:, :, 0, :]

channel 0 (blue - nucleus)

(250, 250, 15)

image[:, :, -1, :]

last channel (green - viral proteins)

(250, 250, 15)

image[100:200, 100:200,: ,:]

cropped 100 x 100 4D image, centered

(100, 100, 3, 15)

Let’s slice and crop the test_timeries.tif (loaded as the dataset Java object) using NumPy array slicing.

# channel 1, time point 3
s1 = dataset[:, :, 0, 3]

# 100 x 100 crop, channel 1, time point 3
s2 = dataset[100:200, 100:200, 0, 3]

print("channel 1, time point 3")
print("100 x 100 crop, channel 1, time point 3")
channel 1, time point 3
100 x 100 crop, channel 1, time point 3

s1 and s2 are Java objects sliced from an image with (X, Y, Channel , Time) dimensions.

<java class 'net.imglib2.view.IntervalView'>
# slice the test data
java_slice = dataset[:, :, 1, 10]
python_slice = xarr[10, :, :, 1]

# print slice shape
print(f"java_slice: {java_slice.shape}")
print(f"python_slice: {python_slice.shape}")
java_slice: (250, 250)
python_slice: (250, 250)

Important: When you slice an ImageJ image (i.e. a RandomAccessibleInterval) an IntervalView is returned. Depending on what you’re doing this object type could be and you can continue with your workflow. In this case however, we need to wrap/convert the IntervalView returned from the slicing operation as an ImgPlus. Then we can use to view the slice. For more information on RandomAccessibleIntervals check out the ImageJ wiki here.

print(f"java_slice type: {type(java_slice)}")

# wrap as ImgPlus -- you can also wrap the slice as a dataset with
img =
java_slice type: <java class 'net.imglib2.view.IntervalView'>
print(f"python_slice type: {type(python_slice)}")

# xarray images can just be displayed
python_slice type: <class 'xarray.core.dataarray.DataArray'>

6.12 Combine slices

With xarray you can combine slices easily using the .concat() operation. Let’s take two slices from the 4D dataset and create a new 3D xarray.

# get two slices from the test dataset
xarr_slice1 = xarr[12, :, :, 1]
xarr_slice2 = xarr[12, :, :, 2]

# these slices should only be 2D (i.e. x,y)
print(f"s1 shape: {xarr_slice1.shape}")
print(f"s2 shape: {xarr_slice2.shape}")
s1 shape: (250, 250)
s2 shape: (250, 250)

Import xarray and combine the two extracted frames.

import xarray as xr

# note that you must specify which coordnate ('dim') to concatenate on
new_stack = xr.concat([xarr_slice1[:,:], xarr_slice2[:,:]], dim='ch')

Now we can check the dimensions of the new 3D image.

print(f"Number of dims: {len(new_stack.dims)}\ndims: {new_stack.dims}\nshape: {new_stack.shape}")
Number of dims: 3
dims: ('ch', 'row', 'col')
shape: (2, 250, 250) only displays 2D images, not 3D ones like new_stack. But we can view both slices of new_stack like so:

# view the data[0])[1])
_images/a5c29d30ec193e15b4ca1e8282c342ec0176cec4ef9e4f94e0df390c7bfc4f71.png _images/a08a9061b3289cc73d16bc8acce8be0d115e9631da47f2b85bd42148c92128b3.png

Remeber we can send new_stack back to ImageJ/Java with for further analysis

# send new_stack back to ImageJ/Java
img =
print(f"img dims: {img.dims}\nimg shape: {img.shape}")
img dims: ('X', 'Y', 'Channel')
img shape: (250, 250, 2)

You can also combine slices from ImageJ images using ImgLib2’s Views.stack() function. stack accepts two or more n-dimensional RandomAccessibleIntervals of the same size and returns a (n+1)-dimensional RandomAccessibleInterval where the last dimension is the index of the input hyperslices.

First let’s use scyjava to import the net.imglib2.view.Views class. You can learn more about this specific class and other ImgLib2 classes at the ImgLib2 Javadoc.

import scyjava as sj

Views = sj.jimport('net.imglib2.view.Views')

Next let’s slice the original dataset (dimensions: X, Y, Channel, Time) to extract two slices to combine into a new stack.

jslice_1 = dataset[:, :, 1, 12]
jslice_2 = dataset[:, :, 2, 12]
print(f"slice 1 shape: {jslice_1.shape}\nslice 2 shape: {jslice_2.shape}")
slice 1 shape: (250, 250)
slice 2 shape: (250, 250)

Now we can use Views to combine the two slices into a new stack.

jstack = Views.stack(jslice_1, jslice_2)
print(f"stack shape: {jstack.shape}")
stack shape: (250, 250, 2)

Note the new 3rd dimension, the index of the slices (i.e. the channel dimension).

Finally we can view the contents of the new stack:

# view the two slices jstack[:, :, 0]))[:, :, 1]))
_images/a5c29d30ec193e15b4ca1e8282c342ec0176cec4ef9e4f94e0df390c7bfc4f71.png _images/a08a9061b3289cc73d16bc8acce8be0d115e9631da47f2b85bd42148c92128b3.png

6.13 Create numpy images takes a single image argument and returns a NumPy array of zeros in the same shape as the input image. This method is used internally when sending/recieving data with and to initialize a destination NumPy array. An important nuance to know is that RandomAccessibleIntervals passed to initialize_numpy_image() will return a NumPy array with a reversed shape. When passing xarray.DataArray or numpy.ndarray images will return a NumPy array of zeros in the same shape as the input array.

# initialize a new numpy array with numpy array
new_arr1 =

# initialize a new numpy array with xarray
new_arr2 =

# initialize a numpy array with RandomeAccessibleInterval
new_arr3 =

print(f"xarr/numpy shape: {xarr.shape}\ndataset shape: {dataset.shape}")
print(f"new_arr1 shape: {new_arr1.shape}\nnew_arr2 shape: {new_arr2.shape}\nnew_arr3 shape: {new_arr3.shape}")
xarr/numpy shape: (15, 250, 250, 3)
dataset shape: (250, 250, 3, 15)
new_arr1 shape: (15, 250, 250, 3)
new_arr2 shape: (15, 250, 250, 3)
new_arr3 shape: (15, 3, 250, 250)

Note that both new_arr1 and new_arr2 have the same shape as the source xarr. new_arr3 however has reversed dimensions relative to dataset (a RandomAcessibleInterval).

6.14 Working with ops

PyImageJ also grants easy access to imagej-ops. Using to convert Python objects into Java ones for ops allows you to utilize the full power of imagej-ops on Python-based xarray.DataArray and numpy.ndarray data.

import numpy as np

arr1 = np.array([[1, 2], [3, 4]])
arr2 = np.array([[5, 6], [7, 8]])
arr_output =

print(arr_output) # this output will be [[5, 12], [21, 32]]
[[ 5 12]
 [21 32]]

6.15 Window service and manipulating windows

In order to use a graphical user interface, you must also initialize PyImageJ with mode='gui' or mode='interactive'. To work with windows, you can:

  • Use ImageJ2’s WindowService through ij.window().

  • Use the original ImageJ’s WindowManager using the ij.WindowManager property.

Please note that the original ImageJ’s WindowManager was not designed to operate in a headless enviornment, thus some functions my not be supported.

You can get a list of active ImageJ2 windows with the following command:


To close windows use this command: