Difference between revisions of "Jython Scripting"

(Introduction: fixing a broken link)
(Image selection using the GenericDialog class: adding the source code and a list of links to useful documentation)
Line 33: Line 33:
 
=== Image selection using the GenericDialog class ===
 
=== Image selection using the GenericDialog class ===
  
coming soon...
+
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.
 +
 
 +
The following list links to documentation of the used Python features:
 +
* [https://docs.python.org/2/library/__future__.html Future statement definitions]
 +
* [https://docs.python.org/2/library/functions.html Built-in Functions]
 +
* [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]
 +
 
 +
 
 +
<source lang='python'>
 +
# 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__ == '__main__':
 +
run_script()
 +
</source>
  
 
=== Using ImageJ-Ops ===
 
=== Using ImageJ-Ops ===

Revision as of 11:02, 30 August 2016

Remark

This page is in a rewrite process that will take some time.

You can view the original content of this page at Jython Scripting Examples.

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

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 Python 3 which is not fully compatible with Python 2 Jython is based on. If you want to start learning Python it's recommended to learn Python 3.x instead of Python 2.

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 Python. This purpose is much better fulfilled by the documentation of Python 2. The focus of this page is to show how features of the Python language can be useful for ImageJ scripting.

That is why examples are used that are fully functional. Just copy the code to the Script Editor and try them by yourself.

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:


# 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__ == '__main__':
	run_script()

Using ImageJ-Ops

coming soon...

Self written Jython modules for ImageJ

coming soon...

References

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