Re-using ImageJ scripts in KNIME

TL;DR: I have a Groovy script that I’d like to use from within KNIME for batch processing. What is currently the easiest way to achieve that?


My script takes an image and a binary mask (currently in two slices of the same stack) and creates a straightened version of the object defined by the mask, containing the (interpolated) pixel data from the image (see also this related discussion):

// @ImagePlus input
// @int maskSlice
// @int profileSlice

import ij.IJ
import ij.plugin.Duplicator
import ij.gui.PolygonRoi
import java.awt.Polygon
import sc.fiji.analyzeSkeleton.AnalyzeSkeleton_

imp = new Duplicator().run(input, maskSlice, maskSlice)
IJ.run(imp, "Skeletonize (2D/3D)", "")

// Initialize AnalyzeSkeleton_
skel = new AnalyzeSkeleton_()
skel.calculateShortestPath = true
skel.setup("", imp)
 
// Perform analysis in silent mode
skelResult = skel.run(AnalyzeSkeleton_.NONE, false, true, null, true, false)

// Read the results
shortestPaths = skelResult.getShortestPathList().toArray()
sppoints = skel.getShortestPathPoints()

// Create polyline ROI by averaging pairs of points
poly = new Polygon()
for (i=0; i<sppoints[0].size-1; i+=2) {
    x = (int)((sppoints[0][i].x + sppoints[0][i+1].x)/2)
    y = (int)((sppoints[0][i].y + sppoints[0][i+1].y)/2)
    poly.addPoint(x,y)
}
pRoi = new PolygonRoi(poly, PolygonRoi.POLYLINE)

// Create straightened image for kymograph etc.
input.setRoi(pRoi)
input.setSlice(profileSlice)
IJ.run(input, "Straighten...", "line=20");

imp.close()

For example, using these two input images:

it would produce output like this (I know it’s not perfect):


Now I wonder what’s the best way to get this into KNIME:

  • The ImageJ Macro node doesn’t seem to be suitable, as I my script makes some use of the Java API, so

    • I can’t transform it into a simple macro, and
    • I also can’t just copy the script into an existing Fiji installation and use a macro run() command to call it, since KNIME only allows to configure a ./plugins/ directory (i.e. Python or Groovy scripts don’t seem to be callable from the Macro node). Correct, @dietzc @tibuch?
  • Using the ImageJ2 integration by transforming the script into an IJ2 plugin would be an option, but would require some work around IJ1.x-isms in the current script.

  • Calling Fiji headless using the External Tool node. I would require some advice setting this up and getting the output back on a Windows system.

  • Any other options? Wait for the scripting integration to be released??

I’d be thankful for any advice.

Correct. You can run ImageJ1 Macros in the ImageJ Macro Node.

Actually, you can pretty easily convert this to an ImageJ2 Plugin. You can put @Parameter private ImagePlus in and @Parameter(type = ItemIO.OUTPUT) ImagePlus out. If I find some time, I will try it for your tonight and post the code here!

I wouldn’t do this. With images you can of course fake it (write images to disc, run your script etc) but this is tedious, cumbersome and slow.

Yes and no. With the KNIME SciJava and KNIP Scripting Integrations you have tons of new options (e.g. directly creating a node from some python script, Copy & Paste this code into the script editor etc), but I can’t promise a release of scripting yet. We did a complete rewrite of the infrastructure after some discussions we had on the last hackathon and there is still a lot of work to be done.

To summarize: I’d go for ImageJ2-Plugin Integration of KNIME. I can help you with that!

Best,

Christian

2 Likes

Can you use the eval("script", javascript) macro function?

1 Like

Alright, I succeeded in doing that:

My test images were 2D, the result images had three additional Unknown dimensions, so I had to use a Dimension Cleaner node to get a simple 2D image. Otherwise it works fine now, both in ImageJ and KNIME :slight_smile:

If you have comments when looking at the code, I’d be happy to read them.


Yes and no.

While I was unable to run Javascript with "script", Beanshell code with "bsh" seemed to work (the embedded ImageJ1.x asks to download Javascript.jar or Beanshell.jar, respectivley, when running the first time).

When running the macro code below however, I get another error, presumably related to the image type, that I wasn’t able to resolve.

id = getImageID();
run("Duplicate...", "duplicate range=2 use");
run("8-bit");
run("Skeletonize (2D/3D)");

eval("bsh", "import ij.IJ; import sc.fiji.analyzeSkeleton.AnalyzeSkeleton_; import ij.gui.PolygonRoi; import java.awt.Polygon; imp = IJ.getImage(); skel = new AnalyzeSkeleton_(); skel.calculateShortestPath = true; skel.setup(\"\", imp); skelResult = skel.run(AnalyzeSkeleton_.NONE, false, true, null, true, false); sppoints = skel.getShortestPathPoints(); poly = new Polygon(); for (i=0; i<sppoints[0].size()-1; i+=2) { x = (int)((sppoints[0].get(i).x + sppoints[0].get(i+1).x)/2); y = (int)((sppoints[0].get(i).y + sppoints[0].get(i+1).y)/2); poly.addPoint(x,y); } pRoi = new PolygonRoi(poly, PolygonRoi.POLYLINE); imp.setRoi(pRoi);");

selectImage(id);
setSlice(1);
run("Restore Selection");
run("Straighten...", "line=20");

selectImage(id);
run("16-bit"); // I tried these two lines to change image type
run("Add Slice"); // and dimensionality, but it doesn't seem to help

The error from knime.log:

java.lang.IllegalArgumentException: Interval must fit into src in SubsetViews.subsetView(...)
    at net.imglib2.ops.operation.SubsetOperations.subsetview(SubsetOperations.java:172)
    at org.knime.knip.imagej1.IJMacroNodeFactory$1.compute(IJMacroNodeFactory.java:301)
    at org.knime.knip.imagej1.IJMacroNodeFactory$1.compute(IJMacroNodeFactory.java:1)
    at org.knime.knip.base.node.ValueToCellNodeModel$1.getCells(ValueToCellNodeModel.java:385)
    at org.knime.knip.base.node.ValueToCellNodeModel.execute(ValueToCellNodeModel.java:534)
    at org.knime.knip.imagej1.IJMacroNodeFactory$1.execute(IJMacroNodeFactory.java:196)
    at org.knime.core.node.NodeModel.executeModel(NodeModel.java:566)
    at org.knime.core.node.Node.invokeFullyNodeModelExecute(Node.java:1128)
    at org.knime.core.node.Node.execute(Node.java:915)
    at org.knime.core.node.workflow.NativeNodeContainer.performExecuteNode(NativeNodeContainer.java:556)
    at org.knime.core.node.exec.LocalNodeExecutionJob.mainExecute(LocalNodeExecutionJob.java:95)
    at org.knime.core.node.workflow.NodeExecutionJob.internalRun(NodeExecutionJob.java:179)
    at org.knime.core.node.workflow.NodeExecutionJob.run(NodeExecutionJob.java:110)
    at org.knime.core.util.ThreadUtils$RunnableWithContextImpl.runWithContext(ThreadUtils.java:328)
    at org.knime.core.util.ThreadUtils$RunnableWithContext.run(ThreadUtils.java:204)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at org.knime.core.util.ThreadPool$MyFuture.run(ThreadPool.java:123)
    at org.knime.core.util.ThreadPool$Worker.run(ThreadPool.java:246)

@dietzc any suggestions?

1 Like

This is great. Sorry, that I wasn’t able to help you earlier. Can you maybe share what it took you to get the plugin run in KNIME? I’ve no further comments, it looks good. In an ideal world, of course, we shouldn’t have any IJ1 dependencies anymore. Maybe you could open some issues, where you describe the IJ1 functionality which you needed?

I don’t really understand why your output is 3D :wink: Can you share the compiled .class? Then I can debug the problem!

This is a tough one. I think it’s related to the problem with the additional result dimension. Can you try to extend the image with the Dimension Extender prior to the ImageJ-Macro node? This will give me a hint if I’m correct.

In general: I’m so much looking forward to the new Scijava/ImageJ integration and scripting in KNIME. This will make our lives so much easier. Sorry for the delay. To say it in @ctrueden words: This on the top five of my priority list… however, the first item on the list will keep me busy for a little while.

1 Like