[ImageJ-devel] (headless) command execution

Curtis Rueden ctrueden at wisc.edu
Thu Apr 3 15:57:50 CDT 2014


Hi Stefan,

Very sorry for the long delay in reply. I should be faster in subsequent
replies, if you have further questions.

> What are the precoditions assumed when running a (headless) command

What happens is highly dependent on *how* you invoke the command...

> may the command assume that its initializer, if any, has been executed
> prior to executing its run-Method?

I assume you are familiar with this tutorial:
https://github.com/imagej/imagej-tutorials/blob/master/execute-commands/src/main/java/ExecuteCommands.java

Note that when executing an ImageJ module using the ModuleService, the run
methods (for the past couple of releases) have a "boolean process" flag.
When process=true, the pre- and post-processing steps are run. This notably
includes running the module initializer preprocessing step.

> may the initializer-Method assume some of the parameter of the command
> already set (as e.g. a single ImageDisplay), and of course, which
> parameters?

The InitPreprocessor calls module#initialize(), whose behavior can be
overridden, but the default implementation of which is here:

https://github.com/imagej/imagej/blob/imagej-2.0.0-beta-7.8/core/core/src/main/java/imagej/module/AbstractModule.java#L75

In short: the module initializer is called first, followed by the
individual initializers of each parameter, if any.

As for *when* this occurs: the InitPreprocessor has "HIGH" priority:
https://github.com/imagej/imagej/blob/imagej-2.0.0-beta-7.8/core/core/src/main/java/imagej/module/process/InitPreprocessor.java#L50

So it happens before many other preprocessors:

$ grep 'priority = ' $(git ls-files | grep Preprocessor)
core/core/src/main/java/imagej/display/ActiveDisplayPreprocessor.java: priority
= Priority.VERY_HIGH_PRIORITY)
core/core/src/main/java/imagej/module/process/CheckInputsPreprocessor.java:
priority
= InputHarvester.PRIORITY - 1)
core/core/src/main/java/imagej/module/process/DebugPreprocessor.java:@Plugin(type
= PreprocessorPlugin.class, priority = Priority.FIRST_PRIORITY)
core/core/src/main/java/imagej/module/process/GatewayPreprocessor.java:
priority
= Priority.VERY_HIGH_PRIORITY)
core/core/src/main/java/imagej/module/process/InitPreprocessor.java:@Plugin(type
= PreprocessorPlugin.class, priority = Priority.HIGH_PRIORITY)
core/core/src/main/java/imagej/module/process/LoadInputsPreprocessor.java:
priority
= Priority.VERY_LOW_PRIORITY + 1)
core/core/src/main/java/imagej/module/process/SaveInputsPreprocessor.java:
priority
= Priority.VERY_LOW_PRIORITY - 1)
core/core/src/main/java/imagej/module/process/ServicePreprocessor.java:
priority
= Priority.VERY_HIGH_PRIORITY)
core/core/src/main/java/imagej/module/process/ValidityPreprocessor.java:
priority
= Priority.VERY_HIGH_PRIORITY + 1)
core/data/src/main/java/imagej/data/display/ActiveImagePreprocessor.java:
priority
= Priority.VERY_HIGH_PRIORITY)
core/ui/src/main/java/imagej/ui/FilePreprocessor.java: priority =
Priority.VERY_LOW_PRIORITY + 1)
core/ui/src/main/java/imagej/ui/UIPreprocessor.java:@Plugin(type =
PreprocessorPlugin.class, priority = Priority.VERY_HIGH_PRIORITY)


So the ones that happen before InitPreprocessor are:
1) DebugPreprocessor
2) ValidityPreprocessor
3) ActiveDisplayPreprocessor
4) ActiveImagePreprocessor
5) GatewayPreprocessor
6) ServicePreprocessor
7) UIPreprocessor

The DebugPreprocessor just logs some debugging output. The
ValidityPreprocessor makes sure that the module is fundamentally
well-formed (e.g., no "final" @Parameter variables, since those cannot be
set via reflection). The other five (2-7) all set various types of
variables based on the state of the ImageJ application context -- for
example, all Service parameters are filled in. So that's why you never see
the ImageJ UI prompt for them.

It should be case that Alida can reuse the default pre- and post-processing
plugin stack -- in other words, you should be able to pass "process=true"
to the ModuleService#run and everything will "just work". Let us know if
not, and we can troubleshoot. As long as no UI has been shown, you will be
in headless mode and no dialogs should ever be shown. (If one does pop up,
it is probably a bug.)

> as an example, the DuplicateImage command adds input parameters to
> itself in its initializer-method depending on the inputDisplay. Thus
> is seems that DuplicateImage requires, that inputDisplay is set prior
> to calling its initializer and that the user is asked for further
> parameters lateron.

Yes, it works in that case because the ActiveDisplayPreprocessor runs
before the InitPreprocessor, so inputDisplay is indeed already set.

> more generally, are there parameters of commands set by the standard
> ij2 preprocessos besides a single unresolved Dataset? And is there an
> easy way to figure out what preprocessors are run in which order in
> the standard ij2 context?

Yes, the preprocessors are (by default) those mentioned above. You can get
the list of them programmatically:

pre = pluginService.createInstancesOfType(PreprocessorPlugin.class)


This gives you one instance each of each preprocessor, in priority order.
Alternately, if you don't want to instantiate them but only inspect the
plugin metadata:

infos = pluginService.getPluginsOfType(PreprocessorPlugin.class)


> What is your concept to run a headless command from command line (or
> shell script), which adds input parameters dependent on the value of
> other input parameters?

On the CLI, we'll harvest values from the user at the same point in time
that we currently do it via the UI. So most of the other preprocessing will
be done; there will be a "CLIInputHarvesterPlugin" that prompts the user to
type in these values using System.in or similar. We have not yet created
this preprocessor plugin, but it would be very straightforward. If you need
this, let me know -- it would be a fun side project. :-)

> Do you encourcage to dynamically add input parameters to commands?

Encourage? Definitely not. Unfortunately, there are certain commands that
basically require this functionality. Use your IDE to check the subtype
hierarchy of DynamicCommand to find them. But as a rule of thumb: headless
modules should not be dynamic. Dynamic commands are *much* more challenging
to support across many different environments (CellProfiler, KNIME, OMERO,
Alida, etc.) *much* more challenging.

> Is there (still) a static method to get hold of the context, as a
> while ago was possible via ImageJ.getContext() in order not to (very)
> often create a new context (or it the context impemented as a
> singelton)? And how does this work out if running command "truely"
> headless that is from command line without ij2 GUI?

The SciJava context is definitely not a singleton. But you can use it that
way if you want: just create one a static variable in your own codebase
somewhere; e.g.:


public static final imagej.ImageJ IJ = new imagej.ImageJ();


Then you'll always have your ImageJ gateway, easily accessible from all
your code. But I would caution you that if your design relies on statics
like that, it will be fundamentally less flexible then if you always inject
a Context in places where one is needed. We have taken great pains to make
all of ImageJ2 work in that way...

> is it remnants, that some ContextCommands have services as parameters
> as they - if I understand correctly - could retrievee the services
> from their contex?

We feel that writing the services as @Parameters is cleaner, because those
services are, in some sense, "inputs" to the module's operation. There are
many ways to express a module's inputs, but our goal is for modules to
declare their inputs in the most specific way possible. (We fall short of
that goal in many places, but are very open to refactoring to improve
things on a case by case basis.)

For example, you could write:


@Parameter

private Context context;


And then call "context.service(ModuleService.class).run(...)" but that is
less specific than:

@Parameter

private ModuleService moduleService;


And then calling "moduleService.run(...)". This latter form more clearly
expresses the fact that it is actually the ModuleService, and not the
Context as a whole, that is required for operation.

As another example, you could declare an ImageDisplayService parameter and
then use it to extract the active image display, then extract the active
dataset from that, and finally process it. But it is much cleaner to
declare the parameter as a Dataset in the first place, so that the module
*could* be used with inputs besides just the active image display's active
dataset. For other environments (CellProfiler/KNIME/OMERO/Alida/etc.), the
notion of an "active image display" might not make sense, so using a
Dataset inputs is better.

> has ij2 already support to execute a headless command from the command
> line or shell script

I actually added something like that last week: a rudimentary interactive
script interpreter. It is mainly for testing the ImageJ OPS project right
now, but it supports whatever scripting language you want to use, with
whatever ImageJ code you want to throw at it.

https://github.com/imagej/imagej-scripting-cli

Example of usage:

$ git clone git://github.com/imagej/imagej-scripting-cli
... code is downloaded ...
$ cd imagej-scripting-cli
$ mvn
... code compiles ...
$ mvn exec:exec
... Maven spits out some stuff ...
ImageJ script interpreter: javascript
> dataset = ij.dataset().open("/Users/curtis/data/toucan.png");
toucan.png
> w = dataset.max(0);
160
> h = dataset.max(1);
148

> future = ij.command().run("imagej.plugins.commands.app.AboutImageJ",
true, []);
java.util.concurrent.FutureTask at 2698dd08
> module = future.get();
imagej.plugins.commands.app.AboutImageJ
> module.getOutput("display").getClass();
class imagej.data.display.DefaultImageDisplay


Once we have the CLI input harvesting plugin, this will be a little slicker
in that "ij.module().run(...)" and "ij.command().run(...)" will be usable
for modules that take "real" inputs.

Regards,
Curtis

On Thu, Mar 13, 2014 at 4:39 AM, Stefan Posch <posch at informatik.uni-halle.de
> wrote:

> Hi Curtis, hi Johannes,
>
> we have a few questions regarding commands and command execution
> to wrap them for our graphical editor and also command line execution
> of operators resp. commands.
>
> What are the precoditions assumed when running a (headless) command
>
>   o may the command assume that its initializer, if any, has been executed
> prior to executing its run-Method?
>
>   o may the initializer-Method assume some of the parameter of the command
> already set
>     (as e.g. a single ImageDisplay), and of course, which parameters?
>
>     as an example, the DuplicateImage command adds input parameters to
> itself
>     in its initializer-method depending on the inputDisplay.
>     Thus is seems that DuplicateImage requires, that inputDisplay is set
> prior to
>     calling its initializer and that the user is asked for further
> parameters lateron.
>
>   o more generally, are there parameters of commands set by the standard
> ij2 preprocessos
>     besides a single unresolved Dataset?
>     And is there an easy way to figure out
>     what preprocessors are run in which order in the standard ij2 context?
>
> - What is your concept to run a headless command from command line (or
> shell script), which
>   adds input parameters dependent on the value of other input parameters?
>
>   Do you encourcage to dynamically add input parameters to commands?
>
> - Is there (still) a static method to get hold of the context, as a while
> ago was
>   possible via ImageJ.getContext() in order not to (very) often create a
> new
>   context (or it the context impemented as a singelton)?
>   And how does this work out if running command "truely" headless that is
>   from command line without ij2 GUI?
>
> - is it remnants, that some ContextCommands have services as parameters
>   as they - if I understand correctly - could retrievee the services from
> their contex?
>
> - has ij2 already support to execute a headless command from the command
> line or shell script
>
> Thanks a lot and cheers
>
> Stefan
> --
> Prof. Dr.-Ing. Stefan Posch,
>         Institut fuer Informatik, Martin-Luther-Universitaet
> Halle-Wittenberg
>         Von-Seckendorff-Platz 1, 06099 Halle (Saale)
> phone:  ++49 345 55-24728
> fax:    ++49 345 55-27039
> e-mail: Stefan.Posch at informatik.uni-halle.de
> www:    www.informatik.uni-halle.de/~posch/
>
> _______________________________________________
> ImageJ-devel mailing list
> ImageJ-devel at imagej.net
> http://imagej.net/mailman/listinfo/imagej-devel
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://imagej.net/pipermail/imagej-devel/attachments/20140403/f9d00437/attachment-0001.html>


More information about the ImageJ-devel mailing list