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

3 Converting to Java: ij.py.to_java()

The function ij.py.to_java() is capable of converting common Python and NumPy data types into their Java/ImageJ equivalent. There is one important nuance; converting a NumPy array to Java creates a Java object that points to the NumPy array. This means that changing the Java object also changes the NumPy array.

3.1 Converting between Java and Python

Converting between Java and Python is done using the ij.py.to_java() and ij.py.from_java() functions. For more information about ij.py.from_java(), checkout the next notebook: 04-Retrieving-Data-from-Java. A table of common data types and their converted types is listed below.

Python object

Java Object

numpy.ndarray

net.imglib2.python.ReferenceGuardingRandomAccessibleInterval

xarray.DataArray

net.imagej.Dataset

str

java.lang.String

int

java.lang.Integer

float

java.lang.Float

list

java.util.ArrayList

dict

java.util.LinkedHashMap

tuple

java.util.ArrayList

NumPy and xarrays are linked to Java equivalents

ij.py.to_java() is capable of converting common Python and numpy data types into their Java/ImageJ/ImageJ2 equivalent. There is one important nuance; converting a numpy.ndarray or xarray.DataArray to Java creates a Java object that points to the numpy array. This means that changing the Java object also changes the numpy array.

3.2 Converting Python objects to Java

We can see how ij.py.to_java() works to convert Python objects to Java. In this section we will convert lists as an example. First we need to initialize ImageJ:

import imagej

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

Now let’s look at how to convert a Python list to Java. Modifying either list demonstrates how lists are not linked, unlike numpy.ndarray/xarray.DataArrays with their respective Java object.

# create lists
python_list = [1, 2, 3, 4]
java_list = ij.py.to_java(python_list)

# modify one list
python_list[0] = 4

# check list contents
print(f"python_list: {python_list}\njava_list: {java_list}")
python_list: [4, 2, 3, 4]
java_list: [1, 2, 3, 4]

A Java list can be accessed the same way as a Python list.

print("List values:")
for i in range(len(python_list)):
    print(f"python: {python_list[i]}, java: {java_list[i]}")
List values:
python: 4, java: 1
python: 2, java: 2
python: 3, java: 3
python: 4, java: 4

Note: ij.py.to_java() is not the only way to create a Java list/arrray. You can specifically create Java arrays by calling JPype’s JArray and JInt:

from jpype import JArray, JInt

java_int_array = JArray(JInt)([1, 2, 3, 4])

print(f"type: {type(java_int_array)}\nvalue: {java_int_array}")
type: <java class 'int[]'>
value: [1, 2, 3, 4]

For more information on creating/working with generic Java objects visit JPype’s documentation here.

3.3 Converting NumPy arrays to Java

NumPy arrays become RandomAccessibleIntervals (wrapped as a net.imagej.DefaultDataset) and can substitute for IterableIntervals.

import numpy as np

# get numpy array and list
test_arr = np.array([[5, 12], [21, 32]])
test_list = [1, 2, 4, 8, 16, 32, 64]

# convert array and list to Java
jarr = ij.py.to_java(test_arr)
jlist = ij.py.to_java(python_list)

print(type(jarr))
print(type(jlist))
<java class 'net.imagej.DefaultDataset'>
<java class 'java.util.ArrayList'>

We can check that jarr is a RandomAccessibleInterval by checking it against the Java class with isinstance. By contrast jlist should evaluate as False as it is not a RandomAcessibleInterval.

import scyjava as sj

# import RandomAccessibleInterval class
RandomAccessibleInterval = sj.jimport('net.imglib2.RandomAccessibleInterval')

print(f"jarr: {isinstance(jarr, RandomAccessibleInterval)}")
print(f"jlist: {isinstance(jlist, RandomAccessibleInterval)}")
jarr: True
jlist: False

ij.py.to_java() also works to convert NumPy arrays into ImageJ types. Let’s grab an image:

# Import an image with scikit-image.
# NB: 4D (X, Y, Channel, Z) confocal image of a HeLa cell in metaphase.
from skimage import io

url = 'https://media.imagej.net/pyimagej/4d/metaphase_hela_cell.tif'
img = io.imread(url)
# get microtubule slice
# NB: The dimension shape of `img` is (pln, row, col, ch) or (Z, Y, X, Channel).
mt = img[0, :, :, 2]

# show image
ij.py.show(mt)
_images/2fd21c2b63cbfb0881962bbf6bb42991b05d4945fc2546cba3fab9b3ec4d81c2.png

Any Op that requires a RandomAccessibleInterval can run on a NumPy array that has been passed to to_java(). Remember that this method creates a view, meaning that the Op is modifying the underlying Python object:

Let’s run a Difference of Gaussians on our numpy image using ImageJ:

result = np.zeros(mt.shape)
# these sigmas will be nice for the larger sections
sigma1 = 2.5
sigma2 = 0.5
# note the use of to_java on img and result to turn the numpy images into RAIs
ij.op().filter().dog(ij.py.to_java(result), ij.py.to_java(mt), sigma1, sigma2)
# purple highlights the edges of the vessels, green highlights the centers
ij.py.show(result, cmap = 'viridis')
_images/b25d0b4b3135c0ce6f49204f011d461ec606ac0f4c84c0cbb0f1ec7c8a35eea8.png