This is an archive of the old MediaWiki-based ImageJ wiki. The current website can be found at imagej.net.

Jython Scripting

Learn
Topics
Introduction
Getting Started
User Guides
Tutorials
Tips and Tricks
Presentations
Plugins
Techniques
All Techniques
Colocalization
Deconvolution
Registration
Segmentation
Stitching
Tracking
Visualization
Scripting
Overview
User input
Basics of script writing
How to use the Javadoc
Batch processing
Script Editor
Auto Imports
Templates
Running headlessly
Comparisons
Toolbox
Multithreading in Clojure
Multithreading in JavaScript
Chess in Jython
Languages
BeanShell
Groovy
ImageJ Macro
JavaScript
Lisp (Clojure)
MATLAB
Python (Jython)
R (Renjin)
Ruby (JRuby)
Scala


Introduction

Jython is an implementation of the Python programming language designed to run on the Java platform. [1] In ImageJ Jython is one of several supported languages.

When to use Jython

All scripting language supported by ImageJ can be used to access the ImageJ API. There are only differences in how the imports are handled and in the syntax of the selected language. Jython has a syntax that differs from most other language as indentations instead of brackets are used to group code blocks.

The following list will help you to decide if Jython is the right choice to create scripts for ImageJ:

  • If you have experience with Python you can easily use Jython for ImageJ scripting. But you have to keep in mind that tools commonly used in many Python projects (e.g. Numpy) are not available in Jython. By building your own modules you can create complex scripts that otherwise are only possible by writing ImageJ plugins in Java.
  • If don't have any experience in programming, the Python language is a good choice to start with. If your only aim is to write scripts for ImageJ, there are other languages you should try first (e.g. Groovy).
  • In Python many problems can be solved with less code than in other languages. Still the code is easy to read. Have a look at the examples on this page and decide if you want to start using Python for ImageJ scripting.

Explanation

The Java implementation of Python is limited to the standard library of Python 2.
It is not possible to use external python modules (like Numpy...) however, any Java class residing in the Fiji installation can be used.

Even with the given limitations, Jython is a powerful language for ImageJ scripting. Hopefully the examples on this page can convince you of that.

Jython basics for ImageJ



Introduction

The aim of this page is not to teach how to program in Python. This purpose is much better fulfilled by the documentation of Python2. The focus of this page is to show how features of the Python language can be useful for ImageJ scripting.

That is why more complex examples are used that are fully functional. Just copy the code to the Script Editor and try them by yourself. Extensive in-line documentation is used to explain the implementation.

Hello World

- With print

There are 2 ways to print some information back to the user.
The first one is a classical python print statement, that will print some information to the console.
print "Hello world"
You can print any kind of variable and objects.
print "This is a string followed by an int", 10

NB1 : If used in a plugin, and no console window is open then the printed information will not be visible to the user (contrary to the log function below)

NB2 : Using numerous print statements might slow down the execution time when used in a plugin (not observed when executing from the script interpreter).


- With IJ.log()

1
2
3
from ij import IJ
IJ.log("Hello world")
IJ.log("This is a string followed by an int " + str(10))

Contrary to the print statement the log function display some output into a log window (newly open if not already open), and accept only a string as argument.

Image selection using the GenericDialog class

This example script will create up to 10 new images and create a GenericDialog to select 3 of them. Finally the names of the selected images are printed to the Log window. It is recommended to copy the code to the Script Editor and run it by yourself.

The following list links to documentation of the used Python features:


src/main/resources/script_templates/Tutorials/Wiki_Jython_Tutorial_1.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
'''Image selection using the GenericDialog class
 
This code is part of the Jython tutorial at the ImageJ wiki.
http://imagej.net/Jython_Scripting#Image_selection_using_the_GenericDialog_class
'''
 
# The module __future__ contains some useful functions:
from __future__ import with_statement, division
# This imports the function random from the module random.
from random import random
# Next we import Java Classes into Jython.
# This is how we can acces the ImageJ API:
from ij import IJ, WindowManager
from ij.gui import GenericDialog
 
# A function is created with the def keyword.
# This function does not need any parameters.
def create_test_image():
    # Python uses indentation to create code blocks
 
    # Local variables are assigned.
    # We can assign the same value to more than one variable.
    image_width = image_height = 512
    box_width = box_height = 128
    offset_x = offset_y = 192
    counts = 64
    stdv = 16
    # The build in function int() is used to convert float to int.
    # The variable random contains a function that is called by adding parentheses.
    offset_x = int(2 * random() * offset_x)
    offset_y = int(2 * random() * offset_y)
    # We can define a function inside a function.
    # Outside of create_test_image() this function is not available.
    # By adding an asterisk to a parameter, all given parameters are combined to a tuple.
    def make_title(*to_concat):
        prefix = 'TestImage'
        # To create a tuple with a single entry the comma is necessary.
        # The 2 tuples are concatenated by using the + operator.
        to_join = (prefix,) + to_concat
        # We create a generator that converts every singe entry of the tuple to a string.
        strings_to_join = (str(arg) for arg in to_join)
        # The string ',' has a join method to concatenate values of a tuple with the string as seperator.
        # The result is a string.
        return ','.join(strings_to_join)
    def check_existence(title):
        if WindowManager.getIDList() is None:
            return False
        image_titles = (WindowManager.getImage(id).getTitle() for id in WindowManager.getIDList())
        return title in image_titles
    # To negate an expression put not in front of it.
    if not check_existence(make_title(offset_x, offset_y)):
        # The following code has been created by using the Recorder of ImageJ, set to output Java code.
        # By removing the semicolons, the code can be used in Jython.
        # The parameters can be modified by using variables and string concatenation.
        imp = IJ.createImage(make_title(offset_x, offset_y), "8-bit black", image_width, image_height, 1)
        # The build in function str() is used to convert int to string.
        IJ.run(imp, "Add...", "value=" + str(counts))
        imp.setRoi(offset_x , offset_y, box_width, box_height)
        IJ.run(imp, "Add...", "value=" + str(counts))
        IJ.run(imp, "Select None", "")
        IJ.run(imp, "Add Specified Noise...", "standard=" + str(stdv));
        # We don't want to confirm when closing one of the newly created images.
        imp.changes = False
        imp.show()
 
# This function uses parameters.
# A default value is given to the third parameter.
def create_selection_dialog(image_titles, defaults, title='Select images for processing'):
    gd = GenericDialog(title)
    # The build in function enumerate() returns two values:
    # The index and the value stored in the tuple/list.
    for index, default in enumerate(defaults):
        # for each loop we add a new choice element to the dialog.
        gd.addChoice('Image_'+ str(index + 1), image_titles, image_titles[default])
    gd.showDialog()
    if gd.wasCanceled():
        return None
    # This function returns a list.
    # _ is used as a placeholder for values we don't use.
    # The for loop is used to call gd.getNextChoiceIndex() len(defaults) times.
    return [gd.getNextChoiceIndex() for _ in defaults]
 
# It's best practice to create a function that contains the code that is executed when running the script.
# This enables us to stop the script by just calling return.
def run_script():
    while WindowManager.getImageCount() < 10:
        create_test_image()
 
    image_titles = [WindowManager.getImage(id).getTitle() for id in WindowManager.getIDList()]
    # range(3) will create the list [0, 1, 2].
    selected_indices = create_selection_dialog(image_titles, range(3))
    # The script stops if the dialog has ben canceld (None was returned from create_selection_dialog).
    if selected_indices is None:
        print('Script was canceld.')
        return
    # We have to get the corresponding IMagePlus objects.
    selected_imps = [WindowManager.getImage(id) for id in [WindowManager.getIDList()[index] for index in selected_indices]]
    # The previous line can be split into 2 lines:
    # selected_ids = [WindowManager.getIDList()[index] for index in selected_indices]
    # selected_imps = [WindowManager.getImage(id) for id in selected_ids]
 
    for imp in selected_imps:
        # Strings can be formated using the % operator:
        IJ.log('The image \'%s\' has been selected.' % imp.getTitle())
 
# If a Jython script is run, the variable __name__ contains the string '__main__'.
# If a script is loaded as module, __name__ has a different value.
if __name__ in ['__builtin__','__main__']:
    run_script()

Using Scripting Parameters

The second example is inspired by atomic resolution images recorded with an Transmission Electron Microscope (TEM). Such images show a regular structure (a crystal), but the images are noisy because of the low signal. By using a Fourier filter the contrast can be enhanced.

The script will create a periodic structure and add some random noise. The user can control the parameters of the created image. This is realized using Script parameters. The Fourier filtering has been created by using the Recorder. Finally a simple image calculator is used to show that functions can be passed as parameters.

This list links to the documentation of Python features that are introduced with this example:


src/main/resources/script_templates/Tutorials/Wiki_Jython_Tutorial_2.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#@ String(value='Please set some parameters.', visibility='MESSAGE') message
#@ Short(label='Image size', value=512, min=128, max=2048, stepSize=128, style="slider") img_size
#@ Double(label='Image amplitude', value=100) amplitude
#@ Short(label='Spacing', value=16, min=8) spacing
 
'''Using Scripting Parameters
 
This code is part of the Jython tutorial at the ImageJ wiki.
http://imagej.net/Jython_Scripting#Using_Scripting_Parameters
'''
 
# The parameters in front of this comment are populated before the script runs.
# Details on Script parameters can be found at
 
# The module __future__ contains some useful functions:
from __future__ import with_statement, division
 
# It's best practice to create a function that contains the code that is executed when running the script.
# This enables us to stop the script by just calling return.
def run_script():
    # We can use import inside of code blocks to limit the scope.
    import math
    from ij import IJ, ImagePlus
    from ij.process import FloatProcessor
    blank = IJ.createImage("Blank", "32-bit black", img_size, img_size, 1)
    # This create a list of lists. Each inner list represents a line.
    # pixel_matrix[0] is the first line where y=0.
    pixel_matrix = split_list(blank.getProcessor().getPixels(), wanted_parts=img_size)
    # This swaps x and y coordinates.
    # As zip() creates tuples, we have to convert each one by using list().
    pixel_matrix = [list(x) for x in zip(*pixel_matrix)]
    for y in range(img_size):
        for x in range(img_size):
            # This function oszillates between 0 and 1.
            # The distance of 2 maxima in a row/column is given by spacing.
            val = (0.5 * (math.cos(2*math.pi/spacing*x) + math.sin(2*math.pi/spacing*y)))**2
            # When assigning, we multiply the value by the amplitude.
            pixel_matrix[x][y] = amplitude * val
    # The constructor of FloatProcessor works fine with a 2D Python list.
    crystal = ImagePlus("Crystal", FloatProcessor(pixel_matrix))
    # Crop without selection is used to duplicate an image.
    crystal_with_noise = crystal.crop()
    crystal_with_noise.setTitle("Crystal with noise")
    IJ.run(crystal_with_noise, "Add Specified Noise...", "standard=%d" % int(amplitude/math.sqrt(2)))
    # As this is a demo, we don't want to be ask to save an image on closing it.
    # In Python True and False start with capital letters.
    crystal_with_noise.changes = False
    crystal.show()
    crystal_with_noise.show()
    filtered = fft_filter(crystal_with_noise)
    # We create a lambda function to be used as a parameter of img_calc().
    subtract = lambda values: values[0] - values[1]
    # This is a short form for:
    # def subtract(values):
    #   return values[0] - values[1]
 
    # The first time we call img_calc with 2 images.
    difference = img_calc(subtract, crystal, filtered, title="Difference of 2")
    difference.show()
    # The first time we call img_calc with 3 images.
    minimum = img_calc(min, crystal, filtered, crystal_with_noise, title="Minimum of 3")
    minimum.show()
    for imp in (crystal, crystal_with_noise, filtered, difference, minimum):
        IJ.run(imp, "Measure", "")
 
# Functions can be defined after they are used.
# This is only possible if the main code is encapsulated into a function.
# The main function has to be called at the end of the script.
 
def img_calc(func, *imps, **kwargs):
    """Runs the given function on each pixel of the given list of images.
    An additional parameter, the title of the result, is passed as keyword parameter.
    We assume that each image has the same size. This is not checked by this function.
    """
    # If the keyword parameter is not passed, it is set to a default value.
    if not kwargs['title']:
        kwargs['title'] = "Result"
    # This is a 2D list: list[number of images][pixels per image] .
    pixels = [imp.getProcessor().getPixels() for imp in imps]
    # The function is called pixel by pixel.
    # zip(*pixels) rotates the 2D list: list[pixels per image][mumber of images].
    result = [func(vals) for vals in zip(*pixels)]
    # result is a 1D list and can be used to create an ImagePlus object.
    from ij import ImagePlus
    from ij.process import FloatProcessor
    return ImagePlus(kwargs['title'], FloatProcessor(img_size, img_size, result))
 
def split_list(alist, wanted_parts=1):
    """Split a list to the given number of parts."""
    length = len(alist)
    # alist[a:b:step] is used to get only a subsection of the list 'alist'.
    # alist[a:b] is the same as [a:b:1].
    # '//' is an integer division.
    # Without 'from __future__ import division' '/' would be an integer division.
    return [ alist[i*length // wanted_parts: (i+1)*length // wanted_parts]
             for i in range(wanted_parts) ]
 
def fft_filter(imp):
    """ Removing noise from an image by using a FFT filter
    This are operations copied from the ImageJ macro recorder.
    Jython does not complain when you forget to remove the semicolons.
    """
    from ij import IJ
    IJ.run(imp, "FFT", "");
    # No ImagePlus is returned by the FFT function of ImageJ.
    # We need to use the WindowManager to select the newly created image.
    from ij import WindowManager as wm
    fft = wm.getImage("FFT of " + imp.getTitle())
    IJ.run(fft, "Find Maxima...", "noise=64 output=[Point Selection] exclude");
    # Enlarging the point selectins from Find Maxima.
    IJ.run(fft, "Enlarge...", "enlarge=2");
    # Inverting the selection.
    IJ.run(fft, "Make Inverse", "");
    IJ.run(fft, "Macro...", "code=v=0");
    IJ.run(fft, "Inverse FFT", "");
    fft.changes = False
    fft.close()
    imp_filtered = wm.getImage("Inverse FFT of " + imp.getTitle())
    imp_filtered.setTitle("Filtered " + imp.getTitle())
    imp_filtered.changes = False
    return imp_filtered
 
 
# If a Jython script is run, the variable __name__ contains the string '__main__'.
# If a script is loaded as module, __name__ has a different value.
if __name__ in ['__builtin__','__main__']:
    run_script()

A batch opener using os.walk()

We have yet introduced some powerful functions build into Python. Another one is walk() from the os module. It can be used to go through a directory structure and process the contained files. In this example walk() is used to batch open images with ImageJ's function openImage().

To read more about the used features, the following list provides links to additional information:


src/main/resources/script_templates/Tutorials/Wiki_Jython_Tutorial_3.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#@ File(label='Choose a directory', style='directory') import_dir
#@ String(label='File types', value='tif;png') file_types
#@ String(label='Filter', value='') filters
#@ Boolean(label='Recursive search', value=True) do_recursive
 
'''A batch opener using os.walk()
 
This code is part of the Jython tutorial at the ImageJ wiki.
http://imagej.net/Jython_Scripting#A_batch_opener_using_os.walk.28.29
'''
 
# We do only include the module os,
# as we can use os.path.walk()
# to access functions of the submodule.
import os
from java.io import File
 
from ij import IJ
 
def batch_open_images(path, file_type=None, name_filter=None, recursive=False):
    '''Open all files in the given folder.
    :param path: The path from were to open the images. String and java.io.File are allowed.
    :param file_type: Only accept files with the given extension (default: None).
    :param name_filter: Only accept files that contain the given string (default: None).
    :param recursive: Process directories recursively (default: False).
    '''
    # Converting a File object to a string.
    if isinstance(path, File):
        path = path.getAbsolutePath()
 
    def check_type(string):
        '''This function is used to check the file type.
        It is possible to use a single string or a list/tuple of strings as filter.
        This function can access the variables of the surrounding function.
        :param string: The filename to perform the check on.
        '''
        if file_type:
            # The first branch is used if file_type is a list or a tuple.
            if isinstance(file_type, (list, tuple)):
                for file_type_ in file_type:
                    if string.endswith(file_type_):
                        # Exit the function with True.
                        return True
                    else:
                        # Next iteration of the for loop.
                        continue
            # The second branch is used if file_type is a string.
            elif isinstance(file_type, string):
                if string.endswith(file_type):
                    return True
                else:
                    return False
            return False
        # Accept all files if file_type is None.
        else:
            return True
 
    def check_filter(string):
        '''This function is used to check for a given filter.
        It is possible to use a single string or a list/tuple of strings as filter.
        This function can access the variables of the surrounding function.
        :param string: The filename to perform the filtering on.
        '''
        if name_filter:
            # The first branch is used if name_filter is a list or a tuple.
            if isinstance(name_filter, (list, tuple)):
                for name_filter_ in name_filter:
                    if name_filter_ in string:
                        # Exit the function with True.
                        return True
                    else:
                        # Next iteration of the for loop.
                        continue
            # The second branch is used if name_filter is a string.
            elif isinstance(name_filter, string):
                if name_filter in string:
                    return True
                else:
                    return False
            return False
        else:
        # Accept all files if name_filter is None.
            return True
 
    # We collect all files to open in a list.
    path_to_images = []
    # Replacing some abbreviations (e.g. $HOME on Linux).
    path = os.path.expanduser(path)
    path = os.path.expandvars(path)
    # If we don't want a recursive search, we can use os.listdir().
    if not recursive:
        for file_name in os.listdir(path):
            full_path = os.path.join(path, file_name)
            if os.path.isfile(full_path):
                if check_type(file_name):
                    if check_filter(file_name):
                        path_to_images.append(full_path)
    # For a recursive search os.walk() is used.
    else:
        # os.walk() is iterable.
        # Each iteration of the for loop processes a different directory.
        # the first return value represents the current directory.
        # The second return value is a list of included directories.
        # The third return value is a list of included files.
        for directory, dir_names, file_names in os.walk(path):
            # We are only interested in files.
            for file_name in file_names:
                # The list contains only the file names.
                # The full path needs to be reconstructed.
                full_path = os.path.join(directory, file_name)
                # Both checks are performed to filter the files.
                if check_type(file_name):
                    if check_filter(file_name):
                        # Add the file to the list of images to open.
                        path_to_images.append(full_path)
    # Create the list that will be returned by this function.
    images = []
    for img_path in path_to_images:
        # IJ.openImage() returns an ImagePlus object or None.
        imp = IJ.openImage(img_path)
        # An object equals True and None equals False.
        if imp:
            images.append(imp)
    return images
 
def split_string(input_string):
    '''Split a string to a list and strip it
    :param input_string: A string that contains semicolons as separators.
    '''
    string_splitted = input_string.split(';')
    # Remove whitespace at the beginning and end of each string
    strings_striped = [string.strip() for string in string_splitted]
    return strings_striped
 
if __name__ in ['__builtin__','__main__']:
    # Run the batch_open_images() function using the Scripting Parameters.
    images = batch_open_images(import_dir,
                               split_string(file_types),
                               split_string(filters),
                               do_recursive
                              )
    for image in images:
        # Call the toString() method of each ImagePlus object.
        print(image)

Importing Java module and classes

Another great feature of Jython is the possibility to use functions from Java jar package that resides in the jar folder of imageJ.

ImageJ and Fiji API

The following API documentation lists all available modules and functions :

Those package are built-in with Fiji, but any package that resides in the jars folder can be imported provided you know the path to the class.

For example, one of the main built-in ImageJ packages is called ij, and often Jython scripts will write something like this at the top:

1
2
from ij import IJ
# do stuff below....

Doing this allows you to access the IJ class which resides in the ij package. You can find a description of the ij package here. What can we do with the IJ class? Clicking on the IJ link brings you to the class documentation page for IJ. This class contains "static utility methods" which means you can call them with without instantiating (calling the constructor) the IJ class. We will cover constructors later. Looking through the documentation for IJ, lets focus on the method createImage (docs here). This method can be called just like you would call a method on a python class. The documentation shows you need to provide the following parameters (types in parenthesis):

  1. title (string)
  2. width (int)
  3. height (int)
  4. depth (int)
  5. bitdepth (int)

and it returns an ImagePlus object. ImagePlus objects are very important in ImageJ, and you will the documentation for them here. Below is an example of how to import and use the static methods on the IJ class to create an image.

1
2
3
4
5
from ij import IJ # read this as: "from the ij package import the IJ class"
test_img = IJ.createImage("Test image", 512, 512, 1, 8)
# now check the type of test_img
print(type(test_img))
# <type 'ij.ImagePlus'>

This code shows that we have successfully created an ImagePlus object. Looking at the documentation for the ImagePlus class, let's use a few of the methods to make sure the image was created correctly.

1
2
3
4
5
6
7
8
9
10
from ij import IJ
test_img = IJ.createImage("Test image", 512, 512, 1, 8)
# check the type:
print(type(test_img))
# <type 'ij.ImagePlus'>
title = test_img.getTitle()
width = test_img.width
height = test_img.height
print("{} is {} wide and {} tall.".format(title, width, height))
test_img.show()

We accessed the title using the getTitle() method, which takes no arguments and returns the image name. We accessed the image width and height by accessing test_img's fields. These are not methods, but contain information about the class. We could have also used the getWidth() and getHeight() methods as well. We then called the show() method on our test image and a (very boring) 512X512 8 bit image should have popped up.


Here is another example where we use the ImageJ package and the RoiManager class. According to the javadoc, the RoiManager class resides in ij.plugin.frame. Therefore the code will look like :

1
2
3
from ij.plugin.frame import RoiManager
RM = RoiManager()        # we create an instance of the RoiManager class
rm = RM.getRoiManager()  # "activate" the RoiManager otherwise it can behave strangely


Using openCV in Jython

It is even possible to use most of opencv functionalities within Jython/Fiji. There are several options (see the wiki page about opencv), yet the most straight forward is probably IJ-OpenCV which is available via the update sites. It will automatically download the necessary packages and dependencies in your Fiji installation.

A manual installation is also possible by putting the jar packages in the jar folder of imageJ. They are avalaible on the IJopenCV github, which even provides a maven option.

Matrices

The first thing to know about OpenCV is that most functions work with an OpenCV matrix object. Fortunately, the IJ-OpenCV project provides some converters :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#@ ImagePlus ImP
from ijopencv.ij      import ImagePlusMatConverter
from ijopencv.opencv  import MatImagePlusConverter
from ij               import ImagePlus
 
# Convert ImagePlus (actually the contained ImageProcessor) to Matrix object
imp2mat = ImagePlusMatConverter()
ImMat = imp2mat.toMat(imp.getProcessor())
print ImMat
 
# Convert Matrix object to ImageProcessor
mat2ip = MatImagePlusConverter()
NewIP  = mat2ip.toImageProcessor(ImMat)
NewImp = ImagePlus("Matrix converted back to ImagePlus", NewIP)
print NewImP

Such kind of converter is also available for PointRoi to opencv keypoints...

Now to use opencv function, we use the JavaCPP API that contains almost all functions of opencv.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from org.bytedeco.javacpp.opencv_core   import Mat, CvMat, vconcat
 
## Typical matrices ##
 
# Identity Matrix of size (3x3) and type 8-bit
Id = Mat().eye(3,3,0).asMat()
print Id
print CvMat(Id) # handy to visualise the matrix
 
# Matrix of ones (3x3)
One = Mat().ones(3,3,0).asMat()
 
# Matrix of zeros (3x3)
Zero = Mat().zeros(3,3,0).asMat()
 
# Custom Matrices
# 1D-Matrix can be initialize from a list
# For 2D (or more) we have to concatenate 1D-Matrices
 
Row1 = Mat([1,2,3,4,5]) # 1D matrix
Row2 = Mat([6,7,8,9,10])
 
TwoColumn = Mat()              # initialise output
vconcat(Col1, Col2, TwoColumn) # output stored in TwoColumn
print CvMat(TwoColumn)



Similarly there is some apparent redudancy for the function in the javacpp API.

ex : Transform exists in 3 different places :

  • org.opencv.core.Core.transform

This one takes org.opencv.core.Mat as input. It is currently challenging to have such object in Fiji.

  • org.bytedeco.javacpp.opencv_core.cvTransform

using CvArr as input, but even if you manage to convert your input as a CvArr it crashes Fiji. Apparently it is a deprecated version.

  • org.bytedeco.javacpp.opencv_core.transform

That's the one to use ! It takes only org.bytedeco.javacpp.opencv_core.Mat as input, which is the most approriate in Fiji/Jython


Scalar

In addition to Matrices, opencv allows to use Scalar objects A scalar is a 4 item element (v0, v1, v2, v3). If v1=v2=v3=0 then the Scalar is real.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from org.bytedeco.javacpp.opencv_core   import Scalar
 
# Real scalar can be initiated with a float parameters
Number = Scalar(5.0)
Number = Scalar(float(5))
print Number
 
# Using an integer as parameter has a different meaning
Empty = Scalar(5) # This initiate an empty Scalar object of size 5
print Empty
 
# Alternatively one can set the other values of the Scalar
Complex = Scalar(1,2,3,4)
print Complex


Operations

It is possible to perform some operations between matrices, or between Scalar and matrices.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from org.bytedeco.javacpp.opencv_core   import Scalar, Mat, subtract
 
A = Mat([1,2,3,4,5])
B = Mat([1,2,-3,-4,0])
 
Number = Scalar(10.0)
 
## Number - B ( B-Number is also possible)
Expr = subtract(Number,B)
print CvMat(Expr.asMat())
 
## A - B
Out = Mat()
subtract(A,B,Out)
print CvMat(Out)

Self written Jython modules for ImageJ

In Jython you can write all commands line by line in a single file and execute it. To create a neat program, functions and classes can be used to structure code. To prevent using copy&past for regularly used functions and classes, modules are the way to choose. Modules are files that contain functions and classes to import into other files.

To load modules, one has to save them to a directory where Jython will find them. Two lines of code will reveal these directories to you:

1
2
from sys import path
print(path)

When running this code the result is an output like

1
['/home/michael/Software/ImageJ.app/jars/Lib', '/home/michael/Software/ImageJ.app/jars/jython-shaded-2.7.0.jar/Lib', '__classpath__', '__pyclasspath__/']

This tells us that the folder jars/Lib/ inside our ImageJ/Fiji directory is the right place to save modules. As Lib/ does not exist by default, we have to create it.

When a module is imported for the first time, Jython will compile it to Java code. If there is a module named myModule.py, Jython will create a file called myModule$py.class. The next time the module is imported, the jython interpreter uses the .class file instead of the .py file, even if this .py file was modified.

To force the interpreter to use the last version of the py script there are 2 possibilities :

  • Close Fiji, delete the myModule$py.class file and restart Fiji
  • Use the following lines of code (found at stackoverflow) that will force Jython to recompile all modules
1
2
3
4
5
# Use this to recompile Jython modules to class files.
from sys import modules
modules.clear()
# Imports of Jython modules are placed below:
import myModule

Adding a custom directory

If you don't want to use jars/Lib/ to save your modules, you have to extend the array sys.path:

1
2
3
4
5
6
7
8
9
10
11
from sys import path
from java.lang.System import getProperty
  
# extend the search path by $FIJI_ROOT/bin/
# 'fiji.dir' works for plain ImageJ, too.
path.append(getProperty('fiji.dir') + '/bin')
# an alternative can be the users home directory
# path.append(getProperty('user.home') + '/JythonModules')
  
# Now you can import $FIJI_ROOT/bin/myModule.py
import myModule

The function getProperty() accepts many more strings. A list can be found at The Java Tutorials.

Self written Jython packages for ImageJ

On the way to perfectly organize Jython code, packages are the next step. A Jython package is a folder that contain a set of modules scripts together with a __init__.py file. This file can be empty. Below is a typical structure for the Imagej.app/jars/Lib folder:

1
2
3
4
5
6
7
8
9
10
11
Imagej.app/jars/Lib/
-- myModule.py
-- myPackage/
   -- __init__.py
   -- mathTools.py
   -- customFilters.py
   -- fftTools.py
-- myPackage2/
   -- __init__.py
   -- mathTools.py
   -- stackProcessing.py

There are two packages and one module. The first package contains three modules and the second package contains two modules. We can import the modules on different ways:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Import the single module using the default name:
import myModule
 
# Import mathTools from the first package
import myPackage.mathTools
# Use a function from the imported module
myPackage.mathTools.aFunction()
 
# Import mathTools from the second package
from myPackage2 import mathTools
# Use a function from the imported module without prefixing the package
mathTools.aFunction()
 
# Import customFilters from the first package and rename it
from myPackage import customFilters as filters
# Use a function from customFilters.py
filters.aFunction()
 
# Importing all module from a package
from myPackage2 import *
# The next line will fail
stackProcessing.aFunction()

The reason for the last import to fail is the empty __init__.py. We have to define which modules of the package are imported when using import *. This is done by setting the variable __all__ in the __init__.py. For myPackage2 this line of code is needed:

1
__all__ = ["mathTools", "stackProcessing"]

Besides setting this variable, the __init__.py file can contain normal Jython code that is executed upon import of the package.

Bundle packages in a JAR file

An interesting feature of Jython is to search for packages and modules inside of JAR files. The folder structure from the last section can be modified by packing everything into a single myPackages.jar. The name of the JAR file doesn't matter. All imports work the same as explained before.

1
2
3
4
5
6
7
8
9
10
11
12
Imagej.app/jars/Lib/
-- myPackages.jar
   -- myModule.py
   -- myPackage/
      -- __init__.py
      -- mathTools.py
      -- customFilters.py
      -- fftTools.py
   -- myPackage2/
      -- __init__.py
      -- mathTools.py
      -- stackProcessing.py

The advantage of this approach is that you can share your packages easily. For example you can upload the JAR file to an update site. It is possible to upload .py scripts to update sites too, without packaging into a jar. The advantage of jar are that they allow to define dependencies more systematically.

NB : Script implementing "ImageJ menu macro" and "utilitary scripts" that are used as imported modules in other macros should be packed in separate jar files ! Indeed, if not explicitly mentioned, the jython interpreter only looks in the Jars/Lib folder to import module, so the .jar containing the "utilitary scripts" should be put there, while the jar containing the "ImageJ menu macro" can be put either in the Plugin or script/Plugin folder in order to appear in the ImageJ menu.

Contrary to the scripts in Jars/Lib, the menu macro scripts are not compiled, and as explained above they can not be imported in other scripts since the Plugin folder do not reside in the Jython search path by default.

This is the reason why a given project is rather distributed in 2 different jar files as explained here.

Using maven to build packages

Using maven you can automate the packaging of Jython code into JAR files. This approach is only recommended if you already use maven, as installing and learning how to use maven is not worth the time saving of automated packaging.

At GitHub you will find an example project that you can use as a template. Just run mvn package and maven will generate a JAR file at the target directory.

Links

References

  1. Wikipedia entry on Jython. Accessed: 2016-08-30