Converting multi-file dataset to ome-tiff

Hi there,

I am trying to convert a dataset to ome-tiff, for which each image file correspond to a single plane.
Some images belong to the same hyperstack/5D-image, and so I am encoding this information in the ome-xml, which is embedded in each header of the converted ome-tiff image as explained in the doc
OME-TIFF specification — OME Data Model and File Formats 5.6.3 documentation (

I am trying to do a proof-of-concept in Fiji, and I managed to have it working when converting a dataset containing a single 5D image.
However, when the dataset contains more than one 5D-image, the ome-tiff are generated but the metadata block is not as expected, and not valid.

Especially some of the first converted ome-tiff (B1-CO4 and B1-CO6) have the full metadata block in their header, but the tiff data is pointing to the wrong file for the last plane (B5), while for the following well positions the TiffData entries are missing.

The other ome.tiff only have a truncated metadata block in the header, hence the lighter file size.

Below is the expected ome-xml metadata generated with the pseudo-code below, before writing the planes to new ome-tiff files.
expectedMetadata.xml (10.6 KB)

def getConvertedPath(originalPath):
	"""OME-converted image in same image directory with identical filename except extension."""
	originalPath.rtrip('tif') + 'ome.tif'

omeMetadata = MetadataTools.createOMEXMLMetadata();

for image in listImages:
	# Populate metadatas
	# Populate custom metadata Instrument, Channels, Pixels etc...
	# See attachedxml for the resulting metadata

# Finally convert images, once metadata block is completed
reader = ImageReader()
writer = OmeTiffWriter()
writer.setMetadataRetrieve(omeMetadata) # associate writer to metadata

listImage.sort() # sort image planes according to chosen DimensionsOrder XYCZT 

# Read/Write planes
firstConvertedPath = getConvertedPath(listImages[0].getPath())
writer.setID(firstConvertedPath) # initialize writer with first image
for image in listImages:
	# Update input/output file
	# Write planes
	for s in reader.getSeriesCount()
		for plane in reader.getImageCount() {
			writer.saveBytes(plane, reader.openBytes(plane)); 

# Finish conversion by closing reader/writer

The iteration order is correct (listImages is sorted like the order of TiffData blocks in the attached xml).

Maybe the fact that I am not setting the UUIDvalue for the individual TiffData but only the UUIDfilename could be the issue ?
With a single 5D image, the OmeTiffWriter seems to handle it correctly though, by automatically adding UUID to the TiffData and using the matching UUID for the root OME element.

I actually tried setting the TiffData UUID, but after conversion they were not the one I set :sweat_smile:

While for the main ome-UUID I also dont know how to update it with setUUID to match the one of the currently converted tiff image.
The snippet below would throw an error, as it’s not possible to update the metadataStore when the writer is already associated (with setId) to the output files.

firstConvertedPath = getConvertedPath(listImages[0].getPath())
writer.setID(firstConvertedPath) # initialize writer with first image

for image in listImages:
   omeMetadata.setUUID(image.gettUUID())    # update main ome-UUID
   writer.setMetadataRetrieve(omeMetadata)  # attempt to "refresh" the associated metadataStore to use the new UUID, throw an error

I think I need the help of an ome-expert :stuck_out_tongue:


I haven’t gone through through the full details yet but a quick initial thought. When writing the planes you are using reader series count to also set the writer series count. As each of the original files is a single plane this series will always be 0 and so the writer is always writing to the first series which is likely why it works for a single 5D image.

Instead the writer series would need to be incremented for each additional 5D image.

1 Like

Hello David,
Thanks for the feedback, I was actually working that on paper right now and I agree that probably the for-loop is wrong.
I was confused between with Series, Planes and Image Count but I think it’s clearer now.
I will try to fix it and let you know how it goes :wink:

Alright I fixed the loop and the conversion worked as expected :smiley:
This is what it looks like in pseudo-code

for image in listImages:
	# Update input/output file
	bytes = reader.openBytes(0) # I have a single plane in my input tif
	# Write planes
	for i in range(omeMetadata.getImageCount()):
		for plane in range(omeMetadata.getPlaneCount(i)): # get number of planes in 5D-image i
			writer.saveBytes(plane, bytes) 

As a reminder for my self and others:

  • a serie is a collection of 2D image-planes belonging to the same 5D-image/hyperstack (ie 1 serie = 1 5D-image)
  • planes in a serie should be ordered according to the DimensionsOrder defined in the Pixels block
  • metadataStore.getImageCount() returns the number of 5D images (ie series) documented in the metadata

The importer in Fiji seems to recognize the dataset correctly and the metadata is also validated by the xmlvalidate tool.

What’s the logic behind the naming of the image window though ?
For some reason, in my case the same filename is used for all hyperstack windows, followed by the ImageName documented in the metadata (this one is correct).

Underlined in blue is the correct part, but otherwise the first part of the title does not really apply, it is related to a specific slice of one of the 5D image.

1 Like

The title bar for the window uses the filename which was selected followed by the series name (which is the correct part you are looking for). There is also the slice label underneath the title and this can be configured in Plugins > Bio-Formats > Bio-Formats Plugins Configuration, that might be an option if you wanted to make the series name more prominent.

1 Like