Question about ImageJ Ops

I have a question regarding Ops using parts of ImageJ2 as an embedded library.

The only example I found to call Ops is to instantiate the ImageJ class:

https://github.com/imagej/imagej-tutorials/blob/master/using-ops/src/main/java/UsingOps.java

At the moment I want to avoid to use the ImageJ class and the dependencies using ImgLib2 and scifio dependencies.

Is there a way to to call Ops on image data without the net.imagej.ImageJ class?

Hello @Bio7,

If you’re writing your own plugin, you can add an OpService to your class, and call Ops trough that.

@Plugin(type = Command.class)
public class MyPlugin implements Command {
    @Parameter
    private OpService opService;

    public void myMethod() {
        opService.math().add(2, 5);
    }
    ...
}

Best,
Richard Domander

6 Likes

Dear Richard,

thanks for the fast help. I will try that.

Best

Marcel

I think the problem for me is that I would like to use the Ops without the startup of the ImageJ2 interface.

If I have a plugin I have to call it with the ImageJ2 interface like shown in the ImageJ2 tutorials as far as I understood.

Something like:

final ImageJ ij = new ImageJ();

or:

Main.launch(args);
IJ.runPlugIn(MyPlugin.class.getName(), "");

before calling the Ops.

However I use a slightly modified version of ImageJ1.

What I wanted to do is to use Ops e.g., with ImageLib2, Scifio loaded data and then display it with e.g., ImageJ1 to avoid some dependencies caused by the ImageJ2 interface.

Hmm, I haven’t tried this myself, but I think you could try something like

@Plugin(type = Command.class, headless = true)
public class MyClass extends ContextCommand {
    ...
    @Override
    public void run() {
        final Context context = (Context) IJ.runPlugIn("org.scijava.Context", "");
        this.setContext(context);
        myMethod();
    }

    public static void main(String... args) {
        IJ.runPlugIn(MyClass.class.getName(), "");
    }
}

Where myMethod() would then call Ops etc. Note that IJ.* calls go to the ImageJ1 API.

Regards,
Richard

This hack is unnecessary here. It is only required if you are doing things the “IJ1 way.” In other words: if you are implementing ij.plugin.PlugIn and do not have a handle on the SciJava application context.

In your example, you wrote extends ContextCommand which means the command already knows its context. So there is no need to use that hack. Instead, simply write @Parameter fields as desired and they will be injected. You can also write getContext() directly to obtain the SciJava Context instance.

There are two scenarios:

  1. Your code is running within ImageJ somehow—e.g., it is a plugin being launched. In that case, you certainly do not need to spin up a second SciJava application context. There already is one. See above.

  2. Your code is running “outside ImageJ” in some sense. The tutorials are all like this: just running from a main method. In that case, you need to either: A) have a Context passed to you by the caller; or B) spin up a new Context. (E.g.: Writing new ImageJ() spins up a new Context internally.)

So @Bio7 could you please clarify what you mean by “I would like to use the Ops”? Use them how? From where?

Hello Curtis,

my first goal is to make a part of the ImageJ2 API’s (ImageLib, SciJava and Ops) as an updateable Eclipse plugin available to use them in combination with my ImageJ1 Eclipse plugin.

I already managed to use ImgLib2 and SciJava from within the ImageJ1 plugin.

However until now I was not able to use Ops because it seems that the startup of the ImageJ2 interface is required somehow or at least the code examples suggest so which collides at the moment with some minor changes I made on ImageJ1.

I want to use the Ops part only on image data e.g. open a file with SciJava (ImgLib2) and then apply an Ops algorithm on the data which then can be visualized as an ImagePlus in ImageJ1 (Eclipse).

@rimadoma Thanks again for the help!

Hi @Bio7,
I am working on something similar for KNIP, see https://github.com/knime-ip/knip-scijava.
To get started using Ops you should would need to do the following:

  • Require the OpService in a class:
package org.myorg.mypackage

import org.scijava.Context;
import org.scijava.plugin.Parameter;

import net.imagej.ops.OpService;

/**
 * Example to show how to use ops from outside ImageJ2.
 *
 * @author Gabriel Einsdorf
 */
public class MyClass {

       // injecting the context makes all members marked with 
      // @Parmeter avaliable.
       public MyClass(Context context){
           context.inject(this);
       }

	/* Servics */
	@Parameter
		private OpService ops;

	public void testMetod() {
		// Show that ops is available   
		System.out.println(ops.math().add(3,4));
	}
}
  • Inject the context into your class:
Context context = new Context();
MyClass myClass = new MyClass(context);
myClass.testMethod();
2 Likes

You refer to net.imagej.ImageJ, yes? That “gateway” object is never required for any core service. It is merely a convenience.

The LegacyService tries to patch ImageJ1, which does not work due to changes you have made? Do I infer the problem correctly?

It’s that last part—conversion of ImgLib2 data structure to an ij.ImagePlus—which is causing all the trouble, I think. For that, you need the LegacyService. Do you have an SSCCE you can post?

@gab1one Thanks for the code example and the fast reply. That looks interesting. I will try that tomorrow.

I think that’s one problem. I get some error messages indicating a problem with the GenericDialog class if I compile the example with Eclipse (however, my aim is to compile it dynamically).

I can already use ImgLib2, open images and convert them to an ImagePlus object. That works flawlessly.

I can post a SSCCE tomorrow. And will also try out the solution from @gab1one.

@gab1one: Great example. That works at least if I start the java class in a new process.

@ctrueden However it seems that I need to install the bioformats library. Without I got this error of a missing class, service:

Caused by: java.lang.IllegalArgumentException: Invalid service: io.scif.ome.services.DefaultOMEMetadataService

So is an installation of bioformats mandatory (At least part of the library is GPL where I need a EPL compatible License, e.g., Apache)?

In addition if I try to start the example reflectively (after the dynamic Java compilation I call, e.g., the main class by reflection) the following error occurs:

java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)Caused by: java.lang.IllegalArgumentException: Multiple 'net.imagej.ops.math.PrimitiveMath$IntegerAdd' ops of priority 0.4:
1. (int result) =
    net.imagej.ops.math.PrimitiveMath$IntegerAdd(
        int a,
        int b)
2. (int result) =
    net.imagej.ops.math.PrimitiveMath$IntegerAdd(
        int a,
        int b)

Request:
-    net.imagej.ops.math.PrimitiveMath$IntegerAdd(
        Integer,
        Integer)

Candidates:
1.     (int result) =
    net.imagej.ops.math.PrimitiveMath$IntegerAdd(
        int a,
        int b)
2.     (int result) =
    net.imagej.ops.math.PrimitiveMath$IntegerAdd(
        int a,
        int b)

    at net.imagej.ops.DefaultOpMatchingService.findModule(DefaultOpMatchingService.java:110)
    at net.imagej.ops.AbstractOpEnvironment.module(AbstractOpEnvironment.java:219)
    at net.imagej.ops.AbstractOpEnvironment.run(AbstractOpEnvironment.java:98)
    at net.imagej.ops.math.MathNamespace.add(MathNamespace.java:221)
    at MyClass.testMethod(Unknown Source)
    at CallOps.main(Unknown Source)
    ... 8 more

Do you have any ideas why this is loaded two times if called by reflection?

Hi,

Could you paste your exact call to the math.add Op please? As the error states, no Op matches what you’re trying to call, i.e. addition for two Integer objects. Rather the Ops operate on primitive int types, maybe there’s some weird implicit autoboxing going on…

Regards,
Richard

Well this error just exists if I compile and call the example from @gab1one by reflection compiled dynamically in the same Java process. It works however when I compile it regular (Run As Java application in Eclipse):

I wonder what would happen if you wrote testMethod() as

public void testMetod() {
    int a = 3;
    int b = 4;
    System.out.println(ops.math().add(a, b));
}

The same error occurs.

The same result when I call the class from a Groovy script:

javax.script.ScriptException: javax.script.ScriptException: java.lang.IllegalArgumentException: Multiple 'net.imagej.ops.math.PrimitiveMath$IntegerAdd' ops of priority 0.4:
1. (int result) =
    net.imagej.ops.math.PrimitiveMath$IntegerAdd(
        int a,
        int b)
2. (int result) =
    net.imagej.ops.math.PrimitiveMath$IntegerAdd(
        int a,
        int b)

Request:
-    net.imagej.ops.math.PrimitiveMath$IntegerAdd(
        Integer,
        Integer)

Candidates:
1.     (int result) =
    net.imagej.ops.math.PrimitiveMath$IntegerAdd(
        int a,
        int b)
2.     (int result) =
    net.imagej.ops.math.PrimitiveMath$IntegerAdd(
        int a,
        int b)

    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:151)
    at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:264)

I am not quite sure why you want to do that though. Do you want users to write ImageJ2 scripts in Bio7? In that case have a look at https://github.com/scijava/scijava-common/wiki/Scripting , we have already a scripting framework for such cases. I am working on integrating that into KNIME Image Processing, so I can help you further with that.

This indicates that some Services are not available, this one deals with loading metadata for the OME file format, I don’t think you need to worry about it.

By default the new Context() constructor tries to load all available Services as well as all services required by them. You are able to specify the Services you want to use using the other constructors, giving you a much more granular control and avoiding such error messages.

1 Like

Yes that is the first part to use Ops. From within Bio7. But as I already noted I refactored out the ImageJ1 plugin interface for the use in Eclipse or an RCP application and I want to make parts of the ImageJ2 API available to the ImageJ1 plugin.

This would be the first step for an ImageJ2 API integration with a visualization in the ImageJ1 plugin.

A scripting interface and shell is available in Bio7. But there is also plugin for the general Eclipse user available to combine it with, e.g. the ImageJ1 plugin, too. But I think that’s not the problem. Somehow the Ops are loaded twice when called reflectively or by my integrated Groovy interpreter. This was tested inside of Bio7 running in one Java process.

That’s very interesting. Thanks for the info.