[ImageJ-devel] (headless) command execution

Curtis Rueden ctrueden at wisc.edu
Fri Aug 21 22:13:12 CDT 2015


Hi Stefan and Birgit,

More than one year later! Better late than never, hopefully.

> > Maybe we need a way to formally flag these "required before
> > initialize" parameters in the annotation? What do you think?
> > Would that help you?
>
> This would be different from autoFill() ?

Yes. The autoFill attribute is a way of saying to preprocessors: "If you do
some sort of automatic value setting, do not do it to me." E.g., it lets
you have a single image or display input without the
ActiveImagePreprocessor or Active*DisplayPreprocessor populating it. In the
case of the ImageJ UI, the InputHarvester preprocessor will then include it
in the dialog box (since the parameter is still not marked resolved), so
the user can explicitly select the desired object.

> Yes and no.
>
> In a way it makes responsibilities clear, in so far I feel comfortable
> with it.

Agreed, I like the declaration of intent there. But I'm not sure it would
help anything programmatically.

> However, it seems to me that there are (potentially very heavy)
> restrictions on the parameters which may be filled in advance
> (automaticcal and not by the user) in a sensible way. As far as I
> understand ij2 fills in parameters with what I think you called
> AutoFooPreprocessors, so active image and active display?

Indeed. Preprocessor plugins are contextual, meaning they have access to
the entire state of the context. And since the context is extensible, I
really have no idea what sorts of parameters are "sensible" to fill or not.
It really depends on the use cases of the application(s) involved.

> I cannot come up with further ones. How would we fill in an int
> parameter without knowledge of its meaning/sematics?

Right. Of course, the meaning/semantics would need to be known somehow. But
that is outside the scope of the SciJava library design. As I said above, I
can imagine (abstractly) that someone designs an application with various
preprocessors that populate various parameters, including int[], in some
predefined way for its use cases.

> And even for images is only possible for commands with exactly one
> image or display. Obviously we could introduce a further flag for an
> image parameter to notify, that I what to be assigned the active image
> with. But this seems pretty specific. And probably not generic to be
> introduce to scijava but rather deferred to the "application", e.g.
> imagej.

I agree; that flag would be too specific. That's what the preprocessor
design is for: to avoid overspecifying how parameters might get populated,
and instead leave it to the extensions (preprocessors) available to the
context. The ImageJ paradigm is to have an "active image" which we can then
use for populating image parameters, but this is far from the only valid
paradigm.

> And there might be aplications which call for more the two layers?

In a way, the preprocessor design accommodates N layers, because the
preprocessors are run in priority order. But in practice there are two
layers when it comes to the initializers: preprocessors that come before
InitPreprocessor, and preprocessors that come after it. I think this is OK
and there is no need to subdivide initialization farther than that, but if
you have a use case that illustrates otherwise, we can figure it out.

> Summing up, I am afraid that I do not have a cute idea.

Well, your cute idea was in the follow-up mail. We'll get to that below...

> Of course I appreciate the functionality the set initial values or
> make the type of parameters depending on image features (like
> dimensionality, range of values). Still adding parameters seems
> awkward to be. Could we come by with "only" changing types of
> parameters. E.g. for DuplicateImage not add one parameter for each
> axes rather have one array parameter with its length determined by the
> dimensionality? And may be indicate via annotations, depending on
> which other parameter(s) the type of this parameter will change?

Yes, this is very related to a recent reply I sent to imagej-devel in
another thread [1]. I will recapitulate it here:

There are five different classes of modules:

1) Totally fixed, with inputs and outputs known in advance.
2) Updates parameter attributes (such as choices—but not # or type of
inputs/outputs) once during initialization.
3) Updates parameter number and type (i.e., adding/removing inputs/outputs)
once during initialization.
4) Updates parameter attributes repeatedly e.g. during callbacks.
5) Updates parameter number and type repeatedly e.g. during callbacks.

These five types of modules challenge us in different ways:

#1 is easy -- I think everything can support it easily.

#2 and #4 do not update the fundamental aspects of the module: the name and
types of the parameters are known. So such modules can be classified as
fundamentally "well behaved".

#3 and #5 are more difficult because they are fully dynamic: the names and
types of parameters are not known a priori.

I agree with you that it would be nice if #2 and #4 type commands did not
need to extend DynamicCommand, but instead some other abstract class that
indicates they are well behaved from a typing perspective.

I filed an issue for it:
https://github.com/scijava/scijava-common/issues/179

While at it, I also filed an issue for commands which are iteratively
dynamic (i.e., alter their parameters during callbacks):
https://github.com/scijava/scijava-common/issues/180

Though the distinction of "iteratively dynamic" may go away after
implementing your idea to totally eliminate initializers in favor of
callbacks.

And for what it's worth, I agree that better support for List and array
parameters will often times avoid the need to use dynamic modules at all.
This is something we have wanted for a long time:
https://github.com/scijava/scijava-common/issues/118

> Thinking of module and parameter intitalizers it seems to be that I
> get more unsure. Is it exactly to do processing between the two layers
> of parameters.

Yes, that is one of the purposes, as discussed above. But not the sole
purpose.

> And it is to add further parameters

In the case of dynamic modules, yes. And I would classify that as a
"processing between two layers" use case.

> and set initial values to parameters?

Yes, that was the primary intended use case: when you want to set the
initial value of a parameter and it cannot be expressed as a simple
assignment. That is: if you want the default sigma to be 2.5, you simply
write:

  @Parameter
  private double sigma = 2.5;

But if you want to define it by writing code -- presumably to somehow
derive its initial value from the application state -- you need an
initializer. It is not enough to write:

  @Parameter
  private double sigma = initialSigma();

  private double initialSigma() {
    ...
  }

Because in that case, the initialSigma method is called when the object is
first instantiated, but before any context is injected. The initializer,
conversely, is typically called at a very specific point in the
preprocessing chain.

> Are initial parameter values execulsively meant to serve as defaults
> for the user or is it required that some initial values are set (in
> order to properly run the command)?

There is no hard assumption that initializers leave parameters "open" for
further user alteration. Actually, a dynamic command could set any number
of parameters to resolved during initializer routines, and then those
parameters would not be shown to the user in the input harvester.

As discussed earlier in this thread, the precondition of run is that all
required parameters be non-null when run() is called. This can be achieved
by the preprocessing chain, or because some/all parameter values were
passed to the moduleService.run method and prepopulated that way.

> If so: would this mean that for running a command head less in a non
interactive way makes the distiction of two layers is obsolete?

The typically non-interactive use case is to pass all parameter values
explicitly, which would make all the initializer stuff moot, yes. But it is
not a hard requirement -- it really depends on your definition of
"non-interactive"; if the preprocessing chain fulfills missing parameters
in some other way, that is also acceptable from the standpoint of
fulfilling the preconditions of run().

> running the init preprocessor gives
> -----
> Exception in thread "main" java.lang.IllegalStateException: Context
> already injected: org.scijava.AbstractContextual#context

Given the long time frame since you sent this: did you resolve this issue
in an acceptable way? Or is it still a problem for you? If it is still a
problem, I will be happy to look in more detail and give some advice -- in
a much more timely fashion this time.

> Is this intended, i.e. that the init preprocessor for a module may be
> run only once?

Yes, module.initialize() is intended to be called only once. To execute a
module again, create a new instance each time. The current code does still
have this requirement, because CommandModule#initialize() injects the
context, which is only allowed once.

> What we want to do is essentially the following:
> * The user selects a Command via the GUI and we create a Window
> displaying the input parameters.
> * The user may change/supply these parameters and run the Command.
> * Upon return a window displaying the results pops up.
> * However, the first window displaying the input parameters is still
> available, the user may change one/some parameters an rerun the
> command.
> * This eases testing various parameter values or the same set of
> parameters on different images without the need to repeatatly select
> an operator/command.
>
> Have you further advise how we can accomplish this, i.e. reusing a
> command module?

I think you can accomplish all of the above without reusing the same module
instance, but instead creating a new one every time. Is that how you are
currently doing it these days?

Alternately, if you don't call initialize() beyond the first time, that
would probably do the trick as well, although reusing module instances is
not a use case we have considered much.

> BTW: I prefer to not set the parameters via the moduleService.run
> method, as the parameters are set elsewhere (either by the user via a
> GUI configuring the operator/command e.g. from grappa, or by parsing
> command line argument in case of the command line oprunner.
> Additionaly I rather invoke the moduls run-method directly, as it
> seems to me that via moduleservie a new thread is started, and at this
> point we like to have this under control.

All makes sense. You can populate the parameters prior to run() in whatever
way you see fit. If you want to do it after initialization, you could
create your own preprocessor that does it. Or just set them directly right
away. Whatever works.

> it seems, that the init preprocessor assigns the service parameters of
> a Command, not the Service preprocessor as I expected. Is the correct?
> (Probably related to "shadowing" the Service and Context parameters of
> a Command)

Yes, because the InitPreprocessor calls initialize() and for CommandModules
that injects the context. And InitPreprocessor happens before
ServicePreprocessor. I discussed this wrinkle in my previous reply.

> BTW: what is the sematics of injecting a context (into a module?)

Does this answer your question:
https://github.com/scijava/scijava-common/blob/scijava-common-2.44.2/src/main/java/org/scijava/Context.java#L340-L360
?

Note that "injecting a Context" is something done to Java objects, not
modules per se. Initializing a CommandModule instance injects its context
into its associated Command instance [2]. But for other sorts of modules
such as ScriptModule, there is no associated Java object.

> > Do you think that Commands should also include service and context
> > inputs when iterating them? If so, we can try removing that
> > exclusion and see what explodes... ;-)
>
> We would not need them rather hide the from the user.

OK, then I will not worry about changing this behavior any time soon -- I
think it is tolerable for the time being. But unfortunately, to fully
support all modules (not just commands), you still need the logic to hide
service and context inputs, since other modules types like scripts will
often have them.

> no, we are hapy with one, just have to get hold of it (and use it in a
> static way)

Since you only need a single Context, you presumably either: A) have a
standalone application, in which case you should just spin up a Context and
keep a reference to it; or: B) have an ImageJ plugin, in which case you
should implement Command and write "@Parameter private Context context" in
your command and you'll magically get the context injected. Actually, it is
most direct in that case to declare @Parameters for all your needed
services, rather than the context itself, since typically what you will
want to do is make various service calls.

> We think we could do without command and parameter initializers but
> work only with the parameter's callback function

Great idea. This would elegant, and often superior in case of later changes
to the relevant parameter(s). We can try to simplify this for SJC3, which
is slated for release within the next few months [3]. I filed an issue for
it [4]. There are some minor obstacles in certain cases, which I describe
on the issue if you are interested, but they all seem surmountable.

> What if this callback reqires/assumes one or several other parameters
> to aready have be set? It seems to be, that this necessarily is the
> responsibility of the callback function (resp. its programmer) to
> handle this.

Agreed. This is a secondary parameter which depends on multiple primary
parameters. So they all need callbacks which recompute the secondary
parameter -- presumably by calling the same helper method, which does
nothing if any of the needed primary parameters are still null.

> Otherwise the Command or its Parameters have to declare a predefine
> order in which parameters have to be set. (Which we have considered to
> introduce to alida/mitobo few years ago but abandoned this idea as it
> seems to messy/error prone.)

Again agreed: best not to mandate anything with respect to order.

> in case of command execution via GUI (in contrast to
> programmatically): what if not all parameters which add further
> parameters are filled before the GUI is created and presented to the
> user and are set by the user? What we are considering is that the GUI
> to figure out such events have happened and then to adapt the GUI
> accordingly.

Yes, ideally we should support that in the ImageJ UI. I believe that
CellProfiler handles it by rebuilding the UI every time anything changes
structurally. We could do the same thing, and it would be efficient enough
-- the ImageJ API requires specific method calls to mutate parameter
attributes, which would tell us when a UI rebuild of the input harvester is
necessary.

> How to figure out that set set of parameter (or the type of an already
> existing parameter) was change by a callback function?

When a parameter _value_ is changed in a callback, it is typically a direct
assignment to a field. So that would not trigger any additional callbacks.
This makes infinite loops impossible, but also means it becomes the
programmers responsibility to keep track of "transitive parameter
dependencies" and update all downstream parameters accordingly.

When a parameter attribute is changed in a callback (which only
DynamicCommands can do), we can easily make the framework publish some
event -- individual programmers would not need to deal with this, and
everything would "just work."

> I favour the following guideline: A parameter, e.g. inputDisplay, may
> add (or remove) parameters to the command exclusively depending of its
> own value. Furthermore the parameter (names) potentially added by this
> parameter are disjoint from those parameters added by other parameters
> of the command. However, it is probably not possible to check or
> enforce this restriction.

While that would certainly provide some guarantees, I agree that it is not
possible to enforce it. So I would favor not stating any restrictions of
that sort.

> A different issue I came across have a second look at the Parameter
> annotation, sepcifically its choices: The few examples of usage I had
> a look at are parameters of type String which announce valid values.
> It might me worth considering to drop this feature and use Enums
> instead of Strings which easily allows to generate an approprate
> selection by the GUI.

The problem with enums is they are not extensible. So if you want to
dynamically generate the choices via callbacks, an enum will not work.
IIRC, the module framework does already support enums as dropdown list
boxes. But I think there is still a place for the choices attribute, too.
Let me know if you see a way around it.

Regards,
Curtis

[1] http://imagej.net/pipermail/imagej-devel/2015-August/002661.html
[2]
https://github.com/scijava/scijava-common/blob/scijava-common-2.42.2/src/main/java/org/scijava/command/CommandModule.java#L146
[3] https://github.com/scijava/scijava-common/milestones/3.0.0
[4] https://github.com/scijava/scijava-common/issues/181



On Wed, Apr 16, 2014 at 9:47 AM, Stefan Posch <posch at informatik.uni-halle.de
> wrote:

> Hi Curtis,
>
> regarding the concept of initializers I had a discussion with Birgit
> yesterday
> and we came up with the following ideas.
>
> We think we could do without command and parameter initializers but work
> only with
> the parameter's callback function which - as currently - are to be invoked
> upon setting/changing the corresponding parameters value.
>
> As currently, depending on parameter values, e.g. the image assigned to a
> Dataset,
> the command may add (or remove) parameters. This would be accomplished
> by the callback functions of the parameter whose value is the cause for
> these new parameters.
> E.g. the callback function of the inputDisplay of DuplicateImage would add
> the parameters
> (which is currently done by the command initializer).
> If some parameters should get (initial) values depending on the value of
> other
> parameters (e.g. your example of setting default brightness/contrast
> min,max values
> to current min,max of the input image) would we again the responsibility of
> the callback function of the cause, i.e. the input image.
>
> This would eleviate us of the need to fill in parameters required for the
> command initializer.
> If a parameter's value is set (either by a pre processor or
> programmatically)
> its callback function takes care of taking the apropriate actions.
>
> If the pre processor is run, it just needs to set the parameter values it
> decides to,
> and the callback functions would be could automatically.
>
> What remains is of course:
> (i) assume a parameter's value is changed, and its callback method
>   called accordingly. What if this callback reqires/assumes one or several
> other parameters
>   to aready have be set? It seems to be, that this necessarily is the
> responsibility
>   of the callback function (resp. its programmer) to handle this. I.e. to
> check
>   the validity of other parameters it requires. Otherwise the Command or
> its Parameters
>   have to declare a predefine order in which parameters have to be set.
>   (Which we have considered to introduce to alida/mitobo few years ago but
> abandoned this
>   idea as it seems to messy/error prone.)
> (ii) in case of command execution via GUI (in contrast to
> programmatically):
>     what if not all parameters which add further parameters are
>     filled before the GUI is created and presented to the user and are set
> by the user?
>     What we are considering is that the GUI to figure out such events have
> happened
>     and then to adapt the GUI accordingly.
>
>     How to figure out that set set of parameter (or the type of an already
> existing parameter)
>     was change by a callback function?
>     We see two possibilities:
>     (a) a parameter may announce via its annotation that it (occasionally)
>         _may_ change paramter definitions and the GUI always assumes that
> this had happend,
>         is such a parameter's value has changed (which the GUI knows at it
> controls this processes).
>     (b) the callback function is to fire events in case it changes
> parameter definitions.
>
>     (a) might be similar or be used in analogy to your idea of a parameter
> to announce "required before initialize",
>        as the pre processor framework would know that setting of such a
> parameter probably/potentially
>        may add parameters and should better be set before creating the GUI
> to ask the user for
>        further parameters (and if it accomplishes to set all of those
> parameters and either excludes them from
>        the GUI - as currently done by IJ2 for input images - or prohibits
> changing these parameters,
>        we get rid of problem (i) stated above).
>
>
> To make things conceptionally easier (for me) I favour the following
> guideline:
> A parameter, e.g. inputDisplay, may add (or remove) parameters to the
> command
> exclusively depending of its own value. Furthermore the parameter (names)
> potentially
> added by this parameter are disjoint from those parameters added by other
> parameters of
> the command. However, it is probably not possible to check or enforce this
> restriction.
> As stated above in (ii) in my view it would be the task of the programmer
> of the Comand anyway
> to cope with more complicated situations and does not affect to supporting
> "runtime system",
> e.g. the functionality to run pre processors and creating GUIs to harvest
> input vlaues from the user.
>
>
> Best regards
>
> Stefan
>
> PS.:
> A different issue I came across have a second look at the Parameter
> annotation, sepcifically
> its choices: The few examples of usage I had a look at are parameters of
> type String which
> announce valid values. It might me worth considering to drop this feature
> and use Enums instead
> of Strings which easily allows to generate an approprate selection by the
> GUI.
> --
> 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/
>



On Mon, Apr 14, 2014 at 10:16 AM, Stefan Posch <
posch at informatik.uni-halle.de> wrote:

> Hi Curtis,
>
> thanks for your reply, we are getting ahead, but still questions for
> running commands
>
> >    * The run() method of a module can assume that all required input
> >    parameters have been filled (i.e., are non-null). And the
> corresponding
> >    post-condition of run() is that all required output parameters are now
> >    filled.
> perfect, the same with us
>
> >    * The initialize() method of a module cannot necessarily assume
> >    anything about the state of its parameters. As you pointed out,
> ImageJ2
> >    has various commands right now that *do* assume certain parameters
> have
> >    been filled via preprocessing. This works in practice (at least from
> >    the ImageJ application), but is not particularly rigorous.
> >    * Similarly, ImageJ2 has several commands which assume their
> >    initializer has been called, but it is not stated as a formal
> >    precondition. Most probably it makes sense to state that in the
> >    javadoc, would you agree? In other words: if you aren't going to
> >    include the InitPreprocessor in the preprocessing chain, you are still
> >    somehow responsible for calling module.initialize() before calling
> >    module.run(). Is that fair?
> again: perfect
>
> >    So the main sticky point is what to do about initialize() requiring
> >    certain parameters to be already filled. This is useful for dynamic
> >    modules, but also just for computing defaults -- e.g., setting default
> >    brightness/contrast min & max values to the current display min max of
> >    the input image. So it seems there are two "layers" of input
> >    parameters: those needed before initialize() and those not needed for
> >    it.
> >    Maybe we need a way to formally flag these "required before
> initialize"
> >    parameters in the annotation? What do you think? Would that help you?
> This would be different from autoFill() ?
>
> Yes and no.
>
> In a way it makes responsibilities clear, in so far I feel comfortable
> with it.
>
> However, it seems to me that there are (potentially very heavy)
> restrictions
> on the parameters which may be filled in advance (automaticcal and not by
> the user)
> in a sensible way. As far as I understand ij2 fills in parameters with
> what I think
> you called AutoFooPreprocessors, so active image and active display?
> I cannot come up with further ones. How would we fill in an int parameter
> without knowledge of its meaning/sematics?
> And even for images is only possible for commands with exactly one image
> or display.
> Obviously we could introduce a further flag for an image parameter to
> notify,
> that I what to be assigned the active image with. But this seems pretty
> specific.
> And probably not generic to be introduce to scijava but rather deferred to
> the
> "application", e.g. imagej.
> And there might be aplications which call for more the two layers?
>
> Summing up, I am afraid that I do not have a cute idea.
> Of course I appreciate the functionality the set initial values or make
> the type
> of parameters depending on image features (like dimensionality, range of
> values).
> Still adding parameters seems awkward to be. Could we come by with "only"
> changing
> types of parameters. E.g. for DuplicateImage not add one parameter for
> each axes rather
> have one array parameter with its length determined by the dimensionality?
> And may be indicate via annotations, depending on which other parameter(s)
> the
> type of this parameter will change?
>
> Rather more question:
> Thinking of module and parameter intitalizers it seems to be that I get
> more unsure.
> Is it exactly to do processing between the two layers of parameters.
> And it is to add further parameters and set initial values to parameters?
> Are initial parameter values execulsively meant to serve as defaults for
> the
> user or is it required that some initial values are set (in order to
> properly
> run the command)?
>
> If so: would this mean that for running a command head less in a non
> interactive way
> makes the distiction of two layers is obsolete?
>
> >    One thing that may interest you is the recent ImageJ OPS announcement:
> >       [1]http://developer.imagej.net/2014/04/04/announcing-imagej-ops
> >    An Op is just an ImageJ command intended to be fully functional --
> That sounds great ...
>
> >   Hi Stefan,
> >   Thanks. Rather than doing this:
> >     CommandInfo testopInfo = new CommandInfo(IJTestOp.class.getName());
> >     testopModule = (CommandModule) testopInfo.createModule();
> >   I suggest instead:
> >     CommandInfo testopInfo = commandService.getCommand(IJTestOp.class);
> >     testopModule = (CommandModule)
> >   moduleService.createModule(testopInfo);
>
> the advise you gave works fine, however only for running a module for a
> given Command once.
>   (see attached source of RunTestOp.java, the zip is a tiny maven project
> ready to import to eclipse.
>    hopefully I do not forget the attachment again :-)
>
>   in short we have
>         CommandInfo testopInfo =
> commandService.getCommand(IJTestOp.class.getName());
>         CommandModule testopModule =
> (CommandModule)moduleService.createModule( testopInfo);
>
>    set some parameters
>
>    run the service and init preprocessor on the module, then
>
>         testopModule.run();
>     or as an alternative
>         moduleService.run(testopModule, false);
>
>    running the init preprocessor gives
> -----
> Exception in thread "main" java.lang.IllegalStateException: Context
> already injected: org.scijava.AbstractContextual#context
>         at org.scijava.Context.inject(Context.java:293)
>         at imagej.command.CommandModule.initialize(CommandModule.java:144)
>         at
> imagej.module.process.InitPreprocessor.process(InitPreprocessor.java:61)
>         at mainroutine.RunTestOp.main(RunTestOp.java:62)
> ------
>
>   Creating a new module for the same CommandInfo fixes the problem (line
> 60)
>
>   Is this intended, i.e. that the init preprocessor for a module may be
> run only once?
>   This would pose a serious problem to us:
>
>   What we want to do is essentially the following:
>   The user selects a Command via the GUI and we create a Window displaying
> the input parameters.
>   The user may change/supply these parameters and run the Command.
>   Upon return a window displaying the results pops up.
>   However, the first window displaying the input parameters is still
> available, the user may
>   change one/some parameters an rerun the command.
>   This eases testing various parameter values or the same set of
> parameters on different
>   images without the need to repeatatly select an operator/command.
>
>   Have you further advise how we can accomplish this, i.e. reusing a
> command module?
>
>   BTW: I prefer to not set the parameters via the moduleService.run
> method, as the parameters
>        are set elsewhere (either by the user via a GUI configuring the
> operator/command e.g. from grappa,
>        or by parsing command line argument in case of the command line
> oprunner.
>        Additionaly I rather invoke the moduls run-method directly, as it
> seems to me that
>        via moduleservie a new thread is started, and at this point we like
> to have this under control.
>
>   it seems, that the init preprocessor assigns the service parameters of a
> Command,
>   not the Service preprocessor as I expected. Is the correct?
>   (Probably related to "shadowing" the Service and Context parameters of a
> Command)
>
>   BTW: what is the sematics of injecting a context (into a module?)
>
> >    So this is certainly inconsistent and confusing. Do you think that
> >    Commands should also include service and context inputs when iterating
> >    them? If so, we can try removing that exclusion and see what
> >    explodes... ;-)
> We would not need them rather hide the from the user.
>
> >    If you urgently need support for multiple simultaneous ImageJ contexts
> >    created with the default constructor while ij-legacy is on the
> >    classpath, and you want to try fixing the bug yourself, we would be
> >    happy to elaborate further so that you can give it a shot.
> no, we are hapy with one, just have to get hold of it (and use it in a
> static way)
>
> Again, thanks again
>
> best regards   Stefan
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://imagej.net/pipermail/imagej-devel/attachments/20150821/58d6159b/attachment-0001.html>


More information about the ImageJ-devel mailing list