Changing the LUT to glasbey does a mysterious conversion to RGB - Bug?

Hello,

I have a label image that I obtain from a segmentation script and which is initially 8bit grayscale format. So far so good.

To visualize the regions better, I want to convert to “glasbey_on"dark” LUT (or any of the “glasbey” family) and have these lines in my jython script:

IJ.run(labelImp, "glasbey_on_dark","")
labelImp.show()

And then something super mysterious happens: the image is semi-converted to RGB (see the highlighted rectangle: 3 color values). This is an issue because downstream analysis thinks the image is RGB but can only process 8bit.

I already tried debugging it myself but things got only more mysterious. When I for example first display the image and then covnert the LUT, its type is correct.

But the wrong conversion will then randomly happen at a downstream step, so no solution here.

Anyway, I find it very confusing and non-intuitive why the image type changes in this random way - does anyone have any ides?

Thanks already!

Update:
Here is a much simpler way to reproduce the issue: Image duplication causes the same problem.
So when running this macro:

run("Blobs (25K)");

run("glasbey"); // run("Grays") would cause no issue

// duplicate messes the image type up
// in original + duplicate
run("Duplicate...", " ");

Both original and duplicated image end up having the strange colored index image type (type=3) instead of 8bit (type=0)

2 Likes

It gets even more confusing when working with scripting. Before showing an ImagePlus in the UI, it’s still 8-bit, but as soon as you show it, it’s 8-bit Color, as illustrated by this Groovy script (runnable from the script editor):

import ij.IJ
import ij.ImagePlus

def getReadableType(i) {
	switch (i.getType()) {
		case ImagePlus.GRAY8: return "ImagePlus.GRAY8"
		case ImagePlus.GRAY16: return "ImagePlus.GRAY16"
		case ImagePlus.GRAY32: return "ImagePlus.GRAY32"
		case ImagePlus.COLOR_256: return "ImagePlus.COLOR_256"
		case ImagePlus.COLOR_RGB: return "ImagePlus.COLOR_RGB"
	}
}

imp = IJ.openImage("http://imagej.nih.gov/ij/images/blobs.gif")
IJ.run(imp, "glasbey", "")
println ("Original before showing: ${getReadableType(imp)}") // ImagePlus.GRAY8

imp.show()
println ("Original after showing: ${getReadableType(imp)}") // ImagePlus.COLOR_256

imp2 = imp.duplicate()
println ("Duplicate before showing: ${getReadableType(imp2)}") // ImagePlus.GRAY8

imp2.show()
println ("Duplicate after showing: ${getReadableType(imp2)}") // ImagePlus.COLOR_256

Interestingly, the Glasbey LUT only gets applied to the duplicated image, although the command was run on the original imp (before showing it)…

@Wayne can you help shedding some light on this issue?

1 Like

Hello Noreen and Jan -

Speaking from memory, I’ve seen the same kind of thing before,
although I don’t know the exact parameters of when and how it
happens.

However, this is an issue of when a flag gets updated (and
displayed in various gui elements), rather than a bug in the
image not getting “converted” (or getting converted incorrectly).

So, yes, I do think there’s a bug here, but it’s one of keeping
the type-of-image flag in sync with reality.

Some further context:

An 8-bit color image (COLOR_256) is merely an 8-bit image
that has a LUT that happens to be a color LUT. Furthermore
color LUTs and grayscale LUTs are not inherently different.
A color LUT is merely a LUT that happens to have at least
one entry for which R and G and B are not all equal.

ImagePlus does have a private type property, a protected
setType() method, and a public getType() method. But the
type property gets set to COLOR_256 (or not) based on
querying the LUT. Here is ImagePlus's relevant code fragment:

        LookUpTable lut = new LookUpTable(image);
        int type = GRAY8;
        if (lut.getMapSize() > 0) {
            if (lut.isGrayscale())
                type = GRAY8;
            else
                type = COLOR_256;

I don’t know where things get out of sync, but when you apply
glasbey to your 8-bit image, it is now an 8-bit color image (in
that glasbey is a color LUT). But the type property and / or
the gui elements that reflect the type aren’t getting updated in
a timely fashion, and then later some other operation triggers
them getting synced up again.

If you want to be perverse, you can create an 8-bit color image
that has a LUT with only one non-grayscale entry and then
sneak in the back door and edit that entry to be grayscale
(R = G = B). The image will think still think it’s color for a
while until it re-queries the LUT and discovers that it’s pure
grayscale.

You can also have a grayscale image (e.g., pure black) for
which some of the LUT entries are not grayscale, but for
which none of the pixels index into the non-grayscale entries.
Such an image considers itself to be COLOR_256.

In my experience (certainly not exhaustive) I haven’t ever
seen ImageJ do anything wrong with a mis-flagged 8-bit
image (other than displaying confusing gui elements). So
I’ve taken the view that this is primarily a cosmetic issue.

Thanks, mm

2 Likes

That’s interesting, thanks.

The issue described by @noreenw looks to be specific to the Glasbey family of LUTs, however. It didn’t occur when I tested with the Fire, Ice, glow or phase LUTs. Is there any explanation to this?

2 Likes

Thank you @mountain_man and @imagejan for digging into this!

I did experience an issue: some downstream plugins that I used only accepted 8-bit gray as input and converting back to that type is tedious (or I did not find the simple solution). Simply converting to 8-bit no work because all image values are scaled to range [0,255] (independent of whether “scale during conversion” is checked). So one additionally has to reset the range before converting. At least it seems to me that this cannot be the intended way…

2 Likes

Hello Jan -

I am not aware of any substantive difference between the “glasbey”
LUT and, say, “fire.” (glasbey appears to be loaded from a .lut
file shipped with Fiji / ImageJ using IJ2, while fire appears to be
constructed on the fly with IJ1. But glow appears to be loaded
the same way as glasbey, so this difference seems irrelevant.)

However, the plot thickens …

When I run your test script as described below, I get the results
you report for the “glasbey” LUT. But … Things still look
(slightly differently) goofy when I run your script with the “glow”
LUT. (I did this experiment only once, so I can’t say that it’s
repeatable.)

I open up the script editor, set the language to groovy, and
paste your script into the single open tab. Then I open a
new tab, set to groovy, paste again, and change “glasbey”
to “glow.” I run the original script first, and then I run the
“glow” version.

Here are the outputs:

“glasbey” version:

Original before showing: ImagePlus.GRAY8
Original after showing: ImagePlus.COLOR_256
Duplicate before showing: ImagePlus.GRAY8
Duplicate after showing: ImagePlus.COLOR_256

“glow” version:

Original before showing: ImagePlus.GRAY8
Original after showing: ImagePlus.GRAY8
Duplicate before showing: ImagePlus.GRAY8
Duplicate after showing: ImagePlus.GRAY8

Now, as to the images …

From the “glasbey” script:

blobs.giif:
Displays as grayscale (looks like the unmodified “blobs.gif”).
The status-bar pixel values show both an index and a
grayscale rgb value. The rgb values are those from the
inverting LUT that is part of blobs.gif.
Image > Color > Show LUT shows the glasbey LUT.
Image > Show Info... says “(color LUT)”.

DUP_blobs.gif:
Displays in color with glasbey applied.
The status bar shows pixel indices and (what appear to be)
glasbey rbg values.
Image > Color > Show LUT shows the glasbey LUT.
Image > Show Info... says “(color LUT)”.

Now from the “glow” script:

blobs.giif:
Displays as grayscale (looks like the unmodified “blobs.gif”).
The status-bar pixel values show just a value that is the
(pre-LUT) pixel value of the original blobs.gif
Image > Color > Show LUT shows the glow LUT.
Image > Show Info... says “(color LUT)”.

DUP_blobs.gif:
Displays in color with glow applied.
The status bar again just shows pixel values (that are equal to
those of the original blobs.gif) even though the image displays
in color.
Image > Color > Show LUT shows the glow LUT.
Image > Show Info... says “(color LUT)”.

(All four images are labeled, just below the title bar, “256x254;
8-bit; 64K”. In contrast, the original blogs.gif is labeled “256x254;
8-bit (inverting LUT); 64K”.)

Again, I can’t say whether I would get the same results if I ran
this test again. I only ran it once. I didn’t try, for example,
running the “glasbey” and “glow” versions of the script in the
opposite order. Or restarting ImageJ in between running the
two versions of the script. Or etc., etc. …

In general, this is consistent with a lot of different out-of-sync /
race-condition issues I see when displaying and manipulating
images with Fiji / ImageJ. In general, these kinds of things
have always appeared to me to be cosmetic, in that I’ve never
verified that any of the underlying data was incorrect. (Although
it can be quite confusing when trying to use an out-of-sync
gui to probe the underlying data …) In general, I have these
kinds of issue happen some times but not other times – although
I can’t be sure that the steps I followed in producing the differing
results were exactly the same.

I performed these tests with “ImageJ 2.0.0-rc-69/1.52p; Java
1.8.0_172 [64-bit]”, a post-switch-to-https downloaded stock
version of Fiji, freshly auto-updated, on ubuntu 16.04 LTS.

Thanks, mm

2 Likes

Hello Noreen -

As I understand it, this issue is to be expected. When you apply
a non-grayscale LUT to an 8-bit grayscale image you do “convert”
it (in my terminology) to an 8-bit color (COLOR_256) image.

(Note, this is not the same as converting it to an RGB color
(COLOR_RBG) image, which uses 32 bits per pixel to store rgb
values, and does not have a LUT (sometimes called a “palette”).)

But there are (I believe) some tools and commands that check
and will refuse to run on an 8-bit color image (even though
they will run on an 8-bit image with a grayscale LUT).

Converting back to the original 8-bit grayscale image might
be as simple as applying a grayscale LUT, e.g.:
Image > Lookup Tables > Grays.

(If your original grayscale image had a non-trivial grayscale LUT,
then you will have changed the LUT and the image will look
different. But it should still be acceptable to a command that
refuses to run on 8-bit color images.)

If you could post a slimmed-down, as-simple-as-possible script
that illustrates your workflow and the issue you face with this,
forum participants might have some useful suggestions.

Thanks, mm

1 Like

This bug is fixed in the latest ImageJ daily build (1.52r28).

1 Like

Hello Wayne -

Your fix (if I understand the issue correctly) doesn’t work for me.

I upgraded to your daily build (so I am now running ImageJ
2.0.0-rc-69/1.52r28; still on ubuntu).

When I run both the “glasbey” and “glow” versions of Jan’s
test script I get almost the same results as before, namely, for
both test scripts I get the results I reported for the glow test
script in my previous post (with the exception, of course, that
the glasbey script gives me glasbey coloration for DUP_blobs.gif,
and the glasbey LUT for both blobs.gif and DUP_blobs.gif).

Thanks, mm

It works for me. Are you sure you’re running ImageJ 1.52r28? This is the output I get when I the run glasbey version Jan’s script:

Original before showing: ImagePlus.GRAY8
Original after showing: ImagePlus.GRAY8
Duplicate before showing: ImagePlus.GRAY8
Duplicate after showing: ImagePlus.GRAY8

I had to convert “println” to “IJ.log” to see the output.

Hello Wayne -

Yes, I was careful to run ImageJ 1.52r28.

I get the same output as you, but it seems wrong to me.

We first open a GRAY8 image (blobs.gif). But I would expect
all four results for getType() to be COLOR_256, because
all four calls to getType() occur after the image has been
“converted” to COLOR_256 by applying the color (glasbey)
LUT.

Also, the first image displays as grayscale even though
it has been converted to color (and was converted before
imp.show() is called).

For completeness, here is my script and results. (I added
a printout of the version and use both println() and
IJ.log().)

Script:

import ij.IJ
import ij.ImagePlus

def getReadableType(i) {
	switch (i.getType()) {
		case ImagePlus.GRAY8: return "ImagePlus.GRAY8"
		case ImagePlus.GRAY16: return "ImagePlus.GRAY16"
		case ImagePlus.GRAY32: return "ImagePlus.GRAY32"
		case ImagePlus.COLOR_256: return "ImagePlus.COLOR_256"
		case ImagePlus.COLOR_RGB: return "ImagePlus.COLOR_RGB"
	}
}

println ("ImageJ version: ${IJ.getFullVersion()}")
IJ.log ("ImageJ version: ${IJ.getFullVersion()}")

imp = IJ.openImage("http://imagej.nih.gov/ij/images/blobs.gif")
IJ.run(imp, "glasbey", "")
println ("Original before showing: ${getReadableType(imp)}")
IJ.log ("Original before showing: ${getReadableType(imp)}")

imp.show()
println ("Original after showing: ${getReadableType(imp)}")
IJ.log ("Original after showing: ${getReadableType(imp)}")

imp2 = imp.duplicate()
println ("Duplicate before showing: ${getReadableType(imp2)}")
IJ.log ("Duplicate before showing: ${getReadableType(imp2)}")

imp2.show()
println ("Duplicate after showing: ${getReadableType(imp2)}")
IJ.log ("Duplicate after showing: ${getReadableType(imp2)}")

Results:

Started blobs_glasbey.groovy at Sat Oct 05 19:40:23 EDT 2019
ImageJ version: 1.52r28
Original before showing: ImagePlus.GRAY8
Original after showing: ImagePlus.GRAY8
Duplicate before showing: ImagePlus.GRAY8
Duplicate after showing: ImagePlus.GRAY8

Note, for me, the println() results appear in lower
pane of the script-editor window that opens up when I
run New > Script.... (With both println() and
IJ.log() I get the same results in both the script editor
and the Log window.)

Thanks, mm

1 Like

Hello Apply-LUT Enthusiasts -

Here is another bit of unexpected behavior:

If, in a groovy script, I open a grayscale image, .show() it,
and then apply a color LUT, it displays in color. If I apply
the LUT before the .show(), it displays as grayscale.

Here is a test script:

import ij.IJ

imp = IJ.openImage("http://imagej.nih.gov/ij/images/blobs.gif")
imp.show()
IJ.run(imp, "glasbey", "")

imp = IJ.openImage("http://imagej.nih.gov/ij/images/AuPbSn40.jpg")
IJ.run(imp, "glasbey", "")
imp.show()

Here are screenshots of the results:

“blobs” in glasbey color:

Screenshot_of_blobs_gif

“AuPbSn40” in grayscale:

Screenshot_of_AuPbSn_jpb

(I uploaded screenshots because when you save the image
that is incorrectly displayed as grayscale and then reopen it,
the reopened image displays in color.)

Thanks, mm

This bug is fixed in the latest ImageJ daily build (1.52r29).

The conversion to COLOR_256 when applying the glasbey LUT is the bug that is fixed in ImageJ 1.52r28. Now the only way to get a COLOR_256 image is to use the Image>Type>8-bit Color command.

1 Like

Thank you for the fixes! I tried with the latest build and now it works as I would except, i.e. the image type stays GRAY8 independent on which LUT is used to display. This makes switching between LUTs easier and revertible. (Out of curiousity: is there any scenario where GRAY8+LUT information would not be enough knowledge and one would need the COLOR_256 type?)
A nice extra is now that now the behaviour of 8bit images is much more consistent to the behaviour of 16bit images when using a LUT like glasbey to display the regions - since the latter (16bit) never changed data type when changing the LUT.

I’m afraid I could not create a reproducible script. I tried a few flavors of a script but sometimes the issue occurred and sometimes not and I don’t know what state in my application caused this. So no point to investigate further based on this. Since the fix from @Wayne works to avoid dealing with COLOR_256 altogether I think it’s not worth following up on my previous comment here

The COLOR_256 type is needed, and is used, when you use the Image>Type>8-bit Color command to convert an RGB image to 8-bit color.

Hello Wayne -

I don’t follow exactly what you’re saying here. As far as I can tell
the only type of image that can be converted to “8-bit Color” is
“RGB Color.” “RGB Color” can be converted to and from both
“8-bit” (also labeled “8-bit (grayscale)”) and “8-bit Color.”

I don’t see where the COLOR_256 type enters into to any of
this (except maybe to prevent “8-bit” -> “8-bit Color”, which is
anyway essentially a no-op).

Thanks, mm

ImageJ sets the image type to COLOR_256 when you convert an RGB image to 8-bit color using the Image>Type>8-bit Color command.

The latest daily build (1.52r34) fixes a regression that caused COLOR_256 images saved as TIFF to reopen as GRAY8.