Studying plugins: How is FrangiVesselness.class wired up to the Process>Filters>Frangi Vesselness Fiji menu entry?

imagej
plugin
imagej2
imagej-ops
#1

In short, why does the Frangi Vesselness command still show
up in the Fiji menu bar after I hide the plugin class?

For educational purposes I am playing with the IJ2-style Frangi
Vesselness plugin. (Process>Filters>Frangi Vesselness in stock
Fiji 1.51s.)

I took the (educational) liberty of unjarring the jar that
contains FrangiVesselness.class:

./Fiji.app/jars/imagej-ops-0.39.0.jar

hiding FrangiVesselness.class,and rejarring the jar.

With this tweak, Fiji launches, has nearly full functionality, but,
as expected, the Frangi Vesselness command won’t run. However, its
menu entry (Process>Filters>Frangi Vesselness) still shows up in
the Fiji menu bar. Trying to run it throws the sensible error:

[ERROR] Cannot create module: net.imagej.ops.commands.filter.FrangiVesselness
...
Caused by: java.lang.ClassNotFoundException: net.imagej.ops.commands.filter.FrangiVesselness

I see in the source:

the plugin annotation:

@Plugin(type = Command.class, menuPath = "Process>Filters>Frangi Vesselness")
public class FrangiVesselness<T extends RealType<T>> implements Command {

which, I assume, tells the Fiji / ImageJ2 / SciJava framework
where to put the menu entry. Because the menu entry still shows
up after hiding the class, I look elsewhere, and find:

META-INF/json/org.scijava.plugin.Plugin (in imagej-ops-0.39.0.jar)

It contains the entry:

{"class":"net.imagej.ops.commands.filter.FrangiVesselness","values":{"menuPath":"Process>Filters>Frangi Vesselness","type":"org.scijava.command.Command"}}

Is this how the Frangi Vesselness menu item gets wired up to FrangiVesselness.class? Is this json entry generated from the @Plugin annotation in FrangiVesselness.java? If so, is the json entry generated by the java compiler, or by a separate SciJava framework tool that is run at build time? This is probably more of a pure java question, but is the @Plugin annotation preserved in any way in FrangiVesselness.class or is it absent from the class file and only shows up in some non-java resource, such as the json file?

(Just to clear up a related point of confusion on my part,
am I right that

net/imagej/ops/filter/vesselness/DefaultFrangi.class

is unrelated to

net/imagej/ops/commands/filter/FrangiVesselness.class,

other than implementing a similar algorithm? And that
DefaultFrangi is not accessible from the stock Fiji menu
bar? And while we’re on the subject, I see in its source,
DefaultFrangi.java:

public class DefaultFrangi<T extends RealType<T>, U extends RealType<U>> extends
	AbstractUnaryComputerOp<RandomAccessibleInterval<T>, RandomAccessibleInterval<U>>
	implements Ops.Filter.FrangiVesselness

Where is the source that defines the Ops.Filter.FrangiVesselness
interface? I am looking for something like net/imagej/ops/Ops.java,
but don’t see it anywhere.)

Thanks, mm.

0 Likes

#2

Yes.

Yes.

Others might correct me or extend the explanation, but in short, SciJava makes use of standard Java annotation processing throughout its architecture, e.g. with @Plugin, @Parameter and @Service annotations.

See also:


I’m not sure, others might know…

No, that’s wrong.

The Command defined by the FrangiVesselness class calls the OpService to run the filter.frangiVesselness op:

The OpService then looks up all possible candidate ops that match the required op type and inputs, and then runs the best matching op. In this case, there’s only one possible op available: DefaultFrangi.

This architecture allows to add different (possibly better and/or faster) implementations at a later time, without the need to change the calling code.

This is a bit tricky, because the Ops interfaces are generated from templates at compile time.

  • The definition is in Ops.list…
  • … and the generated classes live in the subfolder ./target/generated-sources/from-template/net/imagej/ops of your Maven project once you’ve run mvn.

Hope that clarifies the situation a bit for you. Don’t hesitate to ask more specific follow-up questions. Someone will be able to answer… :slight_smile:

5 Likes

#3

Thank you for your reply Jan. It clears up a lot of things.

Yes, thanks. I should have seen this, as FrangiVesselness.java
clearly doesn’t implement any actual algorithm.

Thanks, I’m starting to get a cartoon picture of how these things
fit together. I’ll take a deeper look at some point and and follow
up with further questions as they arise.

Yes, this is a good start. I’ll be back …

Thanks again, mm.

0 Likes

#4

Thanks from me > a year later, too, @imagejan, and to you @mountain_man for asking this question.

It provided the insight I needed and was chasing for a day (I was trying to play with a fork of imagej-ops and had stupidly excluded the mvn target folder from my IntelliJ project so it couldn’t find import net.imagej.ops.Ops)

I have a follow-up documentation question - is the fact that Ops.java is generated from the template Ops.list documented somewhere? and should it be?
It would be good to know where to look next time - I would maybe have expected it to be here somewhere but I haven’t found it/recognised it.

Best,
Alessandro

1 Like

#5

I agree this is poorly documented. At the time, I also found out by looking at some commit that added an op, wondering about that strange Ops.list file, and discovering the Ops.vm file in the same folder :smiley:

Feel free to add some sentences in the wiki wherever you see fit, and where you’d have expected to find this information.

1 Like

#6

Thanks, @imagejan.

I have found some documentation about it now, here: https://github.com/imagej/imagej-ops/wiki/Naming
Hope this helps future readers of this post.

This helped me understand that there is something called a Velocity template that autogenerates the Ops.java class at runtime (?). I added a link to the Velocity website for people (like me) that had never heard of Velocity. Don’t know if this is useful, so feel free to revert back :slight_smile:

1 Like

#7

It’s generated at compile time, i.e. when you run mvn in the imagej-ops project, and will be located in the target/generated-sources/from-template folder.

1 Like

#8

Yep, sorry, agreed…

This: " Within this framework, a plugin is a Java class annotated with the @Plugin annotation. Classes annotated in this way are then automatically discovered and indexed at runtime , when the application is launched by a user (as opposed to compile-time )." from https://imagej.net/Writing_plugins got mixed up with the autogeneration at compile-time in my head, for no apparent reason :smiley:

I learnt a bit more about how Java annotations work as well trying to figure things out yesterday and the day before…Not that I feel confident that I understand these things well, but I am trying :smiley:

Thanks for helping!

1 Like