What, in Fiji / ImageJ, is the technical distinction between a GRAY8 and COLOR_256 image?

fiji
imagej
luts
color

#1

Fiji / ImageJ appears to distinguish between grayscale and
color 8-bit images. (In Fiji-land this distinction is variously
denoted as “8-bit” / “8-bit Color”, “8G” / “8C”, and “GRAY8”
/ “COLOR_256.” I believe theses pairs of names are all
synonymous with one another, but there may be some difference
hiding in there somewhere.)

Let me start by stating emphatically that the difference between
"8G" and “8C” is NOT whether the image is grayscale or color
(as I explain, below). I can create an image using just the
Fiji gui that is a color image, but is reported as “8G,” and
I can create a pure grayscale image that is reported as “8C.”

So what IS the difference, and what value / use does it have
in Fiji? What’s the deal here – why does Fiji have color images
that it calls grayscale (“8G”) and grayscale images that it calls
color (“8C”)?

(I probe the type of the image in various ways, but the
simplest is to use the Fiji gui and run Image>Type and look
whether the “8-bit” or “8-bit Color” check box is checked.)

Some details:

This is “ImageJ 2.0.0-rc-65/1.51u; Java 1.8.0_66 [64-bit];” on
ubuntu 16.04 LTS.

I can make a “fake-grayscale” image as follows:

   File>New>Image...
      Name: fake_grayscale
      Type: 8-bit
      Fill with: Ramp
      Width: 512 pixels
      Height: 512 pixels
      Slices: 1
   Image>Lookup Tables>Fire

Now selecting:

   Image>Type

shows “8-bit” checked. (Also calling imp.getType() in a java
plugin returns the constant ImagePlus.GRAY8.) But the image
is visibly a color image.

Conversely, I can make a “fake-color” image:

   File>New>Image...
      Name: fake_color
      Type: 8-bit
      Fill with: Ramp
      Width: 512 pixels
      Height: 512 pixels
      Slices: 1
   Image>Lookup Tables>Spectrum
   Image>Type>RGB Color
   Image>Type>8-bit Color
   Image>Lookup Tables>Grays

Now selecting:

   Image>Type

shows “8-bit Color” checked. (Also calling imp.getType()
returns the constant ImagePlus.COLOR_256.) But the image
is purely shades of gray.

The “8G” labelling of fake_grayscale survives being written to
disk as fake_grayscale.tif, and reopened; the “8C” labelling of
fake_color does NOT survive being stored as fake_color.tif.
(I am not able to upload .tif files so I can’t provide image files
that illustrate this behavior. But you can reproduce the images
within Fiji by following the recipes given above.)

I have a theory that “8G” should logically mean that the image
is a byte image with a LUT that happens to be a pure
grayscale LUT, and “8C” should logically indicate a byte image
whose LUT is not pure grayscale. Furthermore, ImagePlus
contains a (semi-)redundant data member, ImagePlus.imageType,
that encodes the “8G” / “8C” difference, but that is not
always kept in sync with the image’s actual color properties.

Thanks, mm


#2

Hey there,

In both cases, the underlying pixel values are 8-bit unsigned integers (0-255), but what you did when you constructed your images, you applied a “LUT”, turning them into “pseudocolor” images. That way a monochromatic (single channel) image can look colored. See https://imagej.nih.gov/ij/docs/guide/146-9.html#toc-Section-9 This is only for display, and doesn’t change the underlying pixel values.

The distinction between the two image types you ask about only seem to be whether the applied LUT is greyscale, or includes colors other than gray. Basically not much of a difference, practically speaking.

This snippet from ij.ImagePlus#setImage(java.awt.image.Image) shows this:

if (lut.getMapSize() > 0) {
			if (lut.isGrayscale())
				type = GRAY8;
			else
				type = COLOR_256;
		}

Hope this helps!

D


#3

David,

Thank you for your explanation.

I basically agree with you, but I think I would phrase it a little
differently.

As far as I can tell, 8-bit images in Fiji / ImageJ are always
pseudo-color. That is, they always have a LUT.

(The code is convoluted, and it is possible that the LUT is
sometimes lazy-initialized, but 8-bit images always have a LUT,
at least logically. For 8-bit images, in the api,
imp.getProcessor().getColorModel()
always returns a ColorModel (LUT), and in the application,
Image>Color>Show LUT always shows a LUT.)

While it is true that I “applied” a LUT in my example, I didn’t need
to. I can get the same results (fake-grayscale and fake-color) by
editing the already-existing LUT:
Image>Color>Edit LUT....

(Again, for all I know the LUT I edit may be lazy-initialized, but
it is, at least, logically already existing.)

Well yes, that’s how it works internally. But saying that the color
palette (LUT) is “only for display” I think overstates the case.
In many cases the color palette is an integral part of the image.
For many of the 8-bit (pseudo)-color images you find on the
internet, nobody cares about the specific values of the “pixels”
(the indices into the palette) – their specific values are an artifact
of the color-quantization algorithm used to reduce some
full-color image to the 8-bit pseudo-color image.

Yes, this is the conclusion I provisionally came to – that the only
difference between a “GRAY8” (“8G”) and a “COLOR_256” (“8C”)
image is whether all of the entries of the (always-existing) LUT
happen to be pure gray.

So I come back to my original question: Does the distinciton
between “8G” and “8C” have any additional meaning in ImageJ
beyond being, in effect, a cached value for isGrayscale()?
(I believe your answer would be no.)

And, if the answer is no, I would say that the ImageJ code is
buggy, in that it doesn’t keep this cached value correctly
synchronized with changes to the LUT that can change
the value of isGrayscale().

Perhaps the ImageProcessor.getType() code:

    /** Returns the current image type (ImagePlus.GRAY8, ImagePlus.GRAY16,
        ImagePlus.GRAY32, ImagePlus.COLOR_256 or ImagePlus.COLOR_RGB).
        @see #getBitDepth
    */
    public int getType() {
        return imageType;
    }

should be changed to something like:

    public int getType() {
        if (imageType == UNDECIDED8) {
            if (lut.isGrayscale())  return GRAY8;
            else  return COLOR_256;
        }
        return imageType;
    }

(and replace most instances of imageType in the rest of the code
with getImageType()).

Yes, I appreciate your analysis.

Thanks, mm


#4

Hey there, this 8G/8C differentiation indeed does not appear to consistently work as one would reasonably expect, and it is likely a bug that you should report. As you’ve found out, there are several components involved in this bookkeeping. I’ve never looked into this, as I only use 8-bit for binary. And you correctly point out that ImageJ does produce a default java.awt.image.IndexColorModel for all 8-bit images that don’t have a LUT using n -> RGB(n,n,n) - so you can call them all “pseudocolor” if you like! Cheers, D