11
$\begingroup$

tl;dr I am looking for a short but complete example that shows how to apply an ImageJ filter to a Mathematica Image through J/Link.


Fiji/ImageJ is one of the largest and most popular image processing tools. It is designed to be scriptable and can be used even interactively through a Jupyter notebook. It supports several languages such as Groovy/Python/Scala/R/Ruby/etc., but not Mathematica.

My aim is to use certain filters from ImageJ through the Ops framework. I am sure that this is possible through J/Link. Unfortunately, I don't know Java, and I'd need to go through many technical hurdles to get everything working.

I am looking for an answer that would get a typical Mathematica user (with no or minimal Java knowledge) through these technical hurdles. Assume that I downloaded the Fiji app and installed some plugins. What do I need to do to access a filter, such as median, from Mathematica, including setting up classpaths, (re)installing J/Link, and creating the required Java objects before I can call the necessary methods to run the filter?

The Groovy script to do this might look like this:

// Median filter.
median = ij.op().run("create.img", eyes32)
neighborhood = new HyperSphereShape(4)
ij.op().run("filter.median", median, eyes32, neighborhood)

Ideally, the ImageData would be transferred from Mathematica (here it comes from the eyes32 variable which was earlier loaded from a file).


There is a closely related question on StackOverflow. My question is different because it is specifically about using the Ops API and avoiding writing and compiling Java code (if possible).


Update with current progress and issues:

The first step is to create the ij object used in the tutorial notebooks.

Needs["JLink`"]

(* adjust the path to your Fiji *)
AddToClassPath@FileNames["/Users/szhorvat/Desktop/Fiji.app/jars/*"];

LoadJavaClass["com.wolfram.jlink.JLinkClassLoader"]

Issue 1: I can't include the crucial net.imagej.ImageJService below without triggering an error.

context = JavaNew["org.scijava.Context",
  JLinkClassLoader`classFromName /@ {
    (* "net.imagej.ImageJService", *) (* including this results in an error about loading net.imagej.legacy.LegacyService — how can I fix this? *)
    "net.imagej.ops.OpService",
    "io.scif.SCIFIOService",
    "org.scijava.io.IOService",
    "org.scijava.app.StatusService"
    }
  ]

ij = JavaNew["net.imagej.ImageJ", context]
(* JLink`Objects`vm1`JavaObject16280761535561729 *)

io = ij@io[]
(* JLink`Objects`vm1`JavaObject15342879492800513 *)

op = ij@op[]
(* JLink`Objects`vm1`JavaObject1760488337702913 *)

mandrill = io@open["http://imagej.net/images/baboon.jpg"]
(* JLink`Objects`vm1`JavaObject15612747035181057 *)

Issue 2: It is unclear to me why the following fails, even though Methods[op] does list the run and help methods.

med = op@run["create.img", mandrill]

(* During evaluation of Java::nometh: No method named run defined in class net.imagej.ops.DefaultOpService. *)
(* $Failed *)

op@help["create.img"]

(* During evaluation of Java::nometh: No method named help defined in class net.imagej.ops.DefaultOpService. *)
(* $Failed *)

Final remaining task: 1. retrieve the image data from the mandrill object 2. create a similar object directly from Mathematica using and Image's ImageData.

$\endgroup$
4
$\begingroup$

This is not an answer to the main question on how to do it in Mathematica but merely one possible reason why this whole thing might not be achievable. In Java, your exact example looks like this

package de.halirutan.ijm;

import java.io.IOException;
import net.imagej.ImageJ;
import net.imglib2.algorithm.neighborhood.HyperSphereShape;

public class TestCase {

    public static void main(String[] args) throws IOException {
        final ImageJ ij = new ImageJ();
        final Object mandrill = ij.io().open("http://imagej.net/images/baboon.jpg");
        final Object median = ij.op().run("create.img", mandrill);
        HyperSphereShape neighborhood = new HyperSphereShape(4);
        ij.op().run("filter.median", median, mandrill, neighborhood);
        ij.io().save(median, "median.png");
    }

}

This short thing takes almost a full minute to run because setting up all the imagej stuff seems expensive. Nevertheless, it saves successfully the median image of the mandrill.

The question is, why does this command fail in Mathemtica:

med = op@run["create.img", mandrill]

The reason is simple, because the error message is correct: "No method named run defined in class net.imagej.ops.DefaultOpService". But why does it work in Java then? Because since Java 8 it is possible to implement methods directly in an interface:

public interface OpEnvironment extends Contextual {

    // code...

    default Object run(String name, Object... args) {
        return run(this.module(name, args));
    }

    // code...

So Mathematica sees that there should be a method run because the DefaultOpService does implement it, but it is not able to call this default interface implementation.

I'm uncertain if this is possible at all in JLink but I'm sure, that this is the reason why it doesn't work. It's a Java 8 feature and although my 11.3 installation uses Java 8u152, it seems JLink doesn't support it.

$\endgroup$
  • $\begingroup$ So what remains as an option is writing my own class that wraps the relevant methods, right? $\endgroup$ – Szabolcs Aug 24 '18 at 7:22
  • $\begingroup$ Or talk to someone from the JLink team. I haven't spent very much time with this, I've seen these "op" things the first time, so there is a good chance I missed something. $\endgroup$ – halirutan Aug 24 '18 at 7:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.