loci.formats.in.OMETiffReader loads different slices than loci.formats.in.TiffReader

and I believe TiffReader is right. The below semi-minimal Beanshell script loads very different stuff depending on which reader I am using. Is this a known issue or something expected or should we dig deeper? I have wasted two days on this already and it would be great if somebody could pick up if this is an actual problem. Mauy be I am just using something the wrong way and can save the time to figure out how to share the tiff in question and other details.

import net.imglib2.RandomAccessibleInterval;
import net.imglib2.img.array.ArrayImgs;
import net.imglib2.view.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import loci.formats.FormatTools;
import loci.formats.IFormatReader;
import loci.formats.ImageReader;
import loci.formats.in.TiffReader;
import ij.*;

RandomAccessibleInterval openSlice(
    IFormatReader reader,
    int slice,
    int width,
    int height) {

  byte[] bytes = new byte[width * height * 2];
  reader.openBytes(slice, bytes, 0, 0, width, height);

  ByteBuffer buffer = ByteBuffer.wrap(bytes);
  short[] shorts = new short[width * height];
  buffer.order(reader.isLittleEndian() ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN);
  buffer.asShortBuffer().get(shorts);
  return ArrayImgs.unsignedShorts(shorts, new long[]{width, height});
}


reader = new ImageReader();
//reader = new TiffReader();
reader.setId("/path/to/ominous/tiff-file.tiff");
width = reader.getSizeX();
height = reader.getSizeY();

IJ.log("" + reader.getReader());
int[] indices = new int[]{1, 7, 11, 13, 16, 20, 25, 30, 32, 39, 43, 46, 48, 53, 59, 61, 67, 70, 73, 77, 81, 87, 89, 94, 97, 101, 106, 109, 114, 116, 121, 124, 131, 132, 139, 143, 145, 149, 155, 157, 160, 165, 169};

list = new ArrayList();

for (i : indices) {
  list.add(openSlice(reader, i, width, height));
}

Views.stack(list);

Under what conditions is OMETiffReader being used, @axtimwalde? For both ImageReader() as well as TiffReader() I would assume it’s not being used due to the file ending (.tiff rather than .ome.tiff). ~J

As in the above script, if I do

reader = new ImageReader();
reader.setId("that.tiff");
IJ.log(reader.getReader());

the result is an OMETiffReader, not a TiffReader, that’s how I found out what is going on. What do you think getReader()should return?

Yeah, @s.besson is telling me I’m wrong, too. :smile: Do you have the file somewhere downloadable?

Yes, the OMETiffReader has been very lenient since its inception so it will detect any TIFF-based file containing valid OME-XML metadata independently of the usage of the official extension. Could you share a sample file to confirm this is the case?

No, it’s massive and not mine. Will check with the owner. I was hoping for a short answer and explanation what I am doing wrong which would have saved me a lot of time.

So, assuming that the selection of the reader may be false, do you think there will be a good reason for reader.openBytes(slice, bytes, 0, 0, width, height); to open different data depending on which tiff reader is used?

What about just the output of tiffinfo? (Stripped of anything that looks too private)

A “good reason” will depend on what’s in the metadata: OME-XML for example is there precisely to define the dimensionality/ordering of the IFDs, but there could be conflicting metadata from another tool.

~J.

Thanks! The file seems to enumerate the position in z,t,c space of every single IFD plane in its meta-data. So e.g. Bioformats can load it in the correct order, fine. Why does that have an effect on reader.openBytes(slice, bytes, 0, 0, width, height); though where slice is documented as “the image index within the file”?

@axtimwalde the openBytes API operates on plane index and the mapping between a set (z, t,c) coordinates and individual IFDs within a TIFF file is defined by the selected reader.

The API documentation is certainly confusing and misleading and I opened https://github.com/ome/bioformats/pull/3613 to update it in the upcoming release. Thank you for pointing this out.

Ok, thanks. What is the linear mapping of the (z,t,c) vector into plane index in either reader (and why is it different?). The pull request does not clarify this, it just replaces words which means that the API method cannot be used. Also, more important, what is the API method to open a plane by IFD, I would jump straight for that because it is simple and leaves no room for interpretation ;).

When all else fails, writing a TIFF parser isn’t too hard for most TIFF files.

Here is a a script to parse IFD entries (one per slice), and here is another that uses the parse_TIFF_IFDs function to load a TIFF file as an ImgLib2 LazyCellImg (see approach #6).

That works for uncompressed and untiled Tiffs only though. I was hoping to use a library that supports more than this most trivial subset of the spec, and so far I find TiffReader doing the job ok. I was just confused about the fact that OMETiffReader establishes a new and undocumented linear order of IFDs instead of the in-file naive order. I still think that this is a bug either in the implementation or in the design, or I do not yet understand why this new order would be better than the other. If this linear order is related to the (c,z,t) coordinates of planes, what is the intended behavior for gaps, and how does that relate to getImageCount()?

Ok, thanks. What is the linear mapping of the (z,t,c) vector into plane index in either reader (and why is it different?).

In the case of the OME-TIFF spec, the order of the planes is determined by the combination of the image dimensions + dimension order metadata as well as the TiffData elements which express the mapping between the planes and the IFDs of the consistuting TIFF file(s). If you have the output of tiffinfo we can probably confirm this order is different from the in-file order.

Also, more important, what is the API method to open a plane by IFD, I would jump straight for that because it is simple and leaves no room for interpretation :wink:
and so far I find TiffReader doing the job ok

Fair enough and with this in mind, we certainly think TiffReader should provide you what you want. Typically in the case of a multi-page TIFF where all planes have identical dimensions, the reader should iterate over the plane in the order of the IFDs.
If you are looking for a higher level of granularity, the TiffParser API is the one that TiffReader delegates to and handles the IFD listing as well as the retrieval of the bytes for a given IFD.

Best,
Sebastien

1 Like