Note that ImageJ also ships a unified Script Interpreter plugin, accessible from Plugins ▶ Scripting ▶ Script Interpreter. But it is currently beta quality, and the Python language does not work properly due to bugs. Once this issue is fixed, the unified Script Interpreter will replace the language-specific interpreters such as the Jython Interpreter.

Within the interpreter, all ImageJ, java.lang.* and TrakEM2 classes are automatically imported. So creating new images and manipulating them is very straighforward.

Language basics

Any text after a # is commented out.

There are no line terminators (such as ';' in other languages), neither curly braces to define code blocks.

Indentation defines code blocks.

Functions are defined with def, and classes with class.

Functions are objects, and thus storable in variables.

Jython (and python in general) accepts a mixture of procedural and object-oriented code.

Jython currently implements the Python language at its 2.5 version. All documentation for python 2.5 applies to Jython bundled with Fiji (with the remarks listed later).

Importing classes

To reference Java classes from Jython you will need to import them.

Unlike ImageJ 1.x, ImageJ2 (and therefore Fiji) does not automatically import any classes. Consequently, scripts written for ImageJ 1.x will not run in ImageJ2 without adding the proper imports.
The rationale is that the auto-import feature is not safe. What if two classes of the same name live in two different packages? Or if a new class is introduced that makes formerly unique names ambiguous? All of a sudden, all of the scripts that reference the original class no longer work. In short: auto-imports are dangerously imprecise.

Workflow for creating Jython scripts

To create a script for the GUI, the recommended setup is the following:

Edit and save a file in your favorite text editor. If you want ImageJ1 to insert it into the Menu structure, the file must be saved somewhere under ImageJ plugins folder, have an underscore on the name, and a .py extension.

Run Plugins ▶ Scripting ▶ Refresh Jython scriptsonly the very first time after newly creating the file under any folder or subfolder of ImageJ's plugins folder. A menu item will appear with its name, from which it can be run.

Keep editing (and saving) the file from your editor. Just select the menu item to execute it over and over. Or use the Plugins ▶ Utilities ▶ Find Commands... window to launch it easily (keybinding 'l').

The next time Fiji is run, it will setup all your scripts in the Plugins menu.

If all you need is a script to run in headless mode, simply do:

fiji --headless filepath.py

Some limitations of jython

What about NumPy and SciPy?

If you like Python, you probably want to use Python modules such as the excellent NumPy and SciPy libraries. Unfortunately, Jython does not support linking to Python modules backed by native code. See this thread on the ImageJ forum for some options and alternatives. This area is somewhere a dedicated programmer could make a huge splash and benefit the entire scientific community.

Though jython tries to be as close as possible as python, there are some differences you may experience during scripting.

Float "special numbers" such as NaN and Inf are not handled.

For instance,

a = float('nan')

will create the correct float number in python, but will throw an exception in jython.

Instead, to create a NaN in jython, use:

>>> a = Double.NaN
>>> print a
NaN

To test if a number is NaN:

>>> if Double.isNaN(a):
print "a is NaN!"
a is NaN!

Some existing python modules can't be imported in jython.

This is for instance the case of the module numpy, which would have been really convenient for analysing data and results.

Your Jython version may be matching a much older Python version than you expect.

The latest Jython stable release (as of May 2015) is 2.7.0. Fiji (as of December 2015) distributes Jython 2.5.3. Any recent Python syntax such as except ExceptionType as e: or with open(filepath, 'r') as f: will fail.

Jython tutorials for ImageJ

Defining variables: obtaining the current image

imp = IJ.getImage()

Which is the same as:

imp = WindowManager.getCurrentImage()

Since calling the above is long and tedious, one can declare a variable that points to the above static methods:

c = WindowManager.getCurrentImage

Above note the lack of parentheses.

To execute the function, just use parentheses on it:

imp = c()

The above gets the value of c, which is the method named getCurrentImage in class WindowManager, and executes it, storing its returned object in imp.

Now, we want to measure the intensity of each particle. To do so, we'll retrieve the ROI from the ROIManager, set them one at a time on the original (non-watershed, non-thresholded) image stored in the variable blobs, and measure:

# Create a new list to store the mean intensity values of each blob:
means = []
for roi in RoiManager.getInstance().getRoisAsArray():
blobs.setRoi(roi)
stats = blobs.getStatistics(Measurements.MEAN)
means.append(stats.mean)

Finally read out the measured mean intensity value of each blob, along with its area:

Creating an image from a text file

A data file containing rows with 4 columns:

...
399 23 30 10.12
400 23 30 12.34
...

... where the columns are X, Y, Z and value, for every pixel in the image.
We assume we know the width and height of the image.
From this sort of data, we create an image, read out all lines and parse the numbers:

The reduce function to obtain a single value from a list of values (the pixel array) by applying a function to every pair of consecutive values (in this case, the Math.min).

lambda, which is used to declare an anonymous function that takes one argument.

The map function, which runs a function given as argument to every element of a list (here, every pixel) and returns a new list with all the results.

Extract a specific color channel for a given time frame of a composite image

Suppose you have a 4D multicolor image, and want to obtain a stack of slices corresponding to a specific color channel and time frame.

The CompositeImage is a stack whose slices are interpreted as belonging to specific color channels, Z slices and time frames. To find out which slice corresponds to what, use the getStackIndex method of the ImagePlus, which translates between color channels, z slices and time frames to the slice index in the underlying ImageStack.

Notice that color channels, stack slices and time frames are all 1-based. For example, if you have 3 color channels, then these have indices 1, 2, and 3 (not 0, 1 and 2).

Visualize any number of TIFF stacks in a single composite multi-color image stack

Suppose you have 1000 stacks of Drosophila fly brains, each with different neurons labeled in a single color channel. Suppose that you have registered all these confocal stacks. Were you to overlay them, you would see whether the labeled neurons overlap in 3D space or not.

Here is a script to do that. First, it asks for a directory containing any number of TIF image stacks. It assumes all stacks have the same dimensions, and that they are all single channel (i.e. just red, or just green, etc.). Then, it displays a small window with a listing of many colors: red, green, blue, orange, gray, etc. Any of the hundreds of stacks in the directory can be assigned to each color channel.

The stacks are accessed in a virtual way, so even 1000 (one thousand) stacks will be managed just fine in small laptop.

Create a virtual stack from the TIF files present in a folder and its subfolders, recursively

# Walk recursively through an user-selected directory
# and add all found filenames that end with ".tif"
# to a VirtualStack, which is then shown.
#
# It is assumed that all images are of the same type
# and have the same dimensions.
import os
from ij.io import DirectoryChooser
from ij import IJ, ImagePlus, VirtualStack
def run():
srcDir = DirectoryChooser("Choose!").getDirectory()
if not srcDir:
# user canceled dialog
return
# Assumes all files have the same size
vs = None
for root, directories, filenames in os.walk(srcDir):
for filename in filenames:
# Skip non-TIFF files
if not filename.endswith(".tif"):
continue
path = os.path.join(root, filename)
# Upon finding the first image, initialize the VirtualStack
if vs is None:
imp = IJ.openImage(path)
vs = VirtualStack(imp.width, imp.height, None, srcDir)
# Add a slice, relative to the srcDir
vs.addSlice(path[len(srcDir):])
#
ImagePlus("Stack from subdirectories", vs).show()
run()

Open the slices of a very large multi-image stack file one by one, and save each as a new image file

Apply a binary mask to every slice in an image stack

Will work with regular stacks and with any kind of complex stack like a composite image or a 4d volume. Keep in mind that all stack types in ImageJ consists of a sequence of 2d images, each editable with an ImageProcessor obtained from the ImageStack that one can get from the ImagePlus. (The ImagePlus being what the opener or the WindowManager provides.)

# Albert Cardona 2012-10-05 for Sara Abrahamsson
#
# Take a stack of images and a mask,
# and clear the area outside the mask for every image.
#
# ASSUMES that the mask:
# 1. Is 8-bit;
# 2. has the area to keep as 255;
# 3. has the area to clear as zeros.
from ij import IJ
from ij import WindowManager as WM
# If the images are open:
volume = WM.getImage("stack.tif")
mask = WM.getImage("mask.tif")
# Or if the images have to be loaded from files:
# volume = IJ.openImage("/Users/sara/images/stack.tif")
# mask = IJ.open("/Users/sara/images/mask.tif")
# Obtain the underlying stack of 2d images
stack = volume.getStack()
# Fill every stack slice with zeros for the area outside the mask
for i in xrange(1, stack.getSize() + 1):
# ip is the ImageProcessor for one stack slice
ip = stack.getProcessor(i)
ip.setValue(0)
ip.fill(mask)
volume.updateAndDraw()
volume.show()

Note that it is counterintuitive that the area outside the mask gets filled with zeros. If you want the area inside the mask to get filled with zeros, then add this step before the loop:

Tips and Tricks

Getting a list of all members in one package

You can use the Python function dir(<package>) to see the contents of a package:

import ij
print dir(ij)

Note: As of April 26nd, 2010, you need to start Fiji with

fiji -Dpython.cachedir.skip=false --

for dir(<package>) to work.

Specifying the encoding of the source

When your source code contains non-ASCII characters (such as umlauts), Jython will complain with a SyntaxError: Non-ASCII character in file '<iostream>', but no encoding declared.

You can fix this issue by putting the line

# -*- coding: iso-8859-15 -*-

as first line into your source code (or if it starts with #!/usr/bin/python, as second line), as suggested here. You might need to replace the string iso-8859-15 by something like utf-8 if your source code is encoded in UTF-8.

Changing the default encoding

By default, Jython encodes the standard output (and other streams) with the ASCII encoding. Often, this is not what you want. You can change the default encoding like this:

To ensure that you see the stack trace, print it to the ImageJ log window instead of stdout (whathever the latter may be):

IJ.log(str(sys.exc_info()))

Importing other .py scripts (modules)

If you want to import other python files, you need to import them. This requires that the files are found in the so-called search path, a list of directories in which Jython looks for the modules (.py files) to import. You can easily extend the search path:

Of course arrays can also be created empty. For numbers, all values will be zero. For an arbitrary class such as String, all values will be null (or None, in python parlance).

In the example below, we create an empty two-dimensional array of double[N][] type, where the smaller, inner arrays are null (just like in java a new double[5][] would have the second-order also all null):

Inline java code inside jython: the Weaver

Jython is great at doing high-level operations on images. But sometimes one wants to edit pixels specifically. Such low-level loops in jython are far from the performance offered by java. But writing a special-purpose java class for a minor piece of code is painful and requires specific java skills regarding code compilation and classpath management.

The weaver removes all the pain. Here is an example, where the float[] pixels array of the current image is iterated to compute the mean intensity:

The above is trivial and it is meant only as an example (there are better ways to get the mean value, such as via imp.getStatistics(). Notice that the Weaver.inline function takes three arguments: the java code to inline, the map of bindings, and the return type. In the example, we pass only the float[] pixels array, and define Double as the return type. The return type is optional.

Internally, bindings are represented as fields in a java class, set as either primitives (like double, int ...) or the least general public class or superclass of the object to bind.

There is a fourth optional argument for inline (boolean) to show the generated java code in a tab of the Script Editor.

A better example that exploits the capabilities of the Weaver is the following: compile the function once, and then call it over and over with different parameters. The bindings cannot be changed, but if they are arrays or collections, one can change the elements of these collections. For example, to obtain a new ImageStack that is the result of applying XOR to each consecutive pair of slices (which will give you the boundaries of objects):

Reading command line arguments given to a script

The Fiji launcher can execute scripts. When running scripts from the command line with the launcher, it is convenient to read out the arguments given to the script. For example, suppose you create a script to open an image file and do some processing with it, and you want to read the name of the file to open from the command line argument. Here is how:

IMPORTANT: notice that, when executing scripts from the command line, there is no auto-importing of common imports. So above we must declare "from ij import IJ" to import the namespace IJ with all the static utility functions such as openImage.

Catching errors from a running macro

ImageJ exits with zero even when it fails (see bug report). A possible fix is to convert the macro into a plugin but a quicker fix, is to wrap the macro call into a script. For this purpose, it is enough to check the returned string of runMacroCode, which will return the string [aborted] in case of failure:

The JysonCodec.class is the toplevel class, so to import this library include the following line in your jython script:

import com.xhaus.jyson.JysonCodec as jyson

Jython for plugins

Using a jython script as a plugin

The simplest way is to place the jython script file into fiji/plugins/ folder or a subfolder, and it will appear in the menus after running "' Plugins ▶ Scripting ▶ Refresh Jython Scripts'" or "' Help ▶ Refresh Menus'", or on restarting Fiji.

If you want to have the Jython script show up in a place outside the Plugins menu, just put the file into an appropriate subdirectory of fiji/plugins/Scripts/; for example, if you put a Jython script called Animation_.py into fiji/plugins/Scripts/File/New/, it will be available as File ▶ New ▶ Animation.

Distributing jython scripts in a .jar file

PLEASE NOTE: there is no need to do the following -- unless you want to bundle a couple of scripts in one package. See entry above.

The easiest way to distribute a (single) Jython script is to start the Script Editor, open the Jython script and make the bundle with File ▶ Export as .jar.

Alternatively -- or if you want to bundle multiple scripts -- you can do it the manual way:

The whole idea is to be able to distribute an entire collection of scripts in a single .jar file, for best convenience.

In this example, we create two jython scripts that we want to distribute in a .jar file as plugins: