Need help with groovy

imagej-ops
groovy
Tags: #<Tag:0x00007fa3048ad7d0> #<Tag:0x00007fa3048ad690>

#1

I am trying to make a trivial program to add 2 images together.
In the example is

// add a constant value to an image
ops.math().add(sinusoid, 13.0)

where sinusoid is a double type image, so that it makes sense to add 13.0

I took an ECT image which is an array of short integers. My groovy script is

// @ImageJ ij
// @OpService ops
// @CommandService cmd
// @UIService ui
ect1 = ij.io().open("/home/ilan/Documents/ect1/ECT1.dcm")
ui.show("ect", ect1)
ops.math().add(ect1, 20)

I am trying to add an integer to a short, but I understand that groovy is intelligent enough to not exceed a short integer, and 20 is very far away from the maximum value.
I get back

groovy.lang.MissingMethodException: No signature of method: net.imagej.ops.math.MathNamespace.add() is applicable for argument types: (net.imagej.DefaultDataset, java.lang.Integer) values: [ECT1.dcm, 20]
Possible solutions: add(int, int), add(net.imglib2.img.array.ArrayImg, int), add(net.imglib2.img.planar.PlanarImg, int), add(double, double), add(float, float), add(long, long)

I think it is complaining about my short but I’m not totally sure. I certainly want to keep the native value of the input image as short, so I don’t know what to do with the suggestions I am getting - presumably it wants me to convert to an integer (even if I don’t want to do so)?
Thanks,
Ilan


#2

The type matching in ImageJ Ops is a bit tricky when you try to use the typed ops signatures (ops.math().add()) with weakly-typed script languages.

The issue here is that your input etc1 is a DefaultDataset, and the number is treated as Integer by the Groovy interpreter.


From script languages such as Groovy, I’d recommend using the ops.run() method instead, taking the op name as a first input:

ops.run("math.add", in1, in2)

This allows the framework to look for converters for in1 and in2 that best match any available op signature.

The following Groovy script worked for me (using the special “.fake” file extension to generate a sample image):

#@ IOService io
#@ OpService ops

img = io.open("16bit-unsigned&pixelType=uint16&lengths=100,100&axes=X,Y.fake")

ops.run("math.add", img, 20 as short)

Note that I used the language-agnostic script parameter syntax (#@) here, as it is documented on the Script Parameters wiki page. The comment-based syntax that you used works as well, but is less readable in my opinion, and cannot be as easily transferred to other languages.


#3

Thanks Jan. I have no idea of any preferred format. Somewhere I saw the comment, so that is what I used. I like your #@ better as there is even a space to separate out the important part, the name. I will follow your convention.

This now works:

#@ ImageJ ij
#@ OpService ops
#@ CommandService cmd
#@ UIService ui
ect1 = ij.io().open("/home/ilan/Documents/ect1/ECT1.dcm")
ui.show("ect", ect1)
ops.run("math.add", ect1, 20)

I didn’t know exactly what to do with your special fake file, so I tried without it. I wanted it to pick up my explicit file and I failed to see where that came in, in what you wrote. It turns out the simple minded method worked. (I don’t know if it is “dangerous” in any way.)

First I tried “20 as short” as a clean method, but I wanted to see what would happen if I wouldn’t explicitly cast it. That too worked.

While I am asking questions I noticed there were 2 images displayed: my “ect” image and a “result” image. Are these the same object? Does ops.run(…) always display a result? Can I do something like myResult = ops.run(…) and then get properties out of myResult? Somehow I want to look at what I have just done. In java I would just put a break point in the debugger and do whatever I please. Scripts are a different world.

I don’t want to bother you to teach me all these new things. If you can give me a good reference, that is more than enough.

Thanks,
Ilan


#4

When you run a script, and that script does not declare any outputs via the script parameter syntax, the final computation in your script will be displayed automatically as result. In this case, the ops.run call is returning an image, which the script framework then displays.

You can avoid this behavior by either:

  1. Declaring any output parameter; e.g. #@output Object myOutputImage
  2. Ensuring the final line of the script is void or returns null; e.g. a simple null on its own line at the end

Yes you can.

As a starting point, try using println to output details of what you are interested in.

Fiji also ships with a simple object inspector, similar to that feature of an IDE’s debugger. Try this code:

import com.github.sbridges.objectinspector.Inspector
Inspector.inspect(myObjectOfInterest)

You can also combine Groovy with the debugger of your IDE. Either launch Fiji from your IDE (by cloning the fiji/fiji project and importing it), or else connect to a Fiji installation via remote debugging. Set a breakpoint in Java code, then write a Groovy script that triggers that code (directly or indirectly).

Depends what you are trying to learn. But you could check out the Groovy documentation, for example.


#5

Thanks for the help.
I want to make a function inside the script, to get beyond the trivial level.
My code which doesn’t yet work is

#@ ImageJ ij
#@ ImagePlus imp
#@ OpService ops
#@ CommandService cmd
#@ UIService ui
import com.github.sbridges.objectinspector.Inspector

def gcl = new GroovyClassLoader()
ect1 = ij.io().open("/home/ilan/Documents/ect1/ECT1.dcm")
def clazz1 = ImagePlus.class                                           
//meta = getMeta(1, ect1)
println clazz1
ui.show("ect", ect1)
//myRes = ops.run("math.add", ect1, 20)
//Inspector.inspect(myRes)

String getMeta(int slice, clazz1 img) {
	if( img1.getImage() == null) return null;
	String meta = img1.getStack().getSliceLabel(slice);
	// meta will be null for SPECT studies
	if (meta == null ) meta = (String) img1.getProperty("Info");
	return meta
}

ect1 is an ImagePlus object, and I want to try to work on it.
The line def clazz1 = ImagePlus.class tells me that ImagePlus doesn’t exist.
I’ve tried img.class with the same result. ij.class goes through but I don’t know how to get ImagePlus from ImageJ.
The other part is that ImagePlus doesn’t exist. If I could somehow use ect1, then I have an ImagePlus which does exist.

Ilan


#6

Is it necessary to open the image in the script? You could instead open the image (File > Import > Image…) before launching the script—then the #@ ImagePlus imp will be populated with the active image.

If you are certain you need to explicitly convert between IJ1 and IJ2 data structures, you can use ImageJFunctions.wrap. These materials may be helpful to learn more: https://github.com/maarzt/imagej-legacy-course

There are some things in your script that are unnecessary:

  • def gcl = new GroovyClassLoader() – not sure what this is for, but it’s not used
  • def clazz1 = ImagePlus.class – was this just for testing/understanding?
  • #@ ImageJ ij – that gateway object is just a convenience so that you can write e.g. ij.io() for the IOService instead of writing #@ IOService io as a parameter directly. I’d suggest deleting the gateway declaration in favor of specific services, so that your script only depends on what it is really using.

#7

Since I’m still feeling my way around, there are failed experiments still around.
I’m sure I will not need to open the file in the script.
I’m using the script editor to run the script, so I need to know how I can get a specific file/image in this setting. The script editor has a button to Run and that is what I am using.
How do I start the script so that it would pick up some specific image?
Later on I will clearly call the script from inside the program, but for now I am leaning.
Can I define some input parameters to a script which I can populate using the script editor, or some other method?
These may be dumb questions, but from where I sit, I’m still trying to get enough to work with.


#8

You open an image in the user interface, then you run your script (containing a #@ ImagePlus imp parameter) from the script editor and the variable imp will contain the currently open image.

Script parameters of numeric or text type, such as String, Integer or Double will pop up a dialog when you run the script from the editor.

Once you want to start doing this (calling your script from another script or plugin), you can use the run() method of ScriptService.


#9

I still need help with the basics. The current code is
#@ ImageJ ij
#@ ImagePlus imp
#@ OpService ops
#@ CommandService cmd
#@ UIService ui

//ect1 = ij.io().open("/home/ilan/Documents/ect1/ECT1.dcm")
def clazz1 = imp.class
println clazz1
//meta = getMeta(1, ect1)

/* String getMeta(int slice, clazz1 img1) {
if( img1.getImage() == null) return null;
String meta = img1.getStack().getSliceLabel(slice);
// meta will be null for SPECT studies
if (meta == null ) meta = (String) img1.getProperty(“Info”);
return meta
}*/

What I do now is open an image in Fiji then use the script editor to run my script.
I was pleased to see that it does indeed pick up the current image.
If I run the script with getMeta commented out, it runs:
Started myscript.groovy at Sun Jul 01 15:47:24 IDT 2018
class ij.ImagePlus

However if I include the getMeta, it gets confused:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Script9.groovy: 12: unable to resolve class clazz1
@ line 12, column 27.
String getMeta(int slice, clazz1 img1) {

My intention is that getMeta be an internal function, which is called when I need it.
Since the call to it is commented out, it should have no effect.
Presumably I’m doing something stupid.

I don’t see any way to include an input parameter in the script editor itself, so for the moment I can live with using the current image.
The next step will be to call the script from inside my program. Let’s assume for the sake of argument that I have several open images and the one I want to pass is called ect1. How would I code such a thing?
run(“myscript.groovy”, ect1);
On the script side I need to capture which imagePlus object I passed as input parameter.
What command do I need on the script side to make such a capture?
Do I need to do anything special in the script to indicate that I am using input parameters, and what they are?

Thanks again for the help. I would be totally lost without it.
Ilan