Further details on exporting raw pixel data to OME-TIFF files

This document explains how to export pixel data to OME-TIFF using Bio-Formats version 4.2 and later.

Working example code is provided in FileExport.java - code from that class is referenced here in part. You will need to have bioformats_package.jar in your Java CLASSPATH in order to compile FileExport.java.

The first thing that must happen is we must create the object that stores OME-XML metadata. This is done as follows:

ServiceFactory factory = new ServiceFactory();
OMEXMLService service = factory.getInstance(OMEXMLService.class);
IMetadata omexml = service.createOMEXMLMetadata();

The ‘omexml’ object can now be used in our code to store OME-XML metadata, and by the file format writer to retrieve OME-XML metadata.

Now that we have somewhere to put metadata, we need to populate as much metadata as we can. The minimum amount of metadata required is:

  • endianness of the pixel data

  • the order in which dimensions are stored

  • the bit depth of the pixel data

  • the number of channels

  • the number of timepoints

  • the number of Z sections

  • the width (in pixels) of an image

  • the height (in pixels) of an image

  • the number of samples per channel (3 for RGB images, 1 otherwise)

We populate that metadata as follows:

omexml.setImageID("Image:0", 0);
omexml.setPixelsID("Pixels:0", 0);

// specify that the pixel data is stored in big-endian order
// replace 'TRUE' with 'FALSE' to specify little-endian order
omexml.setPixelsBinDataBigEndian(Boolean.TRUE, 0, 0);

omexml.setPixelsDimensionOrder(DimensionOrder.XYCZT, 0);
omexml.setPixelsType(PixelType.UINT16, 0);
omexml.setPixelsSizeX(new PositiveInteger(width), 0);
omexml.setPixelsSizeY(new PositiveInteger(height), 0);
omexml.setPixelsSizeZ(new PositiveInteger(zSectionCount), 0);
omexml.setPixelsSizeC(new PositiveInteger(channelCount *
samplesPerChannel), 0);
omexml.setPixelsSizeT(new PositiveInteger(timepointCount), 0);

for (int channel=0; channel<channelCount; channel++) {
  omexml.setChannelID("Channel:0:" + channel, 0, channel);
  omexml.setChannelSamplesPerPixel(new PositiveInteger(samplesPerChannel),
    0, channel);
}

Unit<Length> unit = UNITS.MICROMETER;
Length physicalSizeX = new Length(1.0, unit);
Length physicalSizeY = new Length(1.5, unit);
Length physicalSizeZ = new Length(2, unit);
omexml.setPixelsPhysicalSizeX(physicalSizeX, 0);
omexml.setPixelsPhysicalSizeY(physicalSizeY, 0);
omexml.setPixelsPhysicalSizeZ(physicalSizeZ, 0);

There is much more metadata that can be stored; please see the Javadoc for loci.formats.meta.MetadataStore for a complete list.

Now that we have defined all of the metadata, we need to create a file writer:

ImageWriter writer = new ImageWriter();

Now we must associate the ‘omexml’ object with the file writer:

writer.setMetadataRetrieve(omexml);

The writer now knows to retrieve any metadata that it needs from ‘omexml’.

We now tell the writer which file it should write to:

writer.setId("output-file.ome.tiff");

It is critical that the file name given to the writer ends with “.ome.tiff” or “.ome.tif”, as it is the file name extension that determines which format will be written.

Now that everything is set up, we can save the image data. This is done plane by plane, and we assume that the pixel data is stored in a 2D byte array ‘pixelData’:

  int sizeC = omexml.getPixelsSizeC(0).getValue();
  int sizeZ = omexml.getPixelsSizeZ(0).getValue();
  int sizeT = omexml.getPixelsSizeT(0).getValue();
  int samplesPerChannel = omexml.getChannelSamplesPerPixel(0).getValue();
  sizeC /= samplesPerChannel;

  int imageCount = sizeC * sizeZ * sizeT;

  for (int image=0; image<imageCount; image++) {
    writer.saveBytes(image, pixelData[image]);
  }
}

Finally, we must tell the writer that we are finished, so that the output file can be properly closed:

writer.close();

There should now be a complete OME-TIFF file at whichever path was specified above.