imagej / pyimagej

Use ImageJ from Python
https://pyimagej.readthedocs.io/
Other
474 stars 83 forks source link

Proper way of loading macro scripts #246

Closed jackyko1991 closed 1 year ago

jackyko1991 commented 1 year ago

Hi pyimagej developers,

I am trying to integrate TrackMate with my python image analysis workflow, and finding pyimagej is the optimal solution to the Fiji macro.

However after checking the documentation, we have to explicitly include the scripts in ij.py.run_macro function:

macro = """
#@ String name
#@ int age
#@ String city
#@output Object greeting
greeting = "Hello " + name + ". You are " + age + " years old, and live in " + city + "."
"""

args = {
    'name': 'Chuckles',
    'age': 13,
    'city': 'nowhere'
}

result = ij.py.run_macro(macro, args)
print(result.getOutput('greeting'))

or running with ij.py.run_script:

language_extension = 'ijm'
result_script = ij.py.run_script(language_extension, macro, args)
print(result_script.getOutput('greeting'))

In TrackMate I have prepared the macro in jython or groovy.

What is the proper extension for these two languages?

Can we load the script from ExampleScript_1.py or Run_TrackMate_Headless.groovy without pasting it inline?

Also if we have converted the image to ImagePlus format in pyimagej, can we pass the image to macro scripts through args? If so how can we call the passed image file in the the jython?

ctrueden commented 1 year ago

What is the proper extension for these two languages?

#@ ScriptService ss
ss.getLanguages().forEach { println("${it}: ${it.getExtensions()}") }
Groovy: [groovy]
BeanShell: [bsh]
Clojure: [clj]
ImageJ Macro: [ijm]
Java: [java]
JavaScript: [js]
Python: [py]
R: [R, r, S, s]
Ruby: [rb]
Scala: [scala]

Can we load the script from ExampleScript_1.py or Run_TrackMate_Headless.groovy without pasting it inline?

Yes, you can call it by file path using the Java-side method ij.script().run(File, boolean, Object[]). The ij.py.run_script Python-side function is just a wrapper around some ij.script().run(...) methods. You can convert your Python arguments to an Object[] using ij.py.jargs. If you want to block until script execution is complete, call get() on the Future returned by ij.script().run.

Also if we have converted the image to ImagePlus format in pyimagej, can we pass the image to macro scripts through args?

Yes, images passed to an ImageJ macro via SciJava script parameters are automatically converted to string image IDs. So you can do that, and then pass them to macro functions that support passing titles or IDs. (Images passed to any other script language such as Jython are not converted to strings, but rather passed as whatever object type you specified in the parameter declaration.)

If so how can we call the passed image file in the the jython?

A Jython script is not an ImageJ macro. ImageJ macro is a specific script language, Jython is a different script language. If you want to pass an ImagePlus object to a Jython script, you can write #@ ImagePlus imp as a script parameter. If you want to pass an image file to a Jython script, you can write #@ File imageFile and then pass this file to whatever is expecting a file path.

jackyko1991 commented 1 year ago

Finally I get things done in two approaches, using python-side and java-side call:

//test.ijm
#@ String name
#@ int age
#@ String city
#@ output Object greeting1
greeting = "Hello " + name + "."  + " You are " + age + " years old, and live in " + city + "."

On python side we need to load the script file but not parse the path

# define python side arguments
args = {
    'name': 'JK',
    'age': 99,
    'city': 'London'
}

# load the script file
with open('test.ijm')"r") as f:
    script_ijm = f.read()

# Run with ij.py.run_script() function
result = ij.py.run_script('ijm', script_ijm, args)
print(result.getOutput("greeting"))

On java side the file need to be handled by java File class object

# define python side arguments
args = {
    'name': 'JK',
    'age': 99,
    'city': 'London'
}

File = sj.jimport('java.io.File')
jfile = File('test.ijm')

# convert python side stuffs to java side
jargs = ij.py.jargs(args)
result_future = ij.script().run(jfile,True,jargs)

# get the result from java future, blocking will occur here
result = ij.py.from_java(result_future.get().getOutput("greeting"))
print(result)

Working on the Jython argument parsing as instructed here. Need to test if python side ImageJ1 imp or ImageJ2 dataset object can be directly parsed, or need to be bridged via ij.py.window_manager.setTempCurrentImage(imp) and retrieve the imp in jython through imp = WindowManager.getCurrentImage(). Perhaps this need to be run on interactive mode

ctrueden commented 1 year ago

@jackyko1991 Did you get everything working? If you are still having issues, please feel free to reopen this issue with details. :smile_cat: