Is there a pure IJ2 mechanism for displaying images using stock Fiji?

imagej2
display
Tags: #<Tag:0x00007fb87cfe38c8> #<Tag:0x00007fb87cfe3760>

#1

I’ve posted a couple of issues I uncovered when exploring
how to display IJ2 images. They are all related to the
fact that (per documentation “found on the internet”) the
approved way to display an IJ2 Img is to call

  ImageJFunctions.show (img, "title");

That is, IJ2 delegates image display to IJ1, and the issues
I’ve stumbled over are associated with the IJ2–IJ1 interface.

So, is there a native IJ2 mechanism that I should be using
to display IJ2 images (using stock Fiji for real-world work)?

Thanks, mm.


#2

Dear @mountain_man,

Thank you for your reports!

Depending on the context of your work, you could be using an output script parameter that is shown automatically using the appropriate UI. Something like the following should do the trick:

@Parameter(type = ItemIO.OUTPUT)
RandomAccessibleInterval<...> img;

An alternative approach would be to use DisplayService.show(title, object). This requires to get hold of a DisplayService via

@Parameter
DisplayService displayService;

// somewhere:
displayService.show("title", img);

You can find more information on those topics in the Under the hood: Scijava notebook.

Best,
Stefan


#3

Hello Stefan,

Thank you, that works.

I believe that shoud be:

// somewhere:
displayService.createDisplay ("title", img);

(DisplayService does not appear to have a .show (...) method.)

Thanks, this also works (with createDisplay).

Two comments:

First, both methods (ItemIO.OUTPUT and createDisplay) appear
(at least visually) to display the images the same way (as one
would expect).

Second, images of signed integral types (I’ve tested ByteType
and IntType) appear to get wrapped / converted to FloatType
(or analogous) images. When displayed, they both show up as
"32-bit," and the pixel values show up (using Fiji’s cursor and
reading the pixel values from Fiji’s status line) as things like
"x=0, y=0, value=-128.0" (note, in particular, the trailing “.0”).

This – converting to FloatType – seems like a reasonable
expedient. No information is lost, and the images display
correctly.

(UnsignedByteType displays exactly I as would expect; in
particular, the display images are labelled as “8-bit.”)

(My tests were done using an IJ2-style plugin in an auto-updated
Fiji.)

Thanks, mm


#4

Argh, sorry for the confusion! It’s actually the UIService with its show method that I wanted to point out.

I am just thinking out loud because I haven’t had time to confirm this. 8-bit images in ImageJ are represented by a ByteProcessor that is backed by a byte[]. That is, the negative range of a byte has to be somehow mapped to [128, 255] in “ImageJ1 values” (8-bit images have values in [0, 255]). The same idea holds for other types, I assume: values from a container of UnsignedShortType could be mapped to a ShortProcessor (backed by a short[]). The only ImageJ1 image type, that can represent negative values is 32-bit, i.e. FloatProcessor backed by a float[]. Hence, all the signed types that you have mentioned IntType, ByteType need a 32-bit ImageJ image. Otherwise, you wouldn’t be able to map the data into the ImageJ1 concept.

Does that make sense?


#5

Hello Stefan,

Thanks for the follow-up. Your analysis of the image-conversion
question is consistent with my thinking.

Yes. I’ve now tried:

  uiService.show ("title", img);

and it works, as well. (As far as I can tell – when run in a java
plugin launched from Fiji – it does exactly the same thing as
displayService.createDisplay ("title", img);,
except for returning void.)

Yes, that makes sense. This was pretty much my thinking when I
was analyzing the related bug in

   ImageJFunctions.show (img, "title");

See my earlier post:

Signed integral Img is displayed incorrectly by ImageJFunctions.show

In any event, the ImageJFunctions.show() bug still stands,
and ought to be addressed somehow. I would suggest (in order
of increasing fanciness):

  1. Remove ImageJFunctions.show() and require the use
    of, e.g., UIService.show(), instead.

  2. Have ImageJFunctions.show(img, "title")
    convert img to FloatType / GRAY32 (which is what
    UIService.show(), DisplayService.createDisplay(),
    etc., appear to do).

  3. Refactor the code so that ImageJFunctions.show()
    uses the same code path as UIService.show() and
    DisplayService.createDisplay() (which, hopefully, are
    already using the same code path as one another).
    (Could ImageJFunctions.show() simply be rewritten
    to delegate to UIService.show(), or is there something
    more going on here that would keep this from working?)

  4. Teach IJ1 how to display signed integral images so
    that IJ2 can use IJ1 to display them without converting
    to FloatType / GRAY32 (which has the cost of the conversion
    itself, as well as a cost of up to a factor of four in storage.)

Thanks, mm


#6
  1. Is not an option because people use ImageJFunctions.show() without using SciJava Services.
  2. Seems to be the way to go, IMHO
  3. That would require dependencies between imglib2-ij and scijava-common which we are trying to avoid
  4. That’s very unlikely to happen because keeping it compatible with existing plugins would be a nightmare, IMHO

Would you mind opening an issue in imglib2-ij describing your observations?


#7

Hi Stefan,

Yes, agreed.

This seems okay – quick and not too dirty.

Yes, makes sense.

Well, this might be unlikely to happen, but I don’t see a problem
keeping things compatible. Adding classes and/or methods to IJ1
(signed integral display support) would be a non-breaking change.
The only compatibility issue that could arise would be using a new
version of ImageJFunctions.show() with an old IJ1 jar that doesn’t
support the new calls. But, with all of the poms and boms and
mavens and updaters, I don’t see a legitimate reason for this to
ever happen.

The new ImageJFunctions would have the new IJ1 display
functionality as a dependency – you only update the former
if do (and can) update the latter. (This must happen all the
time – a new version of X requires new version of some
dependency Y.)

I don’t know enough about the usage patterns of ImageJ to know
whether it would be worth the bother to add functionality to IJ1,
and I don’t know whether the cost of the proposed --> FloatType
conversion (not trivial in principle) would be likely to matter in
practice. So maybe the quick fix is the smarter choice.

But the clean fix is certainly doable.

Not at all. See:

I’ve also added a link to the github issue to the related post:

Thanks, mm