Bioformats - Pyramids in OME-TIFF written but not recognized?

Hello,

I’m trying to convert some raw data into a pyramidal OME TIFF format.
I tried to copy and modify the two examples given in the bioformats example repo:

FileExport and GeneratePyramidResolutions

This is the code I finally came up with (it’s not self contained unfortunately).

            ServiceFactory factory = new ServiceFactory();
            OMEXMLService service = factory.getInstance(OMEXMLService.class);
            OMEPyramidStore meta = (OMEPyramidStore) service.createOMEXMLMetadata();
            meta.createRoot();

            // Let's generate the metadata first
            int iImage = 0;
            Source<?> src = srcs.get(iImage);
            if (src.getName()!=null) {
                meta.setImageName(src.getName(), iImage);
            } else {
                meta.setImageName("No Name", iImage);
            }
            meta.setImageID("Image:"+iImage, iImage);
            meta.setPixelsID("Pixels:"+iImage, iImage);

            // specify that the pixel data is stored in big-endian format
            // change 'TRUE' to 'FALSE' to specify little-endian format
            meta.setPixelsBinDataBigEndian(Boolean.TRUE, iImage, 0);

            // specify that the images are stored in ZCT order
            meta.setPixelsDimensionOrder(DimensionOrder.XYZCT, iImage);

            long sizeX = src.getSource(0,0).dimension(0);
            long sizeY = src.getSource(0,0).dimension(1);
            long sizeZ = src.getSource(0,0).dimension(2);

            int type;
            boolean isRGB = false;
            String pt;

            // specify that the pixel type of the images
            if (src.getType() instanceof Volatile) {
                System.err.println("Volatile unsupported");
                cleanup();
                return;
            } else if (src.getType() instanceof UnsignedByteType) {
                type = FormatTools.UINT8;
                pt = FormatTools.getPixelTypeString(type);
                meta.setPixelsType(PixelType.fromString(pt), iImage);
            } else if (src.getType() instanceof UnsignedShortType) {
                type = FormatTools.UINT16;
                pt = FormatTools.getPixelTypeString(type);
                meta.setPixelsType(PixelType.fromString(pt), iImage);
            } else if (src.getType() instanceof ARGBType) {
                type = FormatTools.UINT8;
                pt = FormatTools.getPixelTypeString(type); isRGB=true;
                meta.setPixelsType(PixelType.fromString(pt), iImage);
            } else {
                System.err.println("Pixel type unsupported");
                cleanup();
                return;
            }

            assert sizeX<Integer.MAX_VALUE;
            assert sizeY<Integer.MAX_VALUE;

            // specify the dimensions of the images
            meta.setPixelsSizeX(new PositiveInteger((int)sizeX), iImage);
            meta.setPixelsSizeY(new PositiveInteger((int)sizeY), iImage);
            meta.setPixelsSizeZ(new PositiveInteger((int)sizeZ), iImage);
            meta.setPixelsSizeC(new PositiveInteger(1), iImage);
            meta.setPixelsSizeT(new PositiveInteger(1), iImage);

            // define each channel and specify the number of samples in the channel
            // the number of samples is 3 for RGB images and 1 otherwise
            meta.setChannelID("Channel:"+iImage+":0", iImage, 0);
            meta.setChannelSamplesPerPixel(new PositiveInteger(isRGB?3:1), iImage, 0);

            for (int i=1;i<resolutions;i++) {
                int divScale = (int) Math.pow(scale,i);
                meta.setResolutionSizeX(new PositiveInteger((int) (sizeX/divScale)),iImage,i);
                meta.setResolutionSizeY(new PositiveInteger((int) (sizeY/divScale)),iImage,i);
            }

            writer = new OMETiffWriter();
            writer.setMetadataRetrieve(meta);
            writer.setId(outputFile.getAbsolutePath());

            iImage = 0;
            src = srcs.get(iImage);

            byte[] arrayToSave = SourceToByteArray.raiUnsignedByteTypeToByteArray(
                    (RandomAccessibleInterval<UnsignedByteType>) src.getSource(0,0), new UnsignedByteType()
            );

            writer.setSeries(iImage);
            writer.saveBytes(iImage,arrayToSave);

            IImageScaler scaler = new SimpleImageScaler();

            for (int i=1;i<resolutions;i++) {
                writer.setResolution(i);
                int divScale = (int) Math.pow(scale,i);
                int x = meta.getResolutionSizeX(iImage,i).getValue();
                int y = meta.getResolutionSizeY(iImage,i).getValue();
                System.out.println("i="+i+"; x="+x+"y="+y);
                byte[] downsample = scaler.downsample(arrayToSave,
                        (int) sizeX, (int) sizeY, Math.pow(scale,i),
                        FormatTools.getBytesPerPixel(pt),
                        false,
                        false,
                        1,
                        false);
                writer.saveBytes(iImage, downsample);
            }
            cleanup();
            System.out.println("Done");

Using this code, I managed to save a tiff file boats.ome.tiff (1.4 MB) , but unfortunately, the pyramid is not recognized. If I use in the FIJI BioFormats importer plugin, it’s just opening the highest resolution.

Did I miss something in the writer ? In the metadata ? At least when saving the file, I see that the multiple resolutions are somehow generated, because the line System.out.println("i="+i+"; x="+x+"y="+y); is executed several times with the correct values.

When comparing the ome-xml file of the file I generated and a sample file of pyramidal ome-tiff (https://downloads.openmicroscopy.org/images/OME-TIFF/2016-06/sub-resolutions/Fluorescence/), I see that multiple ‘images’ are present for the example image (one for each resolution).

On the contrary, there’s only one image declared in the file I generated, (even if there’s a field related to the pyramid resolution at the end):

Any help appreciated!

Nicolas

Hi @NicoKiaru,

Rather than using the OMETiffWriter you want to use PyramidOMETiffWriter, alternately just using ImageWriter will detect it automatically and use the correct writer:

PyramidOMETiffWriter writer = new PyramidOMETiffWriter();
or
IFormatWriter writer = new ImageWriter();

I tested the sample code you used with the same sample file and making the above switch should now generate correct pyramids for you.

1 Like

Ok, thank you @dgault !

That was indeed the problem, I should have left ImageWriter and let the library decide by itself.

Now, since you’re here :wink: , I have another little problem: the image size does not vary with the resolution level, and I get a weird ‘direct pyramid output’. Maybe there’s something wrong when setting in the initial metadata ?

            // specify the dimensions of the images
            meta.setPixelsSizeX(new PositiveInteger((int)sizeX), iImage);
            meta.setPixelsSizeY(new PositiveInteger((int)sizeY), iImage);
            meta.setPixelsSizeZ(new PositiveInteger((int)sizeZ), iImage);
            meta.setPixelsSizeC(new PositiveInteger(1), iImage);
            meta.setPixelsSizeT(new PositiveInteger(1), iImage);

Here’s the image I get when opening the file with Bioformats in FIJI ( The image should be like 7x7 pixels normally ):

And a link to the file : boats3.ome.tif (2.3 MB)

Ok that is very odd, Ive been trying to reproduce this for a while now using pretty much identical code (Ive substituted in an imageReader for the input source) and not seeing the same issue. I don’t think its size values actually, if those are incorrect you are more likely to see an exception for incorrect buffer size when writing the data. Also from the metadata it has the correct dimensions for the resolutions listed in the MapAnnotations which means the setResolutionSizeX and setResolutionSizeY values are correct.

Have you made any other changes to original code you posted?

Ok, thanks for digging @dgault . I will try to make a minimal self containing example in the following days.