Error when trying to write OME-TIFF using python-bioformats

Hi guys,

i tried to write an OME-TIFF using python-bioformats but I got stuck. Any help is really appreciated

This is the script:

import bioformats.omexml as ome
import javabridge as jv
import bioformats
import os
import sys
import numpy as np
import bftools as bf

# Write file to disk
def writeOMETIFF(img_XYCZT, path, type='uint16'):

    if verbose:
        print('Dimensions XYCZT: ' + str(np.shape(img_XYCZT)))
        sys.stdout.flush()

    # Get the new dimensions
    SizeX = np.shape(img_XYCZT)[0]
    SizeY = np.shape(img_XYCZT)[1]
    SizeC = np.shape(img_XYCZT)[2]
    SizeZ = np.shape(img_XYCZT)[3]
    SizeT = np.shape(img_XYCZT)[4]

    # Start JVM for bioformats
    bfpackage = r'c:\Users\m1srh\Documents\Software\Bioformats\5.9.2\bioformats_package.jar'
    jars = jv.JARS + [bfpackage]
    jv.start_vm(class_path=jars, run_headless=True, max_heap_size='4G')

    # Getting metadata info
    omexml = ome.OMEXML()
    omexml.image(0).Name = os.path.split(path)[1]
    p = omexml.image(0).Pixels
    assert isinstance(p, ome.OMEXML.Pixels)
    p.SizeX = SizeX
    p.SizeY = SizeY
    p.SizeC = SizeC
    p.SizeT = SizeT
    p.SizeZ = SizeZ
    p.DimensionOrder = ome.DO_XYCZT
    p.PixelType = type
    p.channel_count = SizeC
    p.plane_count = SizeZ
    p.Channel(0).SamplesPerPixel = SizeC
    omexml.structured_annotations.add_original_metadata(ome.OM_SAMPLES_PER_PIXEL, str(SizeC))

    # Converting to omexml
    xml = omexml.to_xml()

    # Write file using Bioformats
    for frame in range(SizeT):

        index = frame
        pixel_buffer = bioformats.formatwriter.convert_pixels_to_buffer(img_XYCZT[:, :, :, :, frame], type)
        script = """
        importClass(Packages.loci.formats.services.OMEXMLService,
                    Packages.loci.common.services.ServiceFactory,
                    Packages.loci.formats.out.TiffWriter);
    
        var service = new ServiceFactory().getInstance(OMEXMLService);
        var metadata = service.createOMEXMLMetadata(xml);
        var writer = new TiffWriter();
        writer.setBigTiff(true);
        writer.setMetadataRetrieve(metadata);
        writer.setId(path);
        writer.setInterleaved(true);
        writer.saveBytes(index, buffer);
        writer.close();
        """
        jv.run_script(script, dict(path=path, xml=xml, index=index, buffer=pixel_buffer))

##################################################################################################################

# Dimension TZCXY
SizeT = 30
SizeZ = 23
SizeC = 2
SizeX = 217
SizeY = 94

output_file = r'stackome.tiff'
img5d = np.random.randn(SizeX, SizeY, SizeC, SizeZ, SizeT).astype(np.uint16)
writeOMETIFF(img5d, output_file, type='uint16', verbose=True)

and the rror message is this:

C:\ProgramData\Anaconda3\python.exe "C:/scripts/ometiff_write3.py"
Dimensions XYCZT: (217, 94, 2, 23, 30)
Writing frames:
[1]
20:47:38.535 [Thread-0] DEBUG loci.common.services.ServiceFactory - Loaded properties from: services.properties
20:47:38.541 [Thread-0] DEBUG loci.common.services.ServiceFactory - Added interface interface loci.formats.services.POIService and implementation class loci.formats.services.POIServiceImpl
20:47:38.542 [Thread-0] DEBUG loci.common.services.ServiceFactory - Added interface interface loci.formats.services.MDBService and implementation class loci.formats.services.MDBServiceImpl
20:47:38.543 [Thread-0] DEBUG loci.common.services.ServiceFactory - Added interface interface loci.formats.services.JPEGTurboService and implementation class loci.formats.services.JPEGTurboServiceImpl
20:47:38.544 [Thread-0] DEBUG loci.common.services.ServiceFactory - Added interface interface ome.codecs.services.LuraWaveService and implementation class ome.codecs.services.LuraWaveServiceImpl
20:47:38.545 [Thread-0] DEBUG loci.common.services.ServiceFactory - Added interface interface loci.formats.services.JAIIIOService and implementation class loci.formats.services.JAIIIOServiceImpl
20:47:38.547 [Thread-0] DEBUG loci.common.services.ServiceFactory - Added interface interface loci.formats.services.WlzService and implementation class loci.formats.services.WlzServiceImpl
20:47:38.548 [Thread-0] DEBUG loci.common.services.ServiceFactory - Added interface interface loci.formats.services.JHDFService and implementation class loci.formats.services.JHDFServiceImpl
20:47:38.549 [Thread-0] DEBUG loci.common.services.ServiceFactory - Added interface interface loci.formats.services.NetCDFService and implementation class loci.formats.services.NetCDFServiceImpl
20:47:38.550 [Thread-0] DEBUG loci.common.services.ServiceFactory - Added interface interface loci.formats.services.EXIFService and implementation class loci.formats.services.EXIFServiceImpl
20:47:38.551 [Thread-0] DEBUG loci.common.services.ServiceFactory - Added interface interface loci.formats.services.MetakitService and implementation class loci.formats.services.MetakitServiceImpl
20:47:38.552 [Thread-0] DEBUG loci.common.services.ServiceFactory - Added interface interface loci.formats.services.LuraWaveService and implementation class loci.formats.services.LuraWaveServiceImpl
20:47:38.553 [Thread-0] DEBUG loci.common.services.ServiceFactory - Added interface interface loci.formats.services.OMEXMLService and implementation class loci.formats.services.OMEXMLServiceImpl
20:47:38.554 [Thread-0] DEBUG loci.common.services.ServiceFactory - Added interface interface ome.codecs.services.JAIIIOService and implementation class ome.codecs.services.JAIIIOServiceImpl
20:47:38.554 [Thread-0] DEBUG loci.common.services.ServiceFactory - Added interface interface loci.formats.services.JPEGXRService and implementation class loci.formats.services.JPEGXRServiceImpl
20:47:38.865 [Thread-0] DEBUG ome.xml.model.primitives.Timestamp - Invalid timestamp '%(DEFAULT_NOW)s'
20:47:39.138 [Thread-0] DEBUG loci.common.NIOByteBufferProvider - Using mapped byte buffer? false
org.mozilla.javascript.WrappedException: Wrapped java.lang.IllegalArgumentException (<java-python-bridge>#12)
	at org.mozilla.javascript.Context.throwAsScriptRuntimeEx(Context.java:1754)
	at org.mozilla.javascript.MemberBox.invoke(MemberBox.java:148)
	at org.mozilla.javascript.NativeJavaMethod.call(NativeJavaMethod.java:225)
	at org.mozilla.javascript.optimizer.OptRuntime.call2(OptRuntime.java:42)
	at org.mozilla.javascript.gen._java_python_bridge__1._c_script_0(<java-python-bridge>:12)
	at org.mozilla.javascript.gen._java_python_bridge__1.call(<java-python-bridge>)
	at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:394)
	at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:3091)
	at org.mozilla.javascript.gen._java_python_bridge__1.call(<java-python-bridge>)
	at org.mozilla.javascript.gen._java_python_bridge__1.exec(<java-python-bridge>)
	at org.mozilla.javascript.Context.evaluateString(Context.java:1079)
Caused by: java.lang.IllegalArgumentException
	at java.nio.Buffer.position(Unknown Source)
	at loci.common.NIOFileHandle.buffer(NIOFileHandle.java:632)
	at loci.common.NIOFileHandle.readLong(NIOFileHandle.java:423)
	at loci.common.RandomAccessInputStream.readLong(RandomAccessInputStream.java:640)
	at loci.formats.tiff.TiffParser.getNextOffset(TiffParser.java:1301)
	at loci.formats.tiff.TiffParser.getIFDOffsets(TiffParser.java:349)
	at loci.formats.out.TiffWriter.saveBytes(TiffWriter.java:448)
	at loci.formats.FormatWriter.saveBytes(FormatWriter.java:123)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.mozilla.javascript.MemberBox.invoke(MemberBox.java:126)
	... 9 more
Traceback (most recent call last):
  File "C:\ProgramData\Anaconda3\lib\site-packages\javabridge\jutil.py", line 384, in run_script
    scope, script, "<java-python-bridge>", 0, None)
  File "C:\ProgramData\Anaconda3\lib\site-packages\javabridge\jutil.py", line 887, in call
    result = fn(*nice_args)
  File "C:\ProgramData\Anaconda3\lib\site-packages\javabridge\jutil.py", line 854, in fn
    raise JavaException(x)
javabridge.jutil.JavaException: Wrapped java.lang.IllegalArgumentException (<java-python-bridge>#12)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:/Users/M1SRH/OneDrive - Carl Zeiss AG/Python_Projects/BioFormatsRead/ometiff_write3.py", line 146, in <module>
    writeOMETIFF(img5d, output_file, type='uint16', verbose=True)
  File "C:/Users/M1SRH/OneDrive - Carl Zeiss AG/Python_Projects/BioFormatsRead/ometiff_write3.py", line 79, in writeOMETIFF
    jv.run_script(script, dict(path=path, xml=xml, index=index, buffer=pixel_buffer))
  File "C:\ProgramData\Anaconda3\lib\site-packages\javabridge\jutil.py", line 394, in run_script
    raise JavaException(call(e.throwable, "unwrap", "()Ljava/lang/Object;"))
javabridge.jutil.JavaException: <Java object at 0x13386790>

Hi guys,

I found at least one issue (silly me). I changed

output_file = r'stackome.tiff'

to

output_file = r'stack.ome.tiff'

and now the error is gone. But now the resulting OME-TIFF has the wrong dimensions. Somehow C and Z dimension get mixed-up, but I do not know why.

It should have:

# Dimension TZCXY
SizeT = 30
SizeZ = 23
SizeC = 2
SizeX = 217
SizeY = 94

but when I open the file in Fiji I get:

BitsPerPixel 16
DimensionOrder XYCZT
IsInterleaved false
IsRGB false
LittleEndian true
PixelType uint16
Series 0 Name stack.ome.tiff
SizeC 46
SizeT 30
SizeX 217
SizeY 94
SizeZ 1
BitsPerSample 16
Compression Uncompressed
ImageLength 94
ImageWidth 217
Location C:\Users\M1SRH\OneDrive - Carl Zeiss AG\Python_Projects\BioFormatsRead\stack.ome.tiff
MetaDataPhotometricInterpretation Monochrome
MetaMorph no
NumberOfChannels 46
PhotometricInterpretation BlackIsZero
PlanarConfiguration Chunky
ResolutionUnit Centimeter
SampleFormat unsigned integer
SamplesPerPixel 46
Software OME Bio-Formats 5.9.2
XResolution 0.0
YResolution 0.0
hyperstack true

1 Like

Hi @sebi06,

first of all, there might be imprecisions here as we are not experts in the python-bioformats implementation. I started testing your script (see gist for the few changes).

A few immediate questions:

  • in the following block
    p.SizeT = SizeT
    p.SizeZ = SizeZ
    p.DimensionOrder = ome.DO_XYCZT
    p.PixelType = type
    p.channel_count = SizeC
    p.plane_count = SizeZ
    p.Channel(0).SamplesPerPixel = SizeC

I understand you trying to write a 5D image with SizeZ Z-sections, SizeT timepoints and 1 channel containing SizeC channel components. Is that the correct representation? Are the p.channel_count and p.plane_count variables mandatory in the python-bioformats implementation as I assume they should be recomputed from the previous dimensions?

  • in the writing block you are looping over each timepoint and writing passing a full 217x94x23x2 buffer to each IFD index. Looking at the output of tiffinfo this results in a value of SamplesPerPixel which is 46. As TIFF is storing individual 2D planes, you might need to loop over SizeT and SizeC.
TIFFReadDirectory: Warning, Sum of Photometric type-related color channels and ExtraSamples doesn't match SamplesPerPixel. Defining non-color channels as ExtraSamples..
TIFF Directory at offset 0x72a9c4 (7514564)
  Image Width: 217 Image Length: 94
  Resolution: 0, 0 pixels/cm
  Bits/Sample: 16
  Sample Format: unsigned integer
  Compression Scheme: None
  Photometric Interpretation: min-is-black
  Samples/Pixel: 46
  Rows/Strip: 1
  Planar Configuration: single image plane
  ImageDescription: ImageJ=
hyperstack=true
images=1380
channels=2
slices=23
frames=30
  Software: OME Bio-Formats 5.9.0
  • finally, a warning that although the filename is .ome.tiff, the code as it stands is using the base TiffWriter and will effectively create a multi-page TIFF.

Best,
Sebastien

1 Like

Hi Sebastien,

Thx. A played around more with various ways to write an OME-TIFF and now the code looks differnent. Is this more “correct”? It is not finished yet … It is still not writing ImageSeries yet …

import numpy as np
import bioformats.omexml as ome
import tifffile
import sys


def writeplanes(pixel, SizeT=1, SizeZ=1, SizeC=1, order='TZCYX', verbose=False):

    if order == 'TZCYX':

        p.DimensionOrder = ome.DO_XYCZT
        counter = 0
        for t in range(SizeT):
            for z in range(SizeZ):
                for c in range(SizeC):

                    if verbose:
                        print('Write PlaneTable: ', t, z, c),
                        sys.stdout.flush()

                    pixel.Plane(counter).TheT = t
                    pixel.Plane(counter).TheZ = z
                    pixel.Plane(counter).TheC = c
                    counter = counter + 1

    return pixel


# Dimension TZCXY
SizeT = 3
SizeZ = 4
SizeC = 2
SizeX = 217
SizeY = 94
Series = 0


scalex = 0.1
scaley = scalex
scalez = 0.5
pixeltype = 'uint16'
dimorder = 'TZCYX'
output_file = r'stack.ome.tiff'

# create numpy array with correct order
img5d = np.random.randn(SizeT, SizeZ, SizeC, SizeY, SizeX).astype(np.uint16)

# Getting metadata info
omexml = ome.OMEXML()
omexml.image(Series).Name = output_file
p = omexml.image(Series).Pixels
#p.ID = 0
p.SizeX = SizeX
p.SizeY = SizeY
p.SizeC = SizeC
p.SizeT = SizeT
p.SizeZ = SizeZ
p.PhysicalSizeX = np.float(scalex)
p.PhysicalSizeY = np.float(scaley)
p.PhysicalSizeZ = np.float(scalez)
p.PixelType = pixeltype
p.channel_count = SizeC
p.plane_count = SizeZ * SizeT * SizeC
p = writeplanes(p, SizeT=SizeT, SizeZ=SizeZ, SizeC=SizeC, order=dimorder)

for c in range(SizeC):
    if pixeltype == 'unit8':
        p.Channel(c).SamplesPerPixel = 1
    if pixeltype == 'unit16':
        p.Channel(c).SamplesPerPixel = 2


omexml.structured_annotations.add_original_metadata(ome.OM_SAMPLES_PER_PIXEL, str(SizeC))

# Converting to omexml
xml = omexml.to_xml()

# write file and save OME-XML as description
tifffile.imwrite(output_file, img5d, metadata={'axes': dimorder}, description=xml)

1 Like

It seems to work fine using the kast script.

1 Like