Difference between revisions of "Jython Scripting"

(Clean up formatting; add clarification about interpreter plugins)
m (Links: Add aditional link)
(46 intermediate revisions by 7 users not shown)
Line 1: Line 1:
{{Scripting}}== The Jython interpreter plugin ==
+
{{Learn | languages}}
  
The interpreter provides a screen and a prompt. Type any jython code on the prompt to interact with ImageJ.
+
== Introduction ==
  
Launch it from {{bc | Plugins | Scripting | Jython Interpreter}}. See [[Scripting Help]] for all keybindings, and also [[Scripting comparisons]].
+
Jython is an implementation of the Python programming language designed to run on the Java platform. <ref>[http://rsb.info.nih.gov/ij/plugins/index.html Wikipedia entry on Jython]. Accessed: 2016-08-30</ref> In ImageJ Jython is one of several [[Scripting#Supported_languages|supported languages]].
  
{{Warning | Note that ImageJ also ships a unified Script Interpreter plugin, accessible from {{bc | Plugins | Scripting | Script Intepreter}}. 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.}}
+
== When to use Jython ==
  
Within the interpreter, all ImageJ, java.lang.* and TrakEM2 classes are automatically imported. So creating new images and manipulating them is very straighforward.
+
All scripting language supported by ImageJ can be used to access the [http://javadoc.imagej.net/ 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:
  
=== Language basics ===
+
* 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 [[Jython_Scripting#Self_written_Jython_modules_for_ImageJ|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_Scripting|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.
  
* Any text after a # is commented out.
+
=== Explanation ===
* 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 <i>def</i>, and classes with <i>class</i>.
 
* 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 [http://www.python.org/doc/2.5.2/ documentation for python 2.5] applies to Jython bundled with Fiji (with the remarks listed later).
 
  
==== Importing classes ====
+
The Java implementation of Python is limited in functionality. One can use the [https://docs.python.org/2/library/index.html standard library], but it's not possible to install additional Python modules. Moreover a growing number of projects build on Python3 which is not fully compatible with Python2 Jython is based on. If you want to start learning Python it's recommended to learn Python3.x instead of Python2.
  
{{ImportingClasses | lang = Jython}}
+
Even with the given limitations Jython is a powerful language for ImageJ scripting. Hopefully the examples on this page can convince you of that.
You can specify imports in Jython as follows:
 
<source lang="python">
 
from java.io import File
 
</source>
 
Where <code>java.io.File</code> is the class to be imported. See also section [[#Importing_other_.py_scripts_.28modules.29|Importing other ''.py'' scripts (modules)]] for importing user python modules.
 
  
=== Workflow for creating Jython scripts ===  
+
== Jython basics for ImageJ ==
  
To create a script for the GUI, the recommended setup is the following:
+
{{Notice|For an introduction in ImageJ scripting visit the page [[Scripting_basics|Scripting basics]].}}
  
* 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.
+
=== Introduction ===
* Run {{bc | Plugins | Scripting | Refresh Jython scripts}} <b>only</b> 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 {{bc | 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.
+
The aim of this page is not to teach how to program in Python. This purpose is much better fulfilled by the [https://docs.python.org/2/library/index.html documentation of Python2]. The focus of this page is to show how features of the Python language can be useful for ImageJ scripting.
  
If all you need is a script to run in headless mode, simply do:
+
That is why more complex examples are used that are fully functional. Just copy the code to the [[Using_the_Script_Editor|Script Editor]] and try them by yourself. Extensive in-line documentation is used to explain the implementation.
  
  fiji --headless filepath.py
+
=== Image selection using the GenericDialog class ===
  
=== Some limitations of jython ===
+
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 [[Using_the_Script_Editor|Script Editor]] and run it by yourself.
  
{{Box | float=right | title=What about NumPy and SciPy?
+
The following list links to documentation of the used Python features:
| If you like Python, you probably want to use Python modules such as the excellent [http://www.numpy.org/ NumPy] and [http://www.scipy.org/ SciPy] libraries. Unfortunately, Jython does not support linking to Python modules backed by native code. See [http://forum.imagej.net/t/python-scripting/80 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.
+
* [https://docs.python.org/2/library/__future__.html Future statement definitions]
}}
+
* [https://docs.python.org/2/library/functions.html Built-in Functions]
Though jython tries to be as close as possible as python, there are some differences you may experience during scripting.
+
* [https://docs.python.org/2/library/stdtypes.html#str.join str.join()-method]
 +
* [https://docs.python.org/2/tutorial/datastructures.html#list-comprehensions List Comprehensions]
 +
* [https://www.python.org/dev/peps/pep-0289/ Generator Expressions]
 +
* [http://stackoverflow.com/questions/36901/what-does-double-star-and-star-do-for-python-parameters ** (double star) and * (star) parameters]
 +
* [https://docs.python.org/2/library/%5F%5Fmain%5F%5F.html Top-level script environment (__main__)]
 +
* [http://stackoverflow.com/questions/5893163/what-is-the-purpose-of-the-single-underscore-variable-in-python Purpose of the single underscore “_” variable]
  
* <u>Float "special numbers" such as ''NaN'' and ''Inf'' are not handled.</u>
 
For instance,
 
<source lang="python"> a = float('nan') </source>
 
will create the correct float number in python, but will throw an exception in jython.
 
  
Instead, to create a NaN in jython, use:
+
{{GitHubEmbed|org=imagej|repo=imagej-scripting|path=src/main/resources/script_templates/Tutorials/Wiki_Jython_Tutorial_1.py}}
<source lang="python">
 
>>> a = Double.NaN
 
>>> print a
 
NaN
 
</source>
 
To test if a number is NaN:
 
<source lang="python">
 
>>> if Double.isNaN(a):
 
        print "a is NaN!"
 
a is NaN!
 
</source>
 
  
* <u>Some existing python modules can't be imported in jython.</u>
+
=== Using Scripting Parameters ===
:: This is for instance the case of the module ''numpy'', which would have been really convenient for analysing data and results.
 
::
 
:: But see these java numerical libraries: http://math.nist.gov/javanumerics/#libraries , of which:
 
  
:* JaMa (Java Matrix Package)
+
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.
:* Java3D (particularly its [http://java.sun.com/products/java-media/3D/forDevelopers/j3dapi/javax/vecmath/package-summary.html vecmath] package provides general matrix and vector classes ([http://java.sun.com/products/java-media/3D/forDevelopers/j3dapi/javax/vecmath/GMatrix.html GMatrix], [http://java.sun.com/products/java-media/3D/forDevelopers/j3dapi/javax/vecmath/GVector.html GVector]).
 
::
 
:: ... are already included in Fiji.
 
  
* <u>Your Jython version may be matching a much older Python version than you expect.</u>
+
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|Script parameters]]. The Fourier filtering has been created by using the [[Introduction_into_Macro_Programming#The_recorder|Recorder]]. Finally a simple image calculator is used to show that functions can be passed as parameters.
:: 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 <tt>except ExceptionType as e:</tt> or <tt>with open(filepath, 'r') as f:</tt> will fail.
 
  
== Jython tutorials for ImageJ ==
+
This list links to the documentation of Python features that are introduced with this example:
  
=== Defining variables: obtaining the current image ===
+
* [https://docs.python.org/2/library/functions.html#zip The zip() function]
 +
* [http://stackoverflow.com/questions/8421337/rotating-a-two-dimensional-array-in-python Rotating a two-dimensional array]
 +
* [https://docs.python.org/2/reference/expressions.html#lambda Lambda expressions]
  
<source lang="python">
 
imp = IJ.getImage()
 
</source>
 
  
Which is the same as:
+
{{GitHubEmbed|org=imagej|repo=imagej-scripting|path=src/main/resources/script_templates/Tutorials/Wiki_Jython_Tutorial_2.py}}
<source lang="python">
 
imp = WindowManager.getCurrentImage()
 
</source>
 
  
Since calling the above is long and tedious, one can declare a variable that points to the above static methods:
+
=== A batch opener using <code>os.walk()</code> ===
  
<source lang="python">
+
We have yet introduced some powerful functions build into Python. Another one is <code>walk()</code> from the <code>os</code> module. It can be used to go through a directory structure and process the contained files. In this example <code>walk()</code> is used to batch open images with ImageJ's function <code>openImage()</code>.
c = WindowManager.getCurrentImage
 
</source>
 
  
Above note the lack of parentheses.
+
To read more about the used features, the following list provides links to additional information:
  
To execute the function, just use parentheses on it:
+
* [https://docs.python.org/2/library/os.html#os.walk The walk() function]
 +
* [https://docs.python.org/2/library/os.path.html The documentation of os.path]
 +
* [https://docs.python.org/2/library/os.html#os.listdir The listdir() function]
 +
* [http://javadoc.imagej.net/ImageJ1/ Javadoc on IJ.openImage()]
 +
* [https://docs.python.org/2/library/functions.html#isinstance Testing the type of an object using isinstance()]
 +
* [https://docs.python.org/2/library/functions.html#type Identifying the type of an object using type()]
 +
* [https://docs.python.org/2/reference/simple_stmts.html#continue Using continue to control a loop]
 +
* [https://docs.python.org/2/library/stdtypes.html#truth-value-testing Truth Value Testing]
  
<source lang="python">
 
imp = c()
 
</source>
 
  
The above gets the value of <i>c</i>, which is the method named getCurrentImage in class WindowManager, and executes it, storing its returned object in <i>imp</i>.
+
{{GitHubEmbed|org=imagej|repo=imagej-scripting|path=src/main/resources/script_templates/Tutorials/Wiki_Jython_Tutorial_3.py}}
  
 +
== 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.
  
=== Manipulating pixels ===
+
=== ImageJ and Fiji API ===
  
==== Creating a grayscale ramp image ====
+
The following API documentation lists all available module and functions :
First create an image and obtain its pixels:
+
* [http://javadoc.scijava.org/ImageJ1/ ImageJ]
 +
* [http://javadoc.scijava.org/Fiji/ Fiji]
  
<source lang="python">
+
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.
imp = ImagePlus("my new image", FloatProcessor(512, 512))
 
pix = imp.getProcessor().getPixels()
 
</source>
 
  
The length of an array:
+
Let's show one example with the ImageJ package and the class [http://javadoc.scijava.org/ImageJ1/ij/plugin/frame/RoiManager.html RoiManager]. According to the javadoc the RoiManager class resides in <code>ij.plugin.frame</code>. Therefore the code will look like :
  
 
<source lang="python">
 
<source lang="python">
n_pixels = len(pix)
+
from ij.plugin.frame import RoiManager
</source>
+
RM = RoiManager()       # we create an instance of the RoiManager class
 
+
rm = RM.getRoiManager()  # "activate" the RoiManager otherwise it can behave strangely
Then loop to modify them:
 
 
<source lang="python">
 
# catch width
 
w = imp.getWidth()
 
 
# create a ramp gradient from left to right
 
for i in range(len(pix)):
 
  pix[i] = i % w
 
 
# adjust min and max, since we know them
 
imp.getProcessor().setMinAndMax(0, w-1)
 
</source>
 
 
 
... and show the new image:
 
 
 
<source lang="python">
 
imp.show()
 
</source>
 
 
 
 
 
==== Creating a random 8-bit image ====
 
 
 
First import necessary packages: Random, from standard java util library, and [http://www.jython.org/archive/21/docs/jarray.html jarray], the Jython module for native java arrays:
 
 
 
<source lang="python">
 
from java.util import Random
 
from jarray import zeros
 
</source>
 
 
 
Then create the array and fill it with random bytes:
 
 
 
<source lang="python">
 
width = 512
 
height = 512
 
 
pix = zeros(width * height, 'b')
 
Random().nextBytes(pix)
 
</source>
 
 
 
('z' = boolean, 'c' = char, 'b' = byte, 'h' = short, 'i' = int, 'l' = long, 'f' = float and 'd' = double, as explained in the [http://www.jython.org/archive/21/docs/jarray.html jarray documentation].)
 
 
 
Now make a new IndexColorModel (that's what ImageJ's ij.process.LUT class is) for 8-bit images:
 
 
 
<source lang="python">
 
channel = zeros(256, 'b')
 
for i in range(256):
 
    channel[i] = (i -128)
 
cm = LUT(channel, channel, channel)
 
</source>
 
 
 
... and compose a ByteProcessor from the pixels, and assign it to an ImagePlus:
 
 
 
<source lang="python">
 
imp = ImagePlus("Random", ByteProcessor(width, height, pix, cm))
 
imp.show()
 
</source>
 
 
 
==== Creating a random image, the easy way ====
 
 
 
All the above can be summarized like the following:
 
 
 
<source lang="python">
 
from java.util import Random
 
imp = IJ.createImage("A Random Image", "8-bit", 512, 512, 1)
 
Random().nextBytes(imp.getProcessor().getPixels())
 
imp.show()
 
</source>
 
 
 
=== Running a watershed plugin on an image ===
 
 
 
<source lang="python">
 
# 1 - Obtain an image
 
blobs = IJ.openImage("http://imagej.net/images/blobs.gif")
 
# Make a copy with the same properties as blobs image:
 
imp = blobs.createImagePlus()
 
ip = blobs.getProcessor().duplicate()
 
imp.setProcessor("blobs copy", ip)
 
 
 
# 2 - Apply a threshold: only zeros and ones
 
# Set the desired threshold range: keep from 0 to 74
 
ip.setThreshold(147, 147, ImageProcessor.NO_LUT_UPDATE)
 
# Call the Thresholder to convert the image to a mask
 
IJ.run(imp, "Convert to Mask", "")
 
 
 
# 3 - Apply watershed
 
# Create and run new EDM object, which is an Euclidean Distance Map (EDM)
 
# and run the watershed on the ImageProcessor:
 
EDM().toWatershed(ip)
 
 
 
# 4 - Show the watersheded image:
 
imp.show()
 
</source>
 
 
 
The EDM plugin that contains the watershed could have been indirectly applied to the currently active image, which is <i>not</i> recommended:
 
 
 
<source lang="python">
 
imp = IJ.getImage()  # the current image
 
imp.getProcessor().setThreshold(174, 174, ImageProcessor.NO_LUT_UPDATE)
 
IJ.run(imp, "Convert to Mask", "")
 
IJ.run(imp, "Watershed", "")
 
</source>
 
 
 
If you had called <i>show()</i> on the image at any early stage, just update the screen with:
 
 
 
<source lang="python">
 
imp.updateAndDraw()
 
</source>
 
 
 
 
 
==== ... and counting particles, and measuring their areas ====
 
 
 
Continuing from the <i>imp</i> above, that contains the now watersheded "blobs" sample image:
 
 
 
<source lang="python">
 
# Create a table to store the results
 
table = ResultsTable()
 
# Create a hidden ROI manager, to store a ROI for each blob or cell
 
roim = RoiManager(True)
 
# Create a ParticleAnalyzer, with arguments:
 
# 1. options (could be SHOW_ROI_MASKS, SHOW_OUTLINES, SHOW_MASKS, SHOW_NONE, ADD_TO_MANAGER, and others; combined with bitwise-or)
 
# 2. measurement options (see [http://imagej.net/developer/api/ij/measure/Measurements.html Measurements])
 
# 3. a ResultsTable to store the measurements
 
# 4. The minimum size of a particle to consider for measurement
 
# 5. The maximum size (idem)
 
# 6. The minimum circularity of a particle
 
# 7. The maximum circularity
 
pa = ParticleAnalyzer(ParticleAnalyzer.ADD_TO_MANAGER, Measurements.AREA, table, 0, Double.POSITIVE_INFINITY, 0.0, 1.0)
 
pa.setHideOutputImage(True)
 
 
 
if pa.analyze(imp):
 
  print "All ok"
 
else:
 
  print "There was a problem in analyzing", blobs
 
 
 
# The measured areas are listed in the first column of the results table, as a float array:
 
areas = table.getColumn(0)
 
 
 
</source>
 
 
 
To print out the area measurement of each:
 
 
 
>>> for area in areas: print area
 
76.0
 
185.0
 
658.0
 
434.0
 
...
 
 
 
 
 
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 <i>blobs</i>, and measure:
 
 
 
<source lang="python">
 
# 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)
 
</source>
 
 
 
Finally read out the measured mean intensity value of each blob, along with its area:
 
 
 
<source lang="python">
 
for area, mean in zip(areas, means):
 
  print area, mean
 
</source>
 
 
 
6.0 191.47368421052633
 
185.0 179.2864864864865
 
658.0 205.61702127659575
 
434.0 217.32718894009216
 
477.0 212.1425576519916
 
...
 
 
 
=== 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:
 
 
 
<source lang="python">
 
width = 512
 
height = 512
 
stack = ImageStack(width, height)
 
 
 
file = open("/home/albert/Desktop/data.txt", "r")
 
 
 
try:
 
  fp = FloatProcessor(width, height)
 
  pix = fp.getPixels()
 
  cz = 0
 
  # Add as the first slice:
 
  stack.addSlice(str(cz), fp)
 
  # Iterate over all lines in the text file:
 
  for line in file.readlines():
 
    x, y, z, value = line.split(" ")
 
    x = int(x)
 
    y = int(y)
 
    z = int(z)
 
    value = float(value)
 
    # Advance one slice if the Z changed:
 
    if z != cz:
 
      # Next slice
 
      fp = FloatProcessor(width, height)
 
      pix = fp.getPixels()
 
      stack.addSlice(str(cz), fp)
 
      cz += 1
 
    # Assign the value:
 
    pix[y * width + x] = value
 
  # Prepare and show a new image: 
 
  imp = ImagePlus("parsed", stack)
 
  imp.show()
 
# Ensure closing the file handle even if an error is thrown:
 
finally:
 
  file.close()
 
 
</source>
 
</source>
  
 +
=== Using openCV in Jython ===
 +
It is even possible to use most of opencv functionalities within Jython/Fiji. There are several options (see the [https://imagej.net/OpenCV 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.
  
=== Obtain/View histogram and measurements from an image ===
+
A manual installation is also possible by putting the jar packages in the jar folder of imageJ. They are avalaible on the [https://github.com/joheras/IJ-OpenCV IJopenCV github], which even provides a maven option.
 
 
The easiest way is to grab an image and call an ImageJ command to show its histogram:
 
 
 
<source lang="python">
 
imp = IJ.openImage("http://imagej.net/images/blobs.gif")
 
IJ.run(imp, "Histogram", "")
 
</source>
 
  
How ImageJ does it, internally, has to do with the [http://imagej.net/developer/api/ij/process/ImageStatistics.html ImageStatisics] class:
+
==== 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 :
  
 
<source lang="python">
 
<source lang="python">
stats = imp.getStatistics()
+
#@ ImagePlus ImP
print stats.histogram
+
from ijopencv.ij      import ImagePlusMatConverter
</source>
+
from ijopencv.opencv import MatImagePlusConverter
 
+
from ij              import ImagePlus
  array('i',[0, 0, 0, 0, 0, 0, 0, 0, 53, 0, 0, 0, 0, 0, 0, 0, 304,
 
            0, 0, 0, 0, 0, 0, 0, 1209, 0, 0, 0, 0, 0, 0, 0, 3511, 0,
 
            0, 0, 0, 0, 0, 0, 7731, 0, 0, 0, 0, 0, 0, 0, 10396, 0, 0,
 
            0, 0, 0, 0, 0, 7456, 0, 0, 0, 0, 0, 0, 0, 3829, 0, 0, 0,
 
            0, 0, 0, 0, 1992, 0, 0, 0, 0, 0, 0, 0, 1394, 0, 0, 0, 0,
 
            0, 0, 0, 1158, 0, 0, 0, 0, 0, 0, 0, 1022, 0, 0, 0, 0, 0,
 
            0, 0, 984, 0, 0, 0, 0, 0, 0, 0, 902, 0, 0, 0, 0, 0, 0,
 
            0, 840, 0, 0, 0, 0, 0, 0, 0, 830, 0, 0, 0, 0, 0, 0, 0,
 
            926, 0, 0, 0, 0, 0, 0, 0, 835, 0, 0, 0, 0, 0, 0, 0, 901,
 
            0, 0, 0, 0, 0, 0, 0, 1025, 0, 0, 0, 0, 0, 0, 0, 1180, 0,
 
            0, 0, 0, 0, 0, 0, 1209, 0, 0, 0, 0, 0, 0, 0, 1614, 0, 0,
 
            0, 0, 0, 0, 0, 1609, 0, 0, 0, 0, 0, 0, 0, 2220, 0, 0, 0,
 
            0, 0, 0, 0, 2037, 0, 0, 0, 0, 0, 0, 0, 2373, 0, 0, 0, 0,
 
            0, 0, 0, 1568, 0, 0, 0, 0, 0, 0, 0, 1778, 0, 0, 0, 0, 0,
 
            0, 0, 774, 0, 0, 0, 0, 0, 0, 0, 1364, 0, 0, 0, 0, 0, 0, 0])
 
 
 
 
 
The histogram, area and mean are computed by default. Other values like the median need to be specified.
 
  
To calculate other parameters, specify them by bitwise-or composition (see flags in [http://imagej.net/developer/api/ij/measure/Measurements.html Measurements]):
+
# Convert ImagePlus (actually the contained ImageProcessor) to Matrix object
 +
imp2mat = ImagePlusMatConverter()
 +
ImMat = imp2mat.toMat(imp.getProcessor())
 +
print ImMat
  
<source lang="python">
+
# Convert Matrix object to ImageProcessor
stats = imp.getStatistics(Measurements.MEAN | Measurements.MEDIAN | Measurements.AREA)
+
mat2ip = MatImagePlusConverter()
print "mean:", stats.mean, "median:", stats.median, "area:", stats.area
+
NewIP  = mat2ip.toImageProcessor(ImMat)
 +
NewImp = ImagePlus("Matrix converted back to ImagePlus", NewIP)
 +
print NewImP
 
</source>
 
</source>
  
mean: 103.26857775590551 median: 64.0 area: 65024.
+
Such kind of converter is also available for PointRoi to opencv keypoints...
 
 
  
If we set a ROI to the image, then we are measuring only for the inside of the ROI. Here we set an oval ROI of radius 25 pixels, centered:
+
Now to use opencv function, we use the [http://bytedeco.org/javacpp-presets/opencv/apidocs/ JavaCPP API] that contains almost all functions of opencv.
<source lang="python">
 
radius = 25
 
roi = OvalRoi(imp.width/2 - radius, imp.height/2 -radius, radius*2, radius*2)
 
imp.setRoi(roi)
 
stats = imp.getStatistics(Measurements.MEAN | Measurements.MEDIAN | Measurements.AREA)
 
print "mean:", stats.mean, "median:", stats.median, "area:", stats.area
 
</source>
 
 
 
mean: 104.96356275303644 median: 64.0 area: 1976.0
 
 
 
 
 
 
 
To display the histogram window ourselves, we may use the [http://imagej.net/developer/api/ij/gui/HistogramWindow.html HistogramWindow] class:
 
 
 
<source lang="python">
 
hwin = HistogramWindow(imp)
 
</source>
 
 
 
... of which we may grab the image (the plot itself) and save it:
 
 
 
<source lang="python">
 
plotimage = hwin.getImagePlus()
 
IJ.save(plotimage, "/path/to/our/folder/plot.tif")
 
</source>
 
 
 
=== Removing bleeding from one channel to another ===
 
 
 
The technique to use is to divide one channel by the other: the channel to denoise divided by the channel that bled through.
 
 
 
The relatively high-level way to do it is to split the channels and call the ImageCalculator with a "Divide" argument:
 
 
 
<source lang="python">
 
# 1 - Obtain an RGB image stack
 
imp = WindowManager.getCurrentImage()
 
if imp.getType() != ImagePlus.COLOR_RGB:
 
  IJ.showMessage("The active image is not RGB!")
 
  raise RuntimeException("The active image is not RGB!")
 
 
 
if 1 == imp.getNSlices():
 
  IJ.showMessage("Not a stack!")
 
  raise RuntimeException("Not a stack!")
 
 
 
# 2 - Prepare stacks to split slices
 
stack = imp.getStack()
 
red_stack = ImageStack(imp.width, imp.height)
 
green_stack = ImageStack(imp.width, imp.height)
 
 
 
# 3 - Iterate all slices -- notice slices are 1<=i<=size
 
for i in range(1, imp.getNSlices()+1):
 
  slice = stack.getProcessor(i)
 
  red_stack.addSlice(str(i), slice.toFloat(0, None))
 
  green_stack.addSlice(str(i), slice.toFloat(1, None))
 
 
 
# 4 - Apply "divide" via ImageCalculator to the red_stack, which is a new 32-bit stack
 
# Don't use the parameters "create" or "float" or "32" in the parameters string
 
# of the calc.calculate call--then the result of the operation would be
 
# in a new stack that opens beyond our control. Without them, results are
 
# applied to the red_stack
 
calc = ImageCalculator()
 
calc.calculate("Divide stack", ImagePlus("red", red_stack), ImagePlus("green", green_stack))
 
 
 
# 5 - Compose a new color stack
 
new_stack = ImageStack(imp.width, imp.height)
 
for i in range(1, imp.getNSlices()+1):
 
  cp = stack.getProcessor(i).duplicate()
 
  cp.setPixels(0, red_stack.getProcessor(i))
 
  new_stack.addSlice(stack.getSliceLabel(i), cp)
 
 
 
# 6 - Show the new image
 
ImagePlus("Normalized " + imp.title, new_stack).show()
 
</source>
 
 
 
 
 
Alternatively and as an example of direct pixel manipulation, we'll iterate all slices of the image stack, divide the red channel by the green channel, and compose a new stack:
 
 
 
<source lang="python">
 
# 1 - Obtain an RGB image stack
 
imp = WindowManager.getCurrentImage()
 
if imp.getType() != ImagePlus.COLOR_RGB:
 
  IJ.showMessage("The active image is not RGB!")
 
  raise RuntimeException("The active image is not RGB!")
 
 
 
if 1 == imp.getNSlices():
 
  IJ.showMessage("Not a stack!")
 
  raise RuntimeException("Not a stack!")
 
 
 
stack = imp.getStack()
 
 
 
# 2 - Create a new stack to store the result
 
new_stack = ImageStack(imp.width, imp.height)
 
 
 
# 3 - Iterate all slices -- notice slices are 1<=i<=size
 
for i in range(1, imp.getNSlices()+1):
 
  # Get the slice i
 
  slice = stack.getProcessor(i)
 
  # Get two new FloatProcessor with the green and red channel data in them
 
  red = slice.toFloat(0, None)
 
  green = slice.toFloat(1, None)
 
  pix_red = red.getPixels()
 
  pix_green = green.getPixels()
 
  # Create a new FloatProcessor for the normalized result
 
  new_red = FloatProcessor(imp.width, imp.height)
 
  pix_new_red = new_red.getPixels()
 
  # Iterate and set all normalized pixels
 
  for k in range(len(pix_red)):
 
    if 0 != pix_green[k]:
 
      pix_new_red[k] = pix_red[k] / pix_green[k]
 
  # Create a ColorProcessor that has the normalized red and the same green and blue channels
 
  cp = slice.duplicate()
 
  cp.setPixels(0, new_red)  # at channel 0, the red
 
  # Store the normalized slice in the new stack, copying the same slice label
 
  new_stack.addSlice(stack.getSliceLabel(i), cp)
 
 
 
# 4 - Show the normalized stack
 
new_imp = ImagePlus("Normalized " + imp.title, new_stack)
 
new_imp.show()
 
</source>
 
 
 
Notice that this second approach is much slower: accessing every pixel from jython has a high cost. If you would like to do very fast pixel-level manipulations, use java or [[Clojure Scripting|Clojure]].
 
 
 
=== Subtract the minimal value to an image ===
 
 
 
Which is to say, translate the histogram so that the lowest value is at zero.
 
 
 
<source lang="python">
 
# Obtain current image and its pixels
 
imp = IJ.getImage()
 
pix = imp.getProcessor().convertToFloat().getPixels()
 
 
 
# find out the minimal pixel value
 
min = reduce(Math.min, pix)
 
 
 
# create a new pixel array with the minimal value subtracted
 
pix2 = map(lambda x: x - min, pix)
 
 
 
ImagePlus("min subtracted", FloatProcessor(imp.width, imp.height, pix2, None)).show()
 
</source>
 
 
 
Notice we used:
 
* The <i>reduce</i> 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).
 
* <i>lambda</i>, which is used to declare an anonymous function that takes one argument.
 
* The <i>map</i> 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 [http://fiji.sc/javadoc/ij/CompositeImage.html 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 <i>getStackIndex</i> method of the [http://fiji.sc/javadoc/ij/ImagePlus.html ImagePlus], which translates between color channels, z slices and time frames to the slice index in the underlying [http://fiji.sc/javadoc/ij/ImageStack.html ImageStack].
 
 
 
<source lang="python">
 
from ij import IJ, ImagePlus, ImageStack
 
 
 
def extractChannel(imp, nChannel, nFrame):
 
""" Extract a stack for a specific color channel and time frame """
 
stack = imp.getImageStack()
 
ch = ImageStack(imp.width, imp.height)
 
for i in range(1, imp.getNSlices() + 1):
 
  index = imp.getStackIndex(nChannel, i, nFrame)
 
  ch.addSlice(str(i), stack.getProcessor(index))
 
return ImagePlus("Channel " + str(nChannel), ch)
 
 
 
imp = IJ.getImage()
 
extractChannel(imp, 1, 1).show()
 
</source>
 
 
 
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 <i>Drosophila</i> 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.
 
 
 
One could easily add more color channels. But there are already lots.
 
 
 
The script uses [[Imglib]] scripting to normalize images and generate the color composite. See this [http://www.ini.uzh.ch/~acardona/fiji-tutorial/#s10 imglib scripting tutorial] for in-depth explanations.
 
 
 
[[Image:Corti-multi-channel.png]]
 
 
 
<source lang="python">
 
# 2010-12-03 Albert Cardona and Arnim Jenett
 
# At HHMI Janelia Farm, Fiji tutorials class
 
#
 
# Select a directory with multiple image stacks
 
# all of the same dimensions, and show a channel
 
# chooser window to visualize up to 5 of them
 
# in red, green, blue, orange, and gray.
 
#
 
# The stacks are all virtual, opened via LOCI
 
# with BFVirtualStack. The composition of the
 
# RGB ColorProcessor is done with the
 
# script.imglib library.
 
#
 
# Each color channel is shown normalized.
 
# Currently works only with TIF stacks,
 
# and it will interpret them as single-channel.
 
 
 
 
 
from loci.plugins.util import BFVirtualStack
 
from loci.formats import ChannelSeparator
 
from ij.io import DirectoryChooser
 
import os
 
from javax.swing import JScrollPane, JPanel, JComboBox, JLabel, JFrame
 
from java.awt import Color, GridLayout
 
from java.awt.event import ActionListener
 
from script.imglib.math import Compute, Max, Multiply
 
from script.imglib.algorithm import Normalize
 
from script.imglib.color import Red, Green, Blue, RGBA
 
from mpicbg.imglib.image.display.imagej import ImageJFunctions as IJF
 
 
 
 
 
# Choose a directory with lots of tif stacks
 
dc = DirectoryChooser("Choose directory with stacks")
 
srcDir = dc.getDirectory()
 
 
 
# Open each tif stack as a virtual BFVirtualStack
 
bfvs = []
 
names = []
 
for filename in os.listdir(srcDir):
 
  if filename.endswith(".tif"):
 
    print "Reading metadata from", filename
 
    cs = ChannelSeparator()
 
    names.append(filename)
 
    cs.setId(srcDir + filename)
 
    bfvs.append( BFVirtualStack(srcDir + filename, cs, False, False, False) )
 
 
 
names.sort()
 
names = ["None"] + names
 
 
 
 
 
colorToRGB = {
 
'Red' : [255,0,0],
 
'Green' : [0,255,0],
 
'Blue' : [0,0,255],
 
'Orange' : [255,127,0],
 
'Cyan' : [0,255,255],
 
'Yellow' : [255,255,0],
 
'Magenta' : [255,0,255],
 
'Indigo' : [75,0,130],
 
'Violet' : [238,130,238],
 
'Greyscale' : [255,255,255],
 
'Aquamarine' : [127,255,212],
 
'Navy Blue' : [0,0,128],
 
'Sky Blye' : [135,206,235],
 
'Turquoise' : [64,224,208],
 
'Beige' : [245,245,220],
 
'Brown' : [165,42,42],
 
'Chocolate' : [210,105,30],
 
'Dark wood' : [133,94,66],
 
'Light wood' : [133,99,99],
 
'Olive' : [128,128,0],
 
'Green yellow' : [173,255,47],
 
'Sea green' : [32,178,170],
 
'Khaki' : [240,230,140],
 
'Salmon' : [250,128,114],
 
'Pink' : [255,192,203],
 
'Tomato' : [255,99,71],
 
'Scarlet' : [140,23,23],
 
'Purple' : [128,0,128],
 
'Wheat' : [245,222,179],
 
'Silver grey' : [192,192,192]
 
}
 
 
 
# Encode color RGB in floats:
 
tmp = {}
 
for c,rgb in colorToRGB.iteritems():
 
  tmp[c] = [v/255.0 for v in rgb]
 
colorToRGB = tmp
 
 
 
# Colors in the desired listing order:
 
colors = ['Red', 'Green', 'Blue',
 
          'Orange', 'Indigo',
 
          'Cyan', 'Yellow', 'Magenta',
 
          'Turquoise', 'Tomato', 'Olive',
 
          'Violet', 'Green yellow', 'Khaki',
 
          'Scarlet', 'Beige', 'Chocolate',
 
          'Silver grey', 'Pink', 'Wheat',
 
          'Sea green', 'Greyscale', 'Light wood',
 
          'Sky Blye', 'Brown', 'Salmon', 'Navy Blue',
 
          'Aquamarine', 'Purple', 'Dark wood']
 
 
 
# Initalize table of colors vs stacks to use:
 
table = {}
 
for k,v in zip(colors, [1] + [0 for i in range(len(colors)-1)]):
 
  table[k] = v
 
 
 
 
 
def asImg(color, section):
 
  global bfvs, table
 
  index = table[color]
 
  if 0 == index:
 
    return 0  # is "None" color
 
  return IJF.wrap(ImagePlus("", bfvs[index-1].getProcessor(section)))
 
 
 
def maybeNormalize(fn):
 
  """ Do not normalize if no images are present. """
 
  if 0 == fn:
 
    return fn
 
  cursors = []
 
  fn.findCursors(cursors)
 
  if len(cursors) > 0:
 
    return Multiply(Normalize(fn), 255)
 
  return fn
 
 
 
def blendColors(section):
 
  global bfvs, table, colorToRGB
 
  red =  0
 
  green = 0
 
  blue = 0
 
  for colorName,index in table.iteritems():
 
    if 0 == index: continue
 
    img = IJF.wrap(ImagePlus("", bfvs[index-1].getProcessor(section)))
 
    rgb = colorToRGB[colorName]
 
    if 0 != rgb[0]:
 
      red = Max(red, Multiply(img, rgb[0]))
 
    if 0 != rgb[1]:
 
      green = Max(green, Multiply(img, rgb[1]))
 
    if 0 != rgb[2]:
 
      blue = Max(blue, Multiply(img, rgb[2]))
 
  return red, green, blue
 
 
 
class VS(VirtualStack):
 
  def __init__(self):
 
    self.last = None
 
  def getProcessor(self, i):
 
    """ Channel color composition into a single RGB image, as ColorProcessor. 'i' is the section index, 1<=i<=size """
 
    red, green, blue = blendColors(i)
 
    # Transform to RGB by normalizing and scaling to 255
 
    red = maybeNormalize(red)
 
    green = maybeNormalize(green)
 
    blue = maybeNormalize(blue)
 
    # Compose
 
    rgb = RGBA(red, green, blue).asImage()
 
    self.last = IJF.displayAsVirtualStack(rgb).getProcessor()
 
    return self.last
 
  def getSize(self):
 
    return bfvs[0].getSize()
 
  def getSliceLabel(self, i):
 
    return str(i)
 
  def getWidth(self):
 
    return self.last.getWidth()
 
  def getHeight(self):
 
    return self.last.getHeight()
 
  def getPixels(self, i):
 
    return self.getProcessor(i).getPixels()
 
  def setPixels(self, pix, i):
 
    pass
 
 
 
 
 
# Create a new image stack
 
print os.path.split(srcDir)
 
ourImp = ImagePlus(os.path.split(srcDir)[1], VS())
 
ourImp.show()
 
 
 
# Create a bunch of panels, one for each color channel
 
all = JPanel()
 
layout = GridLayout(len(colors), 2)
 
all.setLayout(layout)
 
 
 
# GUI to choose which stacks is shown in which channel
 
class Listener(ActionListener):
 
  def __init__(self, color, choice, imp):
 
    self.color = color
 
    self.choice = choice
 
    self.imp = imp
 
  def actionPerformed(self, event):
 
    global table
 
    table[self.color] = self.choice.getSelectedIndex()
 
    self.imp.updateAndRepaintWindow()
 
 
 
for color in colors:
 
  all.add(JLabel(color))
 
  choice = JComboBox(names)
 
  choice.setSelectedIndex(table[color])
 
  choice.addActionListener(Listener(color, choice, ourImp))
 
  all.add(choice)
 
 
 
frame = JFrame("Channels")
 
frame.getContentPane().add(JScrollPane(all))
 
frame.pack()
 
 
 
frame.setVisible(True)
 
</source>
 
 
 
=== Sort all points of a PointRoi into a chain by distance to each other ===
 
 
 
There may be better ways, but here is one. Read the header to understand its limitations.
 
 
 
<source lang="python">
 
# Albert Cardona 2010-12-17 for Victoria Butler at HHMI Janelia Farm
 
# Given a PointRoi, order the points in a chain
 
# Assumes that the point furthest from all points
 
# is the start or the end of the chain.
 
 
 
from javax.vecmath import Point2f
 
 
 
# Obtain the PointRoi of the current image
 
proi = IJ.getImage().getRoi()
 
 
 
# Interrupt if the ROI is not a PointRoi instance:
 
if proi.getClass() != PointRoi:
 
  raise Exception("Not a PointRoi!")
 
 
 
class Point(Comparable):
 
  def __init__(self, x, y):
 
    self.p = Point2f(x, y)
 
    self.distances = {}
 
    self.distAll = None
 
  def distance(self, point):
 
    return self.p.distance(point.p)
 
  def distanceToAll(self):
 
    if self.distAll is None:
 
      self.distAll = reduce(lambda a, b: a + b, self.distances.values())
 
    return self.distAll
 
  def compareTo(self, point):
 
    if self.distanceToAll() < point.distanceToAll():
 
      return -1
 
    return 1
 
  def toString(self):
 
    return self.p.toString()
 
  def closest(self, points):
 
    """ Find the closest point that is not contained in the set of given points. """
 
    next = None
 
    dist = Float.MAX_VALUE
 
    for p,d in self.distances.iteritems():
 
      if d < dist and not p in points:
 
        next = p
 
        dist = d
 
    return next
 
 
 
# Convert PointRoi points to Point instances
 
px = proi.getXCoordinates()
 
py = proi.getYCoordinates()
 
bounds = proi.getBounds()
 
points = []
 
for i in range(proi.getNCoordinates()):
 
  points.append(Point(bounds.x + px[i], bounds.y + py[i]))
 
 
 
# Precompute all-to-all distances
 
allToAll = {}
 
for j in range(len(points)):
 
  for k in range(j+1, len(points)):
 
    distance = points[j].distance(points[k])
 
    points[j].distances[points[k]] = distance
 
    points[k].distances[points[j]] = distance
 
 
 
# Choose a starting point.
 
# In this case, we use the point most distant from all other points
 
points.sort()
 
first = points[-1]
 
print "First:", first
 
 
 
# Grow the chain from the starting point
 
chain = [first]
 
seen = set() # for fast look-up
 
seen.add(chain[0])
 
while len(chain) < len(points):
 
  next = chain[-1].closest(seen)
 
  if next is None:
 
    break
 
  chain.append(next)
 
  seen.add(next)
 
 
 
print "Chain:", chain
 
</source>
 
 
 
=== Correct illumination in a stack: apply the illumination of one slice to all others ===
 
 
 
The Multi-focus 3D Microscope (Sara Abrahamsson and Matz Gustafsson) takes a single image and later, computationally, 9 image planes are extracted.
 
 
 
The middle slice usually has the desired illumination levels, while the other 8 slices (4 before and 4 after) do not. Here is a script to apply the illumination of the 5th slice to all other slices.
 
 
 
The script takes a directory of images and processed them all, resulting in new images stored as "*-corrected.tif" in the same directory.
 
 
 
<source lang="python">
 
# Albert Cardona 2011-06-09 at HHMI Janelia Farm
 
# Takes a stack of 9 slices
 
# and then computes the mean and stdDev of slice number 5
 
# and normalize the intensity of the other 8 slices
 
# to that of slice 5.
 
#
 
# Created for Jiji Chen to process image stacks from
 
# the multifocus 3D microscope from Matz Gustafsson
 
# and Sara Abrahamsson
 
 
 
 
 
from math import sqrt
 
import os
 
 
 
def computeMean(pixels):
 
  return sum(pixels) / float(len(pixels))
 
 
 
def computeStdDev(pixels, mean):
 
  s = 0
 
  for i in range(len(pixels)):
 
    s += pow(pixels[i] - mean, 2)
 
  return sqrt(s / float(len(pixels) -1))
 
 
 
def process9ImagePlanes(imp):
 
  # reference slice
 
  refSlice = 5
 
  ref = imp.getStack().getProcessor(5)
 
  refMean = sum(ref.getPixels()) / float(len(ref.getPixels()))
 
  refStdDev = computeStdDev(ref.getPixels(), refMean)
 
 
 
  # New stack with the corrected slices
 
  stack = ImageStack(ref.width, ref.height)
 
 
 
  for i in range(1, 10):
 
    # skip the reference slice
 
    if 5 == i:
 
      stack.addSlice(imp.getStack().getSliceLabel(5), ref.convertToFloat())
 
      continue
 
    ip = imp.getStack().getProcessor(i).convertToFloat()
 
    mean = computeMean(ip.getPixels())
 
    stdDev = computeStdDev(ip.getPixels(), mean)
 
    ip.add(-mean)
 
    ip.multiply(1/stdDev)
 
    ip.multiply(refStdDev)
 
    ip.add(refMean)
 
    stack.addSlice(imp.getStack().getSliceLabel(i), ip)
 
 
 
  return ImagePlus(imp.title, stack)
 
 
 
 
 
def accept(filename):
 
  """ Work only with TIFF files. """
 
  return len(filename) - 4 == filename.rfind(".tif")
 
 
 
def run():
 
  dc = DirectoryChooser("pick folder with image stacks")
 
  folder = dc.getDirectory()
 
  if folder is None:
 
    return
 
  for filename in filter(accept, os.listdir(folder)):
 
    imp = IJ.openImage(os.path.join(folder, filename))
 
    if imp is None:
 
      print "Failed to open image:", filename
 
      continue
 
    corrected = process9ImagePlanes(imp)
 
    IJ.save(corrected, os.path.join(folder, filename[0:-4] + "-corrected.tif"))
 
 
 
run()
 
 
 
</source>
 
 
 
 
 
=== Add a mouse listener to the canvas of every open image ===
 
 
 
<source lang="python">
 
from java.awt.event import MouseAdapter
 
 
 
def doSomething(imp):
 
""" A function to react to a mouse click on an image canvas. """
 
IJ.log("clicked on: " + str(imp))
 
 
 
class ML(MouseAdapter):
 
def mousePressed(self, event):
 
  canvas = event.getSource()
 
  imp = canvas.getImage()
 
  doSomething(imp)
 
 
 
listener = ML()
 
 
 
for imp in map(WindowManager.getImage, WindowManager.getIDList()):
 
win = imp.getWindow()
 
if win is None:
 
  continue
 
win.getCanvas().addMouseListener(listener)
 
</source>
 
 
 
After running the script, clicking on any image will result in
 
printing a line to the log window, like:
 
 
 
  clicked on: imp[Untitled-1 400x200x1]
 
 
 
 
 
=== Add a key listener to the canvas of every open image ===
 
 
 
<source lang="python">
 
from java.awt.event import KeyEvent, KeyAdapter
 
 
   
 
   
def doSomething(imp, keyEvent):
 
  """ A function to react to key being pressed on an image canvas. """
 
  IJ.log("clicked keyCode " + str(keyEvent.getKeyCode()) + " on image " + str(imp))
 
  # Prevent further propagation of the key event:
 
  keyEvent.consume()
 
 
class ListenToKey(KeyAdapter):
 
  def keyPressed(this, event):
 
    imp = event.getSource().getImage()
 
    doSomething(imp, event)
 
 
listener = ListenToKey()
 
 
for imp in map(WindowManager.getImage, WindowManager.getIDList()):
 
  win = imp.getWindow()
 
  if win is None:
 
    continue
 
  canvas = win.getCanvas()
 
  # Remove existing key listeners
 
  kls = canvas.getKeyListeners()
 
  map(canvas.removeKeyListener, kls)
 
  # Add our key listener
 
  canvas.addKeyListener(listener)
 
  # Optionally re-add existing key listeners
 
  # map(canvas.addKeyListener, kls)
 
</source>
 
 
=== Create a virtual stack from the TIF files present in a folder and its subfolders, recursively ===
 
 
 
<source lang="python">
 
<source lang="python">
# Walk recursively through an user-selected directory
+
from org.bytedeco.javacpp.opencv_core import Mat, CvMat, vconcat
# 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
+
## Typical matrices ##
from ij.io import DirectoryChooser
 
from ij import IJ, ImagePlus, VirtualStack
 
  
def run():
+
# Identity Matrix of size (3x3) and type 8-bit
  srcDir = DirectoryChooser("Choose!").getDirectory()
+
Id = Mat().eye(3,3,0).asMat()
  if not srcDir:
+
print Id
    # user canceled dialog
+
print CvMat(Id) # handy to visualise the matrix
    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()
+
# Matrix of ones (3x3)  
</source>
+
One = Mat().ones(3,3,0).asMat()
  
=== Open the slices of a very large multi-image stack file one by one, and save each as a new image file ===
+
# Matrix of zeros (3x3)
<source lang="python">
+
Zero = Mat().zeros(3,3,0).asMat()
# 2011-10-18 Albert Cardona for Nuno da Costa
 
# Choose a multi-slice image stack file in a virtual way
 
# and save each slice as an individual image file
 
# in a user-chosen directory.
 
  
import os
+
# Custom Matrices
from loci.plugins.util import BFVirtualStack
+
# 1D-Matrix can be initialize from a list
from loci.formats import ChannelSeparator
+
# For 2D (or more) we have to concatenate 1D-Matrices
  
def run():
+
Row1 = Mat([1,2,3,4,5]) # 1D matrix
  # Choose a file to open
+
Row2 = Mat([6,7,8,9,10])
  od = OpenDialog("Choose multi-image file", None)
 
  srcDir = od.getDirectory()
 
  if srcDir is None:
 
    # User canceled the dialog
 
    return
 
  path = os.path.join(srcDir, od.getFileName())
 
  # Choose a directory to store each slice as a file
 
  targetDir = DirectoryChooser("Choose target directory").getDirectory()
 
  if targetDir is None:
 
    # User canceled the dialog
 
    return
 
  # Ready:
 
  cs = ChannelSeparator()
 
  cs.setId(path)
 
  bf = BFVirtualStack(path, cs, False, False, False)
 
  for sliceIndex in xrange(1, bf.getSize() +1):
 
    print "Processing slice", sliceIndex
 
    ip = bf.getProcessor(sliceIndex)
 
    sliceFileName = os.path.join(targetDir, str(sliceIndex) + ".tif")
 
    FileSaver(ImagePlus(str(sliceIndex), ip)).saveAsTiff(sliceFileName)
 
  
run()
+
TwoColumn = Mat()              # initialise output
 +
vconcat(Col1, Col2, TwoColumn) # output stored in TwoColumn
 +
print CvMat(TwoColumn)
 
</source>
 
</source>
  
 +
{{Warning | The <code>org.bytedeco.javacpp.opencv_core.Mat</code> object is different than the <code>org.opencv.core.Mat</code> !! They don't have exactly the same attributes and functions. In Fiji you should always use the objects from <code>org.bytedeco.javacpp</code>.}}
  
=== 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 [http://imagej.net/developer/api/ij/process/ImageProcessor.html ImageProcessor] obtained from the [http://imagej.net/developer/api/ij/ImageStack.html ImageStack] that one can get from the [http://imagej.net/developer/api/ij/ImagePlus.html ImagePlus]. (The [http://imagej.net/developer/api/ij/ImagePlus.html ImagePlus] being what the opener or the [http://imagej.net/developer/api/ij/WindowManager.html WindowManager] provides.)
 
 
<source lang="python">
 
# 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
+
Similarly there is some apparent redudancy for the function in the javacpp API.
stack = volume.getStack()
 
  
# Fill every stack slice with zeros for the area outside the mask
+
ex : Transform exists in 3 different places :
for i in xrange(1, stack.getSize() + 1):
+
* <code>org.opencv.core.Core.transform</code>
  # ip is the ImageProcessor for one stack slice
+
This one takes <code>org.opencv.core.Mat</code> as input. It is currently challenging to have such object in Fiji.
  ip = stack.getProcessor(i)
 
  ip.setValue(0)
 
  ip.fill(mask)
 
  
volume.updateAndDraw()
+
* <code>org.bytedeco.javacpp.opencv_core.cvTransform</code>
volume.show()
+
using <code>CvArr</code> as input, but even if you manage to convert your input as a <code>CvArr</code> it crashes Fiji. Apparently it is a deprecated version.
</source>
 
  
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:
+
* <code>org.bytedeco.javacpp.opencv_core.transform</code>
 +
That's the one to use ! It takes only <code>org.bytedeco.javacpp.opencv_core.Mat</code> as input, which is the most approriate in Fiji/Jython
  
<source lang="python">
 
mask = mask.duplicate()
 
mask.invert()
 
</source>
 
  
=== Open all series in a LIF file with Bio-Formats ===
+
==== 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.
  
 
<source lang="python">
 
<source lang="python">
 +
from org.bytedeco.javacpp.opencv_core import Scalar
  
# 2014-11-24 Harri Jäälinoja
+
# Real scalar can be initiated with a float parameters
 +
Number = Scalar(5.0)
 +
Number = Scalar(float(5))
 +
print Number
  
from loci.plugins.in import ImagePlusReader,ImporterOptions,ImportProcess
+
# Using an integer as parameter has a different meaning
import sys
+
Empty = Scalar(5) # This initiate an empty Scalar object of size 5
 +
print Empty
  
filename = sys.argv[1]
+
# Alternatively one can set the other values of the Scalar
opts = ImporterOptions()
+
Complex = Scalar(1,2,3,4)
opts.setId(filename)
+
print Complex
opts.setUngroupFiles(True)
 
 
 
# set up import process
 
process = ImportProcess(opts)
 
process.execute()
 
nseries = process.getSeriesCount()
 
 
 
# reader belonging to the import process
 
reader = process.getReader()
 
 
 
# reader external to the import process
 
impReader = ImagePlusReader(process)
 
for i in range(0, nseries):
 
    print "%d/%d %s" % (i+1, nseries, process.getSeriesLabel(i))
 
   
 
    # activate series (same as checkbox in GUI)
 
    opts.setSeriesOn(i,True)
 
 
 
    # point import process reader to this series
 
    reader.setSeries(i)
 
 
 
    # read and process all images in series
 
    imps = impReader.openImagePlus()
 
    for imp in imps:
 
        imp.show()
 
        wait = Wait(str(i) + imp.getTitle())
 
        wait.show()
 
        imp.close()
 
 
 
    # deactivate series (otherwise next iteration will have +1 active series)
 
    opts.setSeriesOn(i, False)
 
 
</source>
 
</source>
  
  
 
+
==== Operations ====
=== Open and save movies with the FFMPEG I/O plugin ===
+
It is possible to perform some operations between matrices, or between Scalar and matrices.
 
 
First note that the FFMPEG I/O plugin was a proof-of-concept that is completely '''unmaintained'''.
 
 
 
Then open the [[Fiji Updater]], push the "Manage update sites" at the bottom left of the dialog, and install the [http://fiji.sc/~schindelin/ffmpeg-plugins/ FFMPEG] plugin by {{Person|Schindelin}} by ticking its checkbox, as [[How_to_follow_a_3rd_party_update_site|explained in more detail here]].
 
 
 
See also the Java source code for the {{GitHub|repo=fiji|tag=8164ae68|path=native/FFMPEG_IO/plugin/src/main/java/fiji/ffmpeg/IO.java|label=IO}} class from the [https://github.com/fiji/fiji/tree/8164ae68/native/FFMPEG_IO/plugin/src/main/java/fiji/ffmpeg/ FFMPEG plugin source code site].
 
  
 
<source lang="python">
 
<source lang="python">
""" Albert Cardona for Marta Zlatic, 2014-01-24. """
+
from org.bytedeco.javacpp.opencv_core import Scalar, Mat, subtract
 
 
from fiji.ffmpeg import IO
 
from java.awt import Color
 
import os
 
 
 
def load(path, first_frame=0, last_frame=-1):
 
  """ Load the whole movie by default. """
 
  io = IO()
 
  imp = io.readMovie(path, False, first_frame, last_frame)
 
  return imp
 
 
 
def save(path, imp, frame_rate=30, bit_rate=400000):
 
  """ frame_rate in fps (frames per second).
 
      bit_rate defines the quality of the movie: higher bit rate results in larger, higher quality movies.
 
      The movie format (e.g. AVI, MPG, etc.) is chosen by the path filename extension. """
 
  io = IO()
 
  io.writeMovie(imp, path, frame_rate, bit_rate)
 
 
 
 
 
def process(imp, convert, roi, time_zero, time_range):
 
  """ Crop, convert to another format, and time-stamp. """
 
  stack = imp.getStack()
 
  size = stack.getSize()
 
  bounds = roi.getBounds()
 
  new_stack = ImageStack(bounds.width, bounds.height)
 
  for i in xrange(1, size + 1):
 
    ip = convert(stack.getProcessor(i))
 
    ip.setRoi(roi)
 
    c = ip.crop()
 
    c.setColor(Color.white)
 
    time = "%.2f" % (time_zero + time_range * (float(i-1) / (size -1)))
 
    c.drawString(time, 5, 15)
 
    new_stack.addSlice(str(i), c)
 
 
 
  return ImagePlus(imp.title, new_stack)
 
 
 
 
 
def batch_process(extension, source_dir, output_dir, convert, roi, time_zero, time_range):
 
  for filename in os.listdir(source_dir):
 
    if filename.endswith(extension):
 
      if os.path.exists(target_dir + filename):
 
        # Skip if movie exists at destination
 
        continue
 
      imp = load(source_dir + filename)
 
      imp2 = process(imp, convert, roi, time_zero, time_range)
 
      imp.flush()
 
      save(target_dir + filename, imp2)
 
      imp2.flush()
 
 
 
 
 
def convert(ip):
 
  """ Convert to 8=bit and crop the range to [0, 128] pixel values. """
 
  c = ip.convertToByte(True)
 
  c.setMinAndMax(0, 128)
 
  return c
 
 
 
  
source_dir = '/path/to/list_of_AVI_movies/'
+
A = Mat([1,2,3,4,5])
target_dir = '/path/to/new_list_of_AVI_movies/'
+
B = Mat([1,2,-3,-4,0])
  
roi = Roi(50, 50, 256, 256)
+
Number = Scalar(10.0)
time_zero = 30  # start at 30 seconds
 
time_range = 30  # range of (also) 30 seconds
 
  
# Process all AVI movie files, saving them also as AVI files
+
## Number - B ( B-Number is also possible)
batch_process('.avi', source_dir, target_dir, convert, roi, time_zero, time_range)
+
Expr = subtract(Number,B)
 +
print CvMat(Expr.asMat())
  
 +
## A - B
 +
Out = Mat()
 +
subtract(A,B,Out)
 +
print CvMat(Out)
 
</source>
 
</source>
  
=== Skeletonize an image and analyze the skeleton ===
+
== Self written Jython modules for ImageJ ==
  
<source lang="python">
+
In Jython you can write all commands line by line in a single file and execute it. To create a neat program, [https://docs.python.org/2/tutorial/controlflow.html#defining-functions functions] and [https://docs.python.org/2/tutorial/classes.html classes] can be used to structure code. To prevent using copy&past for regularly used functions and classes, [https://docs.python.org/2/tutorial/modules.html modules] are the way to choose. Modules are files that contain functions and classes to import into other files.
from ij import IJ
 
from skeleton_analysis import AnalyzeSkeleton_,Graph,Edge,Vertex
 
  
# open image, blur, make b/w, skeletonize
+
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:
imp = IJ.openImage("/path/to/image.tif")
 
IJ.run(imp,"Gaussian Blur...","sigma=5")
 
IJ.run(imp,"Make Binary","")
 
IJ.run(imp,"Skeletonize","")
 
 
 
# run AnalyzeSkeleton
 
# (see http://fiji.sc/AnalyzeSkeleton
 
# and http://fiji.sc/javadoc/skeleton_analysis/package-summary.html)
 
skel = AnalyzeSkeleton_()
 
skel.setup("",imp)
 
skelResult = skel.run(skel.NONE, False, True, None, True, True)
 
 
 
# get the separate skeletons
 
graph = skelResult.getGraph()
 
print len(graph)
 
print skelResult.getNumOfTrees()
 
 
 
def getGraphLength(graph):
 
    length = 0
 
    for g in graph.getEdges():
 
    length = length + g.getLength()
 
    return length
 
 
 
# find the longest graph
 
graph = sorted(graph, key=lambda g: getGraphLength(g), reverse=True)
 
longestGraph = graph[0]
 
 
 
# find the longest edge
 
edges = longestGraph.getEdges()
 
edges = sorted(edges, key=lambda edge: edge.getLength(), reverse=True)
 
longestEdge = edges[0]
 
  
 +
<source lang='python'>
 +
from sys import path
 +
print(path)
 
</source>
 
</source>
  
=== Find peaks in a 3D image ===
+
When running this code the result is an output like
 
 
<source lang="python">
 
# @ImagePlus imp
 
  
from fiji.plugin.trackmate.detection import DogDetector
+
<source lang='python'>
from ij.gui import PointRoi
+
['/home/michael/Software/ImageJ.app/jars/Lib', '/home/michael/Software/ImageJ.app/jars/jython-shaded-2.7.0.jar/Lib', '__classpath__', '__pyclasspath__/']
from ij.plugin.frame import RoiManager
 
from net.imglib2.img.display.imagej import ImageJFunctions
 
 
 
# Set the parameters for DogDetector
 
img = ImageJFunctions.wrap(imp)
 
interval = img
 
cal = imp.getCalibration()
 
calibration = [cal.pixelWidth, cal.pixelHeight, cal.pixelDepth]
 
radius = 0.2 # the radius is half the diameter
 
threshold = 100
 
doSubpixel = True
 
doMedian = False
 
 
 
# Setup spot detector
 
# (see http://javadoc.imagej.net/Fiji/fiji/plugin/trackmate/detection/DogDetector.html)
 
#
 
# public DogDetector(RandomAccessible<T> img,
 
#            Interval interval,
 
#            double[] calibration,
 
#            double radius,
 
#            double threshold,
 
#            boolean doSubPixelLocalization,
 
#            boolean doMedianFilter)
 
detector = DogDetector(img, interval, calibration, radius, threshold, doSubpixel, doMedian)
 
 
 
# Start processing and display the results
 
if detector.process():
 
    # Get the list of peaks found
 
    peaks = detector.getResult()
 
    print str(len(peaks)), "peaks were found."
 
 
 
    # Add points to ROI manager
 
    rm = RoiManager.getInstance()
 
    if not rm:
 
        rm = RoiManager()
 
 
 
    # Loop through all the peak that were found
 
    for peak in peaks:
 
        # Print the current coordinates
 
        print peak.getDoublePosition(0), peak.getDoublePosition(1), peak.getDoublePosition(2)
 
        # Add the current peak to the Roi manager
 
        proi = PointRoi(peak.getDoublePosition(0) / cal.pixelWidth, peak.getDoublePosition(1) / cal.pixelHeight)
 
        proi.setPosition(int(peak.getDoublePosition(2) / cal.pixelDepth))
 
        rm.addRoi(proi)
 
 
 
    # Show all ROIs on the image
 
    rm.runCommand(imp, "Show All")
 
 
 
else:
 
    print "The detector could not process the data."
 
 
</source>
 
</source>
  
== Tips and Tricks ==
+
This tells us that the folder <code>jars/Lib/</code> inside our ImageJ/Fiji directory is the right place to save modules. As <code>Lib/</code> does not exist by default, we have to create it.
  
=== Getting a list of all members in one package ===
+
When a module is imported for the first time, Jython will compile it to Java code. If there is a module named <code>myModule.py</code>, Jython will create a file called <code>myModule$py.class</code>. The next time the module is imported, Jython will use the class file instead of the py file. When modifying the module, it necessary to restart ImageJ/Fiji to use the modified one. A work around is the following code (found at [http://stackoverflow.com/questions/10531920/jython-import-or-reload-dynamically stackoverflow]) that will force Jython to recompile all modules:
  
You can use the Python function ''dir(<package>)'' to see the contents of a package:
+
<source lang='python'>
 
+
# Use this to recompile Jython modules to class files.
<source lang="python">
+
from sys import modules
import ij
+
modules.clear()
print dir(ij)
+
# Imports of Jython modules are placed below:
 +
import myModule
 
</source>
 
</source>
  
'''Note:''' As of April 26nd, 2010, you need to start Fiji with
+
=== Adding a custom directory ===
 
 
fiji -Dpython.cachedir.skip=false --
 
 
 
for ''dir(<package>)'' to work.
 
  
=== Specifying the encoding of the source ===
+
If you don't want to use <code>jars/Lib/</code> to save your modules, you have to extend the array <code>sys.path</code>:
  
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''.
+
<source lang='python'>
 
 
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 [http://docs.python.org/tutorial/interpreter.html#source-code-encoding 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:
 
 
 
<source lang="python">
 
from org.python.core import codecs
 
codecs.setDefaultEncoding('utf-8')
 
</source>
 
 
 
=== Error handling with try / except / finally ===
 
 
 
See complete documentation at: [http://jythonpodcast.hostjava.net/jythonbook/chapter6.html jython book chapter 6].
 
 
 
<source lang="python">
 
x = 10
 
y = 0
 
 
 
try:
 
    z = x / y
 
except NameError, e1:
 
    print "A variable is not defined!", e1
 
except ZeroDivisionError, e2:
 
    print "Dividing by zero doesn't make any sense! Error:", e2
 
finally:
 
    print "This line will always print no matter what error occurs."
 
</source>
 
 
 
Which prints:
 
 
 
Dividing by zero doesn't make any sense! Error: integer division or modulo by zero
 
This line will always print no matter what errors occurs
 
 
 
To catch any kind of errors, use <i>sys.exc_info</i>:
 
 
 
<source lang="python">
 
import sys
 
 
 
try:
 
  z = x / z
 
except:
 
  print "Error: ", sys.exc_info()
 
</source>
 
 
 
Which prints:
 
 
 
Error:  (<type 'exceptions.NameError'>, NameError("name 'x' is not defined",), <traceback object at 0x2>)
 
 
 
 
 
To ensure that you see the stack trace, print it to the ImageJ log window instead of stdout (whathever the latter may be):
 
 
 
<source lang="python">
 
    IJ.log(str(sys.exc_info()))
 
</source>
 
 
 
=== 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:
 
 
 
<source lang="python">
 
 
from sys import path
 
from sys import path
 
from java.lang.System import getProperty
 
from java.lang.System import getProperty
 
+
 
# extend the search path by $FIJI_ROOT/bin/
 
# extend the search path by $FIJI_ROOT/bin/
 +
# 'fiji.dir' works for plain ImageJ, too.
 
path.append(getProperty('fiji.dir') + '/bin')
 
path.append(getProperty('fiji.dir') + '/bin')
 
+
# an alternative can be the users home directory
# Now you can import $FIJI_ROOT/bin/compat.py
+
# path.append(getProperty('user.home') + '/JythonModules')
import compat
 
</source>
 
 
 
A situation you are likely to encounter is when you have multiple jython scripts in a folder under the fiji plugins folder.
 
 
 
For example, suppose you have the folder ''my scripts'' under the fiji ''plugins'' folder, in which you have the script ''Filters.py'' with the following filter functions in it:
 
 
 
<source lang="python">
 
# Script plugins/my scripts/Filters.py
 
from ij import IJ
 
from ij.plugin import Duplicator
 
 
 
def median(imp, radius):
 
  """ Apply a median filter to a copy
 
      of the given ImagePlus, and return it. """
 
  copy = Duplicator().run(imp)
 
  IJ.run(copy, "Median...", "radius=" + str(radius))
 
  return copy
 
 
 
def removeOutliers(imp, radius, threshold, bright):
 
  """ Apply a remove outliers filter to a copy
 
      of the given ImagePlus, and return it. """
 
  copy = Duplicator().run(imp)
 
  which = "Bright" if bright else "Dark"
 
  IJ.run(copy, "Remove Outliers...", "radius=" + str(radius) \
 
      + " threshold=" + str(threshold) + " which=" + which)
 
  return copy
 
</source>
 
 
 
And now you have a second script in which you want to use a function from the ''Filters.py'' script:
 
 
 
<source lang="python">
 
from ij import IJ
 
import sys
 
from java.lang.System import getProperty
 
sys.path.append(getProperty("fiji.dir") + "/plugins/my scripts")
 
from Filters import median
 
 
 
imp = IJ.getImage()
 
medianFiltered = median(imp, 5.0)
 
medianFiltered.show()
 
</source>
 
 
 
=== Defining a class and creating instances of the new class ===
 
 
 
A simple class to store an X,Y coordinate. (In real code, just use javax.vecmath.* classes such as Point3f, Point3d, etc.)
 
 
 
The constructor is defined with <i>__init__</i>, and takes at least one argument , named <i>self</i> by convention (you may name it something else, like <i>this</i>).
 
 
 
<source lang="python">
 
from math import sqrt, pow
 
 
 
class Point:
 
  def __init__(self, x, y):
 
    self.x = x
 
    self.y = y
 
  def distance(self, other):
 
    return sqrt(pow(self.x - other.x, 2), pow(self.y - other.y, 2))
 
</source>
 
 
 
... and create two instances, and measure the distance between them. To create the new instances, we use the name of the class with the arguments in brackets:
 
 
 
<source lang="python">
 
p1 = Point(10, 20)
 
p2 = Point(40, 55.3)
 
 
 
print "Distance:", p1.distance(p2)
 
</source>
 
 
 
=== Adding a static method to a class ===
 
 
 
A static method is a method of a class that doesn't need a <i>self</i> first argument. You may call this method by using the name of the class alone--you don't need to invoke it on an instance.
 
 
 
To declare a method as static, decorate it with <i>@staticmethod</i>, as shown below for method <i>two</i>:
 
 
 
<source lang="python">
 
class Numbers:
 
 
 
  def one(self):
 
    return 1
 
 
 
  @staticmethod
 
  def two():
 
    return 2
 
</source>
 
 
 
Now, to invoke these methods, notice how <i>two</i> doesn't need to be invoked on an instance (we merely prepend the class name), but <i>one</i> does:
 
 
 
<source lang="python">
 
print Numbers.two()
 
 
 
that = Numbers()
 
print that.one()
 
</source>
 
 
 
Why would you want to use a static method? It is useful to keep the namespace tidy, to avoid name collisions.
 
 
 
=== Creating multi-dimensional native java arrays ===
 
 
 
Suppose you want to create a one-dimensional double array, the equivalent of '''double[]''' in java. This is what you would do:
 
 
 
<source lang="python">
 
from jarray import array
 
 
 
data = [1.0, 2.0, 3.0, 4.0]
 
 
 
arr = array(data, 'd')
 
</source>
 
 
 
Other accepted primitive array types are:
 
z boolean
 
c char
 
b byte
 
h short
 
i int
 
l long
 
f float
 
d double
 
 
 
But now suppose you want a two-dimensional double array, the equivalent of '''double[][]''' in java. How to do that? Here's how:
 
 
 
<source lang="python">
 
from jarray import array
 
 
 
data = [[1.0, 2.0], [3.0, 4.0]]
 
twoDimArr = array(data, Class.forName('[D'))
 
</source>
 
 
 
Essentially, what we did is to give the function '''array''' the argument '''class of a one-dimensional double array''', so that it will create an array of that--hence a two-dimensional double array.
 
 
 
For a three-dimensional array in jython, you'd just add another '''[''' (square bracket) to the class name:
 
 
 
<source lang="python">
 
from jarray import array
 
 
 
data = [[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]]
 
threeDimArr = array(data, Class.forName('[[D'))
 
</source>
 
 
 
See the [http://www.jython.org/archive/21/docs/jarray.html jarray documentation].
 
 
 
To create primitive arrays of any class, pass along the class itself. For example, a native array of '''String''':
 
 
 
<source lang="python">
 
from jarray import array
 
 
 
texts = ["one", "two", "three"]
 
 
 
strings = array(texts, String)
 
</source>
 
 
 
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):
 
 
 
<source lang="python">
 
from jarray import zeros
 
 
 
twoDimArr = zeros(5, Class.forName("[D"))
 
 
 
print twoDimArr
 
# prints: array([D, [None, None, None, None, None])
 
 
 
# fill each slot with a new array of length 3:
 
for i in range(len(twoDimArr)):
 
  twoDimArr[i] = zeros(3, 'd')
 
 
 
print twoDimArr
 
#prints: array([D, [array('d', [0.0, 0.0, 0.0]), array('d', [0.0, 0.0, 0.0]),
 
# array('d', [0.0, 0.0, 0.0]), array('d', [0.0, 0.0, 0.0]), array('d', [0.0, 0.0, 0.0])])
 
</source>
 
 
 
 
 
=== 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 <i>float[]</i> pixels array of the current image is iterated to compute the mean intensity:
 
 
 
<source lang="python">
 
from fiji.scripting import Weaver
 
from ij import IJ
 
from java.lang import Double
 
 
 
# The currently open image
 
imp = IJ.getImage()
 
 
 
fp = imp.getProcessor().convertToFloat()
 
pixels = fp.getPixels()  # a float array, float[]
 
w = Weaver.inline(
 
    """
 
    double sum = 0;
 
    for (int i=0; i<pixels.length; i++) {
 
        sum += pixels[i];
 
    }
 
    return sum / pixels.length;
 
    """,
 
    {"pixels" : pixels}, Double)
 
 
 
mean = w.call()
 
 
 
print mean
 
</source>
 
 
 
The above is trivial and it is meant only as an example (there are better ways to get the mean value, such as via <i>imp.getStatistics()</i>. Notice that the <i>Weaver.inline</i> function takes three arguments: the java code to inline, the map of bindings, and the return type. In the example, we pass only the <i>float[]</i> pixels array, and define <i>Double</i> 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 <i>inline</i> (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):
 
 
 
<source lang="python">
 
from fiji.scripting import Weaver
 
 
 
# The currently open image, an 8-bit stack
 
imp = IJ.openImage("http://imagej.net/images/bat-cochlea-volume.zip")
 
 
 
slices = [None, None]
 
 
 
w = Weaver.inline(
 
    """
 
    byte[] pix1 = (byte[]) slices.get(0);
 
    byte[] pix2 = (byte[]) slices.get(1);
 
 
 
    byte[] xor = new byte[pix1.length];
 
    for (int i=0; i<pix1.length; i++) {
 
        xor[i] = (byte)(pix1[i] ^ pix2[i]);
 
    }
 
    return xor;
 
    """,
 
    {"slices" : slices})
 
 
 
stack = imp.getStack()
 
stackXOR = ImageStack(stack.width, stack.height)
 
 
 
for i in range(2, imp.getNSlices()+1):
 
  # Put the pixel arrays into the pre-made list
 
  slices[0] = stack.getPixels(i-1)
 
  slices[1] = stack.getPixels(i)
 
  # Invoke native code
 
  stackXOR.addSlice( str(i-1), w.call() )
 
 
 
ImagePlus("XORed stack", stackXOR).show()
 
</source>
 
 
 
Of course the Weaver is a java library, and may be used from any scripting language such as [[Javascript Scripting|Javascript]], [[JRuby Scripting|JRuby]], and [[:Category:Scripting|others]].
 
 
 
All the above is inspired by the [http://www.scipy.org/Cookbook/Weave Scientific Python Weaver, or scipy Weaver], which inlines C code inside a python file.
 
 
 
=== 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:
 
 
 
<source lang="python">
 
import os, sys
 
from ij import IJ
 
 
 
# Expecting one argument: the file path
 
if len(sys.argv) < 2:
 
  print "Usage: ./fiji-linux64 <script-name> <file-path>"
 
  sys.exit(1)
 
 
 
filepath = sys.argv[1]
 
 
 
# Check if the file exists
 
if not os.path.exists(filepath):
 
  print "File does not exist at path:", filepath
 
  sys.exit(1)
 
 
 
# Open the image
 
imp = IJ.openImage(filepath)
 
print "Processing:", imp.title
 
 
 
# Do some processing ...
 
 
 
</source>
 
 
 
IMPORTANT: notice that, when executing scripts from the command line, there is no auto-importing of common imports. So above we <b>must</b> declare "from ij import IJ" to import the namespace <i>IJ</i> with all the static utility functions such as <i>openImage</i>.
 
 
 
=== Catching errors from a running macro ===
 
 
 
ImageJ exits with zero even when it fails (see [https://github.com/imagej/imagej/issues/61 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 <code>runMacroCode</code>, which will return the string <code>[aborted]</code> in case of [http://jenkins.imagej.net/job/ImageJ1-javadoc/javadoc/ij/plugin/Macro_Runner.html#runMacro%28java.lang.String,%20java.lang.String%29 failure]:
 
 
 
<source lang="python">
 
from ij import IJ
 
import sys
 
 
if not len (sys.argv) > 1:
 
  raise TypeError ("No macro file argument")
 
 
   
 
   
status = IJ.runMacroFile(sys.argv[1])
+
# Now you can import $FIJI_ROOT/bin/myModule.py
if status == '[aborted]':
+
import myModule
  raise StandardError ("Macro execution failed")
 
 
 
sys.exit (0)
 
 
</source>
 
</source>
  
Of course, if your macro happens to return <code>[aborted]</code> for success, you're out of luck ;)
+
The function <code>getProperty()</code> accepts many more strings. A list can be found at [https://docs.oracle.com/javase/tutorial/essential/environment/sysprop.html The Java Tutorials].
  
=== Running 3rd party java libraries ===
+
== Self written Jython packages for ImageJ ==
  
It is possible to run external java programs within Jython. To make them available, just copy the corresponding jar file into Fiji's plugins folder. To import the respective java classes, just do
+
On the way to perfectly organize Jython code, [https://docs.python.org/2/tutorial/modules.html#packages packages] are the next step. In Jython, folders that contain modules are made packages by adding the file <code>__init__.py</code>. This file can be empty. An folder structure can look like this:
 
 
<source lang="python">
 
import name.of.external.java.library as foo
 
</source>  
 
 
 
If the class names of the external package are unknown, one possibility is to inspect the jar file manually. On Linux and Mac OS systems just do on the command line
 
  
 
<source>
 
<source>
 
+
Imagej.app/jars/Lib/
jar tvf <library>.jar
+
-- myModule.py
 
+
-- myPackage/
</source>
+
   -- __init__.py
 
+
   -- mathTools.py
This will print the jar contents as plain text, look for entries ending with ".class". For the implementation of json in jython, provided at jyson.xhaus.com, the output looks like:
+
   -- customFilters.py
 
+
   -- fftTools.py
<source>
+
-- myPackage2/
 
+
  -- __init__.py
jar tvf jyson-1.0.2.jar
+
  -- mathTools.py
    0 Sat Mar 17 14:06:40 CET 2012 META-INF/
+
  -- stackProcessing.py
   106 Sat Mar 17 14:06:38 CET 2012 META-INF/MANIFEST.MF
 
    0 Sat Mar 17 14:06:40 CET 2012 com/
 
    0 Sat Mar 17 14:06:40 CET 2012 com/xhaus/
 
    0 Sat Mar 17 14:06:40 CET 2012 com/xhaus/jyson/
 
   174 Sat Mar 17 14:06:40 CET 2012 com/xhaus/jyson/JSONDecodeError.class
 
   174 Sat Mar 17 14:06:40 CET 2012 com/xhaus/jyson/JSONEncodeError.class
 
   162 Sat Mar 17 14:06:40 CET 2012 com/xhaus/jyson/JSONError.class
 
  1650 Sat Mar 17 14:06:40 CET 2012 com/xhaus/jyson/JysonCodec.class
 
  6350 Sat Mar 17 14:06:40 CET 2012 com/xhaus/jyson/JysonDecoder.class
 
  3899 Sat Mar 17 14:06:40 CET 2012 com/xhaus/jyson/JysonEncoder.class
 
 
</source>
 
</source>
  
The JysonCodec.class is the toplevel class, so to import this library include the following line in your jython script:
+
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:
 
 
<source lang="python">
 
import com.xhaus.jyson.JysonCodec as jyson
 
</source>
 
 
 
[[Category:Scripting]]
 
[[Category:Jython]]
 
 
 
== 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 "'{{bc | Plugins | Scripting | Refresh Jython Scripts}}'" or "'{{bc | 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 {{bc | 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 {{bc | File | Export as .jar}}.
 
  
Alternatively -- or if you want to bundle multiple scripts -- you can do it the manual way:
+
<source lang='python'>
 +
# Import the single module using the default name:
 +
import myModule
  
The whole idea is to be able to distribute an entire collection of scripts in a single .jar file, for best convenience.
+
# Import mathTools from the first package
 +
import myPackage.mathTools
 +
# Use a function from the imported module
 +
myPackage.mathTools.aFunction()
  
In this example, we create two jython scripts that we want to distribute in a .jar file as plugins:
+
# Import mathTools from the second package
 +
from myPackage2 import mathTools
 +
# Use a function from the imported module without prefixing the package
 +
mathTools.aFunction()
  
The <i>printer.py</i> script:
+
# Import customFilters from the first package and rename it
 +
from myPackage import customFilters as filters
 +
# Use a function from customFilters.py
 +
filters.aFunction()
  
<source lang="python">
+
# Importing all module from a package
IJ.log("Print this to the log window")
+
from myPackage2 import *
 +
# The next line will fail
 +
stackProcessing.aFunction()
 
</source>
 
</source>
  
... and the <i>create_new_image.py</i> script:
+
The reason for the last import to fail is the empty <code>__init__.py</code>. We have to define which modules of the package are imported when using <code>import *</code>. This is done by setting the variable <code>__all__</code> at <code>__init__.py</code>. For <code>myPackage2</code> this line of code is needed:
  
<source lang="python">
+
<source lang='python'>
ip = ByteProcessor(400, 400)
+
__all__ = ["mathTools", "stackProcessing"]
imp = ImagePlus("New", ip)
 
ip.setRoi(OvalRoi(100, 100, 200, 200))
 
ip.setValue(255)
 
ip.fill(ip.getMask())
 
imp.show()
 
 
</source>
 
</source>
  
Place both scripts under a folder named <i>scripts/</i> .
+
Besides setting this variable, the file can contain normal Jython code that is executed on import.
  
You will need a tiny .java file specifying a launcher PlugIn, such as:
+
== Bundle packages in a JAR file ==
  
<source lang="java">
+
An interesting feature of Jython is to search for packages and modules inside of [https://en.wikipedia.org/wiki/JAR_(file_format) JAR files]. The folder structure from the last section can be modified by packing everything into a single <code>myPackages.jar</code>. The name of the JAR file doesn't matter. All imports work the same as explained before.
package my;
 
import ij.plugin.PlugIn;
 
import Jython.Refresh_Jython_Scripts;
 
  
public class Jython_Launcher implements PlugIn {
+
<source>
    public void run(String arg) {
+
Imagej.app/jars/Lib/
        new Refresh_Jython_Scripts().runScript(getClass().getResourceAsStream(arg));
+
-- myPackages.jar
    }
+
  -- myModule.py
}
+
  -- myPackage/
 +
      -- __init__.py
 +
      -- mathTools.py
 +
      -- customFilters.py
 +
      -- fftTools.py
 +
  -- myPackage2/
 +
      -- __init__.py
 +
      -- mathTools.py
 +
      -- stackProcessing.py
 
</source>
 
</source>
  
Notice we place the above file under directory <i>my/</i>, packaged.
+
The advantage of this approach is that you can share your packages easily. For example you can upload the JAR file to an [[Update_Sites|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.
To compile it:
 
 
 
$ javac -classpath .:ij.jar:../jars/fiji-scripting.jar:../plugins/Jython_Interpreter.jar my/Jython_Launcher.java
 
 
 
(check that the path to the three jars that you need is correct!)
 
 
 
 
 
Then we define the plugins.config file:
 
 
 
Plugins>My Scripts, "Print to log window", my.Jython_Launcher("/scripts/printer.py")
 
Plugins>My Scripts, "Create image with a white circle", my.Jython_Launcher("/scripts/create_new_image.py")
 
 
 
Finally, we put all files in a .jar file:
 
 
 
$ jar cf my_jython_scripts.jar plugins.config my/Jython_Launcher.class scripts/*py
 
 
 
Then, drop the jar file into fiji/plugins/ folder and run "Help - Update Menus", or restart fiji. Your scripts will appear under Plugins - My Scripts.
 
  
For clarity, this is a summary of the files in the folder:
+
'''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.  
my/Jython_Launcher.java
 
my/Jython_Launcher.class
 
scripts/printer.py
 
scripts/create_new_image.py
 
plugins.config
 
  
 +
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 [http://forum.imagej.net/t/using-self-written-jython-modules-in-imagej/2280 here]. 
 +
 
 +
=== Using maven to build packages ===
  
Notice, though, that <b>you don't need to do the .jar packaging at all</b>. Just place the python scripts directly under fiji/plugins/My Scripts/ and they will appear in the menus as regular plugins.
+
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.
  
= Jython examples in Fiji =
+
At GitHub you will find an [https://github.com/m-entrup/imagej-jython-package example project] that you can use as a template. Just run <code>mvn package</code> and maven will generate a JAR file at the <code>target</code> directory.
  
*{{GitHub|repo=fiji|path=plugins/Examples/Find_Dimension_of_Raw_Image.py|label=Find Dimension of Raw Image}}
+
== Links ==
*{{GitHub|repo=fiji|path=plugins/Examples/Edit_LUT_As_Text.py|label=Edit LUT As Text}}
 
*{{GitHub|repo=fiji|path=plugins/Examples/Delayed_Snapshot.py|label=Delayed Snapshot}}
 
*{{GitHub|repo=fiji|path=plugins/Examples/Command_Launchers/Command_Launcher_Python.py|label=Command Launcher GUI}}
 
*{{GitHub|repo=fiji|path=plugins/Examples/list_all_threads.py|label=List all threads}}
 
*{{GitHub|repo=fiji|path=plugins/Examples/chess_.py|label=Chess}}
 
  
*{{GitHub|repo=fiji|path=plugins/Examples/TrakEM2_Example_Scripts/extract_stack_under_arealist.py|label=Extract stack under AreaList}} in TrakEM2.
+
* [[Jython_Scripting_Examples|Jython Scripting Examples]]
*{{GitHub|repo=fiji|path=plugins/Examples/TrakEM2_Example_Scripts/T2_set_all_transforms_to_identity.py|label=Set all transforms to identity}} for TrakEM2 objects.
+
* [[ImageJ2_Python_Scripts|ImageJ2 Python Scripts]]
*{{GitHub|repo=fiji|path=plugins/Examples/TrakEM2_Example_Scripts/T2_Select_All.py|label=Select All}} objects in TrakEM2.
+
* [https://www.ini.uzh.ch/~acardona/fiji-tutorial/ A Fiji Scripting Tutorial by Albert Cardona]
*{{GitHub|repo=fiji|path=plugins/Examples/TrakEM2_Example_Scripts/Measure_AreaLists.py|label=Measure AreaList}} in TrakEM2.
+
* [http://wiki.cmci.info/documents/120206pyip_cooking/python_imagej_cookbook Jython scripting cookbook]
  
= See also =
+
== References ==
*Albert Cardona's crash course in [http://www.ini.uzh.ch/~acardona/fiji-tutorial/index.html Jython scripting with Fiji].
+
<references />
*Jython for [[TrakEM2 Scripting]].
 

Revision as of 04:03, 11 December 2018

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
Parameters
Basics of script writing
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 in functionality. One can use the standard library, but it's not possible to install additional Python modules. Moreover a growing number of projects build on Python3 which is not fully compatible with Python2 Jython is based on. If you want to start learning Python it's recommended to learn Python3.x instead of Python2.

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.

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

'''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:
# https://docs.python.org/2/library/__future__.html
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:
# https://imagej.nih.gov/ij/developer/api/allclasses-noframe.html
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:
        # http://www.learnpython.org/en/String_Formatting
        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

#@ 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
# http://imagej.net/Script_parameters

# The module __future__ contains some useful functions:
# https://docs.python.org/2/library/__future__.html
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.
    # http://stackoverflow.com/questions/8421337/rotating-a-two-dimensional-array-in-python
    # 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

#@ 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 module 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.

Let's show one example with the ImageJ package and the class RoiManager. According to the javadoc the RoiManager class resides in ij.plugin.frame. Therefore the code will look like :

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 :

#@ 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.

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.

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.

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:

from sys import path
print(path)

When running this code the result is an output like

['/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, Jython will use the class file instead of the py file. When modifying the module, it necessary to restart ImageJ/Fiji to use the modified one. A work around is the following code (found at stackoverflow) that will force Jython to recompile all modules:

# 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:

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. In Jython, folders that contain modules are made packages by adding the file __init__.py. This file can be empty. An folder structure can look like this:

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:

# 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__ at __init__.py. For myPackage2 this line of code is needed:

__all__ = ["mathTools", "stackProcessing"]

Besides setting this variable, the file can contain normal Jython code that is executed on import.

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.

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