BIOP / ijp-LaRoMe

some useful function to get Label from ROIs and vice versa , and more!
BSD 3-Clause "New" or "Revised" License
12 stars 4 forks source link

Interfacing with LaRoMe via FIJI Jython Interpreter #15

Closed jaredbrewer closed 2 years ago

jaredbrewer commented 2 years ago

I noticed the previous issue that has since been closed but was not sure what the actual resolution to it was. There may be some level of my misunderstanding about this, so I apologize if this is a silly question/issue.

My goal is to run Labels2Rois through the Jython interpreter.

This code runs without issue on the currently open image: IJ.run("Label image to ROIs")

However, when I try to do something like this:

import ch.epfl.biop.ij2command.Labels2Rois
from ij import IJ, ImagePlus
from ij.plugin.frame import RoiManager

imp = IJ.openImage([path to image])
IJ.run(imp, "Label image to ROIs")

It fails because it does not understand the imp argument, it only works on the currently open image. I am trying to run this in a largely automated, headless fashion.

I noted on the previous discussion you referenced the use of Groovy scripting and I have made an attempt at dissecting this answer and translating the components to Python, but have not found the right combination of things.

A preliminary question that might clear things up:

I may be able to go through and translate some of the source to Jython but would like to avoid this if possible.

Thank you for your help and this fantastic tool. It is exactly what I am looking for and works great, I am just stumbling over some of these challenges in making this work in a programmatic fashion.

NicoKiaru commented 2 years ago

Hello @jaredbrewer,

There are several ways to call a plugin in ImageJ. Essentially IJ.run("Name of the plugin") usually do not work headless. So you need to call in a different manner. Whether it is called with groovy or Jython do not change a lot, except for some syntax differences.

In LaRoMe, a plugin is actually a java class which implements Command. A Command can declare inputs and outputs with the help of an annotation @Parameter.

Because it is command, it is possible to execute the plugin with the help of a service, here a CommandService. You can get one in a Jython script by using a script parameter in your script:

#@CommandService cs

with the command service, you can execute any plugin which implements command (for instance Labels2Rois) by running it this way (groovy):

import ch.epfl.biop.ij2command.Labels2Rois
cs.run(Labels2Rois, true, "rm", myRoiManager, "imp", myImage).get()

If you need to get outputs, see the original issue where getouput is used. in Labels2Rois, the roi manager you give in to the command is mutated, so the output is given in input.

For an example of the syntax with command service by using Jython, see here for instance.

Let me know if this is helpful or if you are still stuck.


To answer more directly your question. Here's how your Jython script could look like to run headless:

#@CommandService cs

from ch.epfl.biop.ij2command import Labels2Rois
from ij import IJ, ImagePlus
from ij.plugin.frame import RoiManager

roiManager = RoiManager() # instantiate a roi manager and show it
# roiManager = RoiManager(False) # instantiate a roi manager and do not show it

image = IJ.openImage([path to image])
cs.run(Labels2Rois, True, "imp", image, "rm", roiManager).get()
jaredbrewer commented 2 years ago

Hi Nicolas,

This is extremely helpful and very thorough and has proven to be very helpful in both this and other contexts and I am certain will be very helpful for others. I was unaware of the #@CommandService cs notation due to my coming from a pure Python background.

My image analysis jumps around a lot and I the interfacing to be a little bit complicated in my particular contexts, so shortly after I posted this I ported over the "Labels to ROIs" functionality into Jython and have been using it for some time. I just now got the free time to do the same to the other functionality built into LaRoMe. The idea was to make the exercise of the command more pythonic in nature and improve iterability.

https://github.com/jaredbrewer/py-LaRoMe

I haven't finished documenting it yet, but an example use for labelsToROIs would be as follows (after addition to the appropriate plugins path, typically /Fiji.app/jars/Lib):

from ij import IJ, ImagePlus
from ij.plugin.frame import RoiManager
from labelsToROIs import labelsToROIs

path = "/Users/user/Documents/test.tif"
rm = RoiManager.getRoiManager()

imp = IJ.openImage(path)
labelsToROIs(imp)

Hopefully it is clear how this could be easily iterated over an entire directory.

rc = rm.getRoisAsArray()
rm.reset()

Then some downstream operation using the ROIs generated - apply to another image, etc. Due to the way ImageJ behaves, I generally store the ROIs in an array and then add them back when I am ready to use them - also allows for multiple sets to reside in memory

for r in rc:
    rm.addRoi(r)

Anyway, I am greatly indebted to your excellent ground work and it has been immensely beneficial in some of the image quantitation I have been attempting. I hope that my efforts are a compliment and a complement to that great existing work.