Skip to content, Skip to search


Jython Scripting

9,886 bytes added, 04:03, 11 December 2018
Links: Add aditional link
{{NoticeLearn |This page is in a rewrite process that will take some time.<br>You can view the original content of this page at [[Jython Scripting Examples]].languages}}{{Scripting}}== Introduction ==
Jython is an implementation of the Python programming language designed to run on the Java platform. <ref>[ Wikipedia entry on Jython]. Accessed: 2016-08-30</ref> In ImageJ Jython is one of several [[Scripting#Supported_languages|supported languages]].
== When to use Jython ==
All scripting language supported by ImageJ can be used to access the [https 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:
=== 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 Python 3 Python3 which is not fully compatible with Python 2 Python2 Jython is based on. If you want to start learning Python it's recommended to learn Python 3Python3.x instead of Python 2Python2.
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 ==
{{Notice|For an introduction in ImageJ scripting visit the page [[Scripting_basics|Scripting basics]].}}
=== 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 Python 2Python2]. 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 [[Using_the_Script_Editor|Script Editor]] and try them by yourself. Extensive in-line documentation is used to explain the implementation.
=== Image selection using the GenericDialog class ===
* [ Purpose of the single underscore “_” variable]
=== 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|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.
This list links to the documentation of Python features that are introduced with this example:
* [ The zip() function]
* [ Rotating a two-dimensional array]
* [ Lambda expressions]
=== A batch opener using <code>os.walk()</code> ===
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>.
To read more about the used features, the following list provides links to additional information:
* [ The walk() function]
* [ The documentation of os.path]
* [ The listdir() function]
* [ Javadoc on IJ.openImage()]
* [ Testing the type of an object using isinstance()]
* [ Identifying the type of an object using type()]
* [ Using continue to control a loop]
* [ Truth Value Testing]
== 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 :
* [ ImageJ]
* [ Fiji]
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 <code>ij.plugin.frame</code>. Therefore the code will look like :
<source lang="python">
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 :
<source lang="python">
#@ 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.
<source lang="python">
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)
{{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>.}}
Similarly there is some apparent redudancy for the function in the javacpp API.
ex : Transform exists in 3 different places :
* <code>org.opencv.core.Core.transform</code>
This one takes <code>org.opencv.core.Mat</code> as input. It is currently challenging to have such object in Fiji.
* <code>org.bytedeco.javacpp.opencv_core.cvTransform</code>
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.
* <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
==== 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">
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.
<source lang="python">
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()
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:
<source lang='python'>
from sys import path
When running this code the result is an output like
<source lang='python'>
['/home/michael/Software/', '/home/michael/Software/', '__classpath__', '__pyclasspath__/']
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.
When a module is imported for the first time, Jython will compile it to Java code. If there is a module named <code></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 [ stackoverflow]) that will force Jython to recompile all modules:
<source lang='python'>
# Use this to recompile Jython modules to class files.
from sys import modules
# Imports of Jython modules are placed below:
import myModule
=== Adding a custom directory ===
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>:
<source lang='python'>
# The module __future__ contains some useful functions:from sys import path# https://docsfrom java.pythonlang.orgSystem import getProperty # extend the search path by $FIJI_ROOT/2bin/library/__future__# 'fiji.htmlfrom __future__ import with_statementdir' works for plain ImageJ, divisiontoo.# This imports the function random from the module randompath.from random import random# Next we import Java Classes into Jythonappend(getProperty('fiji.dir') + '/bin')# This is how we an alternative can acces be the ImageJ API:users home directory# https://imagejpath.nihappend(getProperty('user.govhome') + '/ijJythonModules') # Now you can import $FIJI_ROOT/developerbin/api/allclasses-noframemyModule.htmlpyfrom ij import IJ, WindowManagermyModulefrom ij.gui import GenericDialog</source>
# A The function is created with the def keyword.# This function does not need any parameters.def create_test_image<code>getProperty()</code> accepts many more strings. A list can be found at [https: # Python uses indentation to create code blocks// The Java Tutorials].
# 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) Self written Jython packages 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., "Add...", "value=" + str(counts)) imp.setRoi(offset_x , offset_y, box_width, box_height), "Add...", "value=" + str(counts)), "Select None", ""), "Add Specified Noise...", "standard=" + str(stdv)); # We don't want to confirm when closing one of the newly created images. imp.changes = False
# This function uses parameters.# A default value is given On the way to the third parameter.def create_selection_dialog(image_titlesperfectly organize Jython code, defaults, title='Select images for processing')[https: 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): html# for each loop we add a new choice element to packages packages] are the dialog. gdnext step.addChoice('Image_'+ str(index + 1)In Jython, image_titles, image_titles[default]) gdfolders that contain modules are made packages by adding the file <code>__init__.showDialog() if gdpy</code>.wasCanceled(): return None # This function returns a listfile can be empty. # _ 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]An folder structure can look like this:
# It's best practice to create a function that contains the code that is executed when running the script.<source># This enables us to stop the script by just calling run_script(): while WindowManager-- myModule.getImageCount() < 10:py create_test_image() -- myPackage/ image_titles = [WindowManager.getImage(id).getTitle() for id in WindowManager -- __init__.getIDList()]py # 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 -- fftTools.')py return-- myPackage2/ # We have to get the corresponding IMagePlus objects -- selected_imps = [WindowManager.getImage(id) for id in [WindowManager -- mathTools.getIDList()[index] for index in selected_indices]]py # The previous line can be split into 2 lines: # selected_ids = [WindowManager -- stackProcessing.getIDList()[index] for index in selected_indices]py # selected_imps = [WindowManager.getImage(id) for id in selected_ids] for imp in selected_imps: # Strings can be formated using the % operator: # http:/</ IJ.log('The image \'%s\' has been selected.' % imp.getTitle())source>
# If a Jython script is run, There are two packages and one module. The first package contains three modules and the variable __name__ second package contains two modules. We can import the string modules on different ways: <source lang='__main__python'># Import the single module using the default name:import myModule # Import mathTools from the first packageimport myPackage.mathTools# If Use a script is loaded function from the imported modulemyPackage.mathTools.aFunction() # Import mathTools from the second packagefrom myPackage2 import mathTools# Use a function from the imported module without prefixing the packagemathTools.aFunction() # Import customFilters from the first package and rename itfrom myPackage import customFilters as filters# Use a function from customFilters.pyfilters.aFunction() # Importing all module, __name__ has from a different value.packagefrom myPackage2 import *if __name__ == '__main__':# The next line will fail run_scriptstackProcessing.aFunction()
=== Using ImageJ-Ops ===The reason for the last import to fail is the empty <code></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></code>. For <code>myPackage2</code> this line of code is needed:
coming soon<source lang='python'>__all__ = ["mathTools", "stackProcessing"]</source> 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 <code>myPackages.jar</code>. The name of the JAR file doesn't matter. All imports work the same as explained before. <source> myPackages.jar -- -- myPackage/ -- -- -- -- -- myPackage2/ -- -- --</source> 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.  '''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 <code>mvn package</code> and maven will generate a JAR file at the <code>target</code> directory. == Self written Jython modules for ImageJ Links ==
coming soon* [[Jython_Scripting_Examples|Jython Scripting Examples]]* [[ImageJ2_Python_Scripts|ImageJ2 Python Scripts]]* [ A Fiji Scripting Tutorial by Albert Cardona]* [ Jython scripting cookbook]
== References ==
<references />