How can I figure out how to execute ImageJ commands from my own code?

What’s the recommended way of finding out how I can reproduce the same functionality of a specific ImageJ menu item in my java code? I am developing a java application that uses ImageJ’s functionality behind the scene, but the UI of ImageJ is not supposed to be visible. I am using the following line of code to have access to the ImageJ’s context if needed:

final ImageJ ij = new ImageJ();

So, suppose that I want to have the funcionality of “Image > Type > 8-bit” applied to an ImagePlus object I have in my code. The first approach that I can think of is to use the “Command Finder”:

It suggests that the 8-bit command resides in ij.plugin.Converter("8-bit") class. But how can I use this piece of code in my application? Googling “ij.plugin.Converter” gets me to the “Converter” class document, which suggests that the Converter is a plugin and I can use it by calling its run method. But I cannot call the run method because I am not developing a plugin.

There is however a piece of code in stackoverflow, which shows how this can be done using the ImageConverter class:

import ij.ImagePlus;
import ij.process.ImageConverter;

// ...

ImagePlus imp = IJ.getImage();
ImageConverter ic = new ImageConverter(imp);
ic.convertToGray8();
imp.updateAndDraw();

Fortunately somebody has already demonstrated how to convert an ImagePlus object to 8-bit using the ij.process.ImageConverter class. But what about other functionalities? Is there any document out there which indicates what class can be used for each menu item?

Hi Meysam

Your ImageJ is actually an ImageJ2 , which I believe, based on previous discussions, you needed so that you could avoid showing the GUI. So you would need an additional step to load the data and then convert to ImageJ1-ImageJPlus. Something like this…

final ImageJ ij=new ImageJ();
String imName = "/home/bnorthan/images/ij_list/convolveheadless/original image.tiff";

final Dataset data = ij.dataset().open(new File(imName).getAbsolutePath());

ImgPlus<UnsignedShortType> img = (ImgPlus<UnsignedShortType>) data.getImgPlus();
ij.ui().show(data);

// in this step we convert the 'Img' to an IJ1 ImagePlus        
ImagePlus imp = ImageJFunctions.wrapUnsignedShort(img, "IJ1-ImagePlus");
IJ.run(imp, "8-bit", "");
ij.ui().show(imp);

Once you have an IJ1 ImagePlus you can then reproduce an ImageJ menu command a couple of ways, I find it easiest to first record a macro in ‘java’ mode. Then, if I run into problems with the macro for some reason I research and import the underlying lower level class(es) (as you did with the converter).

2 Likes

Hello @meysam

There is one trick that I use very often when scripting. If you use the macro recorder, you usually can see the command you executed. In your case:

run("8-bit");

This can be reproduced on a script or piece of java code like this:

IJ.run( imp, "8-bit", "" );

where the first parameter is the input ImagePlus on which you’re applying the command, and the third parameter is a string containing the options of the command (empty in this case).

ignacio

2 Likes

I’m sure the code @bnorthan wrote works fine, but I’m a bit worried about the cast to (ImgPlus<UnsignedShortType>) because we might not know the actual type of data in the Dataset, e.g. if we open a different image file…

Even though it’s a bit more complex, I’d use the ConvertService class which was pointed out to me by @hinerm . To be able to use it in your class needs a @Parameter ConvertService convertService member, and access to the ImageJ2 context. You can get the access by extending the class AbstractContextual which has the method setContext. If your class happens to be a ImageJ2 Command or Op its service @Parameters are populated automatically.

Here’s code where I use the ConvertService to convert a Dataset into an ImagePlus: https://github.com/bonej-org/imagej-tutorials/blob/dataset-wrap/ij2-image-plus/src/main/java/DatasetWrapping.java (line 205). And here’s where I use a class which extends AbstractContextual: https://github.com/rimadoma/BoneJ2-preliminary/blob/master/devUtil/src/test/java/org/bonej/devUtil/intervalUtil/IntervalUtilTest.java

Hope this helps, best
Richard Domander

3 Likes

Thanks a lot for that tip @rimadoma. Also, it looks like it is possible to grab the convert service from the ij context

ImagePlus imp=ij.convert().convert(data, ImagePlus.class);
5 Likes

@bnorthan @rimadoma it’s great to see you guys recommending IJ2 mechanisms :slightly_smiling: I can’t emphasize enough that it is fantastic that you’re providing your own usage examples and code snippets.

Because retrieving stuff from the Context is so fundamental to using ImageJ2, I suggest also linking to the writing plugins page when discussions like this come up. I wanted that page to be the authoritative “how to get stuff from the Context” document, so if anything there is unclear we should improve it!

Scripts also get @Parameter population! :smile:

This is true but Contextual is a little lower-level than people usually need to worry about. If you’re writing a Command you’ll probably extend ContextCommand. If you’re writing a script then you don’t have to worry about class hierarchy at all, and can just write your @Parameters.

Also true. It makes sense to use this invocation if you are writing a program that uses ImageJ. If you are writing a command for ImageJ then using @Parameters has the advantage of explicitly declaring the needs of the class ahead of time.

3 Likes

Agreed!

That said, changing types is something that should be a one-liner in ImageJ2. Unfortunately, with the ImageJ Common data model in flux, it isn’t as easy as it could be. @meysam is right that if you have an ij.ImagePlus, you can use ij.process.ImageConverter to change its type. With ImageJ2, you can use the convert namespace of ImageJ Ops to convert an image data object (any net.imglib2.IterableInterval actually) between types. But this does not work directly on net.imagej.Dataset objects—for the moment, they must be unwrapped by calling getImgPlus() first.

With that caveat, here is a short script that converts an image to 16-bit unsigned integer type using ImageJ2:

// @OpService ops
// @Dataset d
// @OUTPUT Dataset out
out = ops.convert().uint16(d.getImgPlus())

However, it requires the convert.uint16 op, which was only very recently added, and is not yet released + uploaded to the core ImageJ update site.

And that is the really key question here! “Teach me how to learn,” yeah?

For learning ImageJ 1.x API, checking the Command Finder is definitely a good strategy, as you discovered.

For learning ImageJ2, we are doing our best to provide tutorials for common operations. As more ImageJ2-driven commands become available in ImageJ, you will also see those codes listed in the Command Finder prefixed with command: (there are only a few of those at the moment).

Other than that: write to the forum, and we will help.

2 Likes

Are you sure this works correctly? I tested it and I got a whole-black image.
Also, it turns out that in this specific case I don not need to do any conversion. It suffices to use the following:

ImagePlus imp = IJ.openImage(imName);
IJ.run(imp, "8-bit", "");

As you can see, I didn’t even need to instantiate ImageJ:

final ImageJ ij = new ImageJ();

Am I missing anything by omitting the above line? I think I need that only for automatically populating @parameters in Service and Command plugins. Right?

2 Likes

@meysam

I’m not sure. I always instantiate ImageJ because I orgnanize my code as commands. I tested a simple script, that did NOT instantiate ImageJ, and it worked fine.

Brian

FYI, there was a bug in some recent versions of ImageJ 1.x which caused the ImageJFunctions.wrap* methods not to work unless a new ij.ImageJ() was created beforehand. But @tpietzsch reported the bug to @Wayne and he fixed it.

2 Likes