Skip to content, Skip to search

Changes

Jython Scripting

2,981 bytes removed, 04:03, 11 December 2018
m
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>[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]].
<source lang{{GitHubEmbed|org='python'># The module __future__ contains some useful functions:# https://docs.python.org/2/library/__future__.htmlfrom __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.htmlfrom ij import IJ, WindowManagerfrom 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 |repo= 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), "8imagej-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 scripting|path= GenericDialog(title) # The build in function enumerate() returns two values: # The index and the value stored in the tuplesrc/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:main/resources/www.learnpython.orgscript_templates/enTutorials/String_Formatting IJWiki_Jython_Tutorial_1.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__ == '__main__': run_script()</source>py}}
=== Using Scripting Parameters ===
{{GitHubEmbed|org=imagej|repo=imagej-scripting|path=src/main/resources/script_templates/Tutorials/Wiki_Jython_Tutorial_2.py}} === 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: * [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]  {{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.  === ImageJ and Fiji API === The following API documentation lists all available module and functions :* [http://javadoc.scijava.org/ImageJ1/ ImageJ]* [http://javadoc.scijava.org/Fiji/ 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 [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'">from ij.plugin.frame import RoiManagerRM = RoiManager() # we create an instance of the RoiManager classrm = RM.getRoiManager() # @String"activate" the RoiManager otherwise it can behave strangely </source> === Using openCV in Jython ===It is even possible to use most of opencv functionalities within Jython/Fiji. There are several options (valuesee 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.  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.  ==== Matrices ===='Please set The first thing to know about OpenCV is that most functions work with an OpenCV matrix object. Fortunately, the IJ-OpenCV project provides some parametersconverters : <source lang="python">#@ ImagePlus ImP from ijopencv.ij import ImagePlusMatConverterfrom ijopencv.', visibilityopencv import MatImagePlusConverterfrom ij import ImagePlus # Convert ImagePlus (actually the contained ImageProcessor) to Matrix objectimp2mat = ImagePlusMatConverter()ImMat ='MESSAGE'imp2mat.toMat(imp.getProcessor()) messageprint ImMat # @ShortConvert Matrix object to ImageProcessormat2ip = MatImagePlusConverter(label)NewIP ='Image size'mat2ip.toImageProcessor(ImMat)NewImp = ImagePlus("Matrix converted back to ImagePlus", NewIP)print NewImP</source> Such kind of converter is also available for PointRoi to opencv keypoints... Now to use opencv function, valuewe use the [http://bytedeco.org/javacpp-presets/opencv/apidocs/ JavaCPP API] that contains almost all functions of opencv. <source lang=512"python">from org.bytedeco.javacpp.opencv_core import Mat, CvMat, minvconcat ## Typical matrices ## # Identity Matrix of size (3x3) and type 8-bitId =128Mat().eye(3, max3,0).asMat()print Idprint CvMat(Id) # handy to visualise the matrix # Matrix of ones (3x3) One =2048Mat().ones(3,3, stepSize0).asMat() # Matrix of zeros (3x3) Zero =128Mat().zeros(3, style="slider"3,0).asMat() img_size # @DoubleCustom Matrices# 1D-Matrix can be initialize from a list# For 2D (labelor more) we have to concatenate 1D-Matrices Row1 ='Image amplitude'Mat([1,2,3,4,5]) # 1D matrix Row2 = Mat([6,7,8,9, value10]) TwoColumn =100Mat() amplitude # initialise outputvconcat(Col1, Col2, TwoColumn) # @Shortoutput stored in TwoColumnprint CvMat(label=TwoColumn)</source> {{Warning | The <code>org.bytedeco.javacpp.opencv_core.Mat</code> object is different than the <code>org.opencv.core.Mat</code> !! They don'Spacingt 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, valuewhich is the most approriate in Fiji/Jython  ==== Scalar ====16In addition to Matrices, opencv allows to use Scalar objectsA scalar is a 4 item element (v0, v1, v2, minv3).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 parametersNumber =8Scalar(5.0)Number = Scalar(float(5)) spacingprint Number
# The parameters in front of this comment are populated before the script runs.Using an integer as parameter has a different meaningEmpty = Scalar(5) # Details on Script parameters can be found atThis initiate an empty Scalar object of size 5# http://imagej.net/Script_parametersprint Empty
# The module __future__ contains some useful functions:Alternatively one can set the other values of the Scalar# https://docs.python.org/Complex = Scalar(1,2,3,4)print Complex</library/__future__.htmlfrom __future__ import with_statement, divisionsource>
# 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.==== Operations ====# This It is only possible if the main code is encapsulated into a function.# The main function has to be called at the end of the scriptperform some operations between matrices, or between Scalar and matrices.
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'] <source lang= "Resultpython"> # This is a 2D list: list[number of images][pixels per image] from org. pixels = [impbytedeco.getProcessor()javacpp.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 opencv_core import FloatProcessor return ImagePlus(kwargs['title'], FloatProcessor(img_sizeScalar, img_sizeMat, result))subtract
def split_listA = Mat(alist[1, wanted_parts=12,3,4,5]): """Split a list to the given number of parts.""" length B = lenMat(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,2,-3,-4,0]. # '//' 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 outputNumber =[Point Selection] exclude"); # Enlarging the point selectins from Find Maxima. IJ.runScalar(fft, "Enlarge...", "enlarge=2"); # Inverting the selection. IJ10.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
## Number - B ( B-Number is also possible)
Expr = subtract(Number,B)
print CvMat(Expr.asMat())
# 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.A - Bif __name__ Out == '__main__':Mat() subtract(A,B,Out) run_scriptprint CvMat(Out)
</source>
<source lang='python'>
['/home/michael/Software/ImageJ.app/jars/Lib', '/home/michael/Software/ImageJ.app/jars/jython-shaded-2.57.30.jar/Lib', '__classpath__', '__pyclasspath__/']
</source>
<source>
Imagej.app/jars/libLib/
-- myModule.py
-- myPackage/
<source>
Imagej.app/jars/libLib/
-- myPackages.jar
-- myModule.py
</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 [http://forum.imagej.net/t/using-self-written-jython-modules-in-imagej/2280 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 [https://github.com/m-entrup/imagej-jython-package example project ] that you can use as a template. Just run <code>mvn jar:jarpackage</code> and maven will generate a JAR file at the <code>target</code> directory.
== Links ==
* [[Jython_Scripting_Examples|Jython Scripting Examples]]
* [[ImageJ2_Python_Scripts|ImageJ2 Python Scripts]]
* [https://www.ini.uzh.ch/~acardona/fiji-tutorial/ A Fiji Scripting Tutorial by Albert Cardona]
* [http://wiki.cmci.info/documents/120206pyip_cooking/python_imagej_cookbook Jython scripting cookbook]
== References ==
<references />
Emailconfirmed
88
edits