Create more than series using QuPath

I wonder is there any way to write more than 1 series of Pyramidal Image using QuPath ?

It should be possible, but you’ll need to get into the OMEPyramidWriter class at a lower level.

Here is the starting point:

See that it creates a writer from multiple series. You create each series using a separate Builder (just a few lines further down).


That’s why i did , but it always raises an error " Index 1 out of bounds for length 0 "!

Ok, can you include your script as text so it’s easier to replicate the problem with copy & paste?

1 Like

Nouveau document texte (3).txt (3.1 KB)

The following Groovy script works for me, based upon your code:

import qupath.lib.images.writers.ome.OMEPyramidWriter
import static qupath.lib.gui.scripting.QPEx.*

var server = getCurrentServer()

double scale = 4.0
var output = buildFilePath(PROJECT_BASE_DIR, "test.ome.tif")
int tilesize = 512

var tbuilder = new OMEPyramidWriter.Builder(server)
tbuilder.compression(OMEPyramidWriter.CompressionType.DEFAULT)
tbuilder = tbuilder.scaledDownsampling(server.getDownsampleForResolution(0), scale)
tbuilder = tbuilder.tileSize(tilesize).parallelize(true)

var test = tbuilder.build()

var tbuilder2 = new OMEPyramidWriter.Builder(server)
tbuilder2.compression(OMEPyramidWriter.CompressionType.DEFAULT)
tbuilder2 = tbuilder2.scaledDownsampling(server.getDownsampleForResolution(0), scale)
tbuilder2 = tbuilder2.tileSize(tilesize).parallelize(true)

var test2 = tbuilder2.build()

OMEPyramidWriter.Builder tbuilder3 = new OMEPyramidWriter.Builder(server)
tbuilder3.compression(OMEPyramidWriter.CompressionType.DEFAULT)
tbuilder3 = tbuilder3.scaledDownsampling(server.getDownsampleForResolution(0), scale)
tbuilder3 = tbuilder3.tileSize(tilesize).parallelize(true)

var test3 = tbuilder3.build()

println test3

OMEPyramidWriter w = OMEPyramidWriter.createWriter(test,test2,test3)
long startTime = System.currentTimeMillis();
w.writeImage(output);
long endTime = System.currentTimeMillis();
println String.format("OME TIFF export to " + output + " complete in %.1f seconds", (endTime - startTime)/1000.0)

In testing this, I found a bug described here:

I’ve fixed the bug in this pull request:

However, it seems that in Groovy at least (not sure about Java) using var/def rather than referring to OMEPyramidSeries directly in the variable declaration is enough to get around the problem.

I don’t know if that is the issue you are seeing. If not, can you please share a minimal (non-)working example? I had to edit the code you posted previously / add import statements, which is quite slow and risks that I don’t see the exact problem you are reporting.

Actually I am trying to read exported images from QuPath using OpenSlide , Pyramidal images works fine but when it comes to the function OpenSlide.level_dimensions() it shows that it has one dimension which is the level 0 , even if the image is Pyramidal . That’s why i come up with a solution to write more than one serie of image in order to be able to read all dimensions which it failed .

Same output

Oh, that sounds like a totally different problem from what I’d understood you meant.

The spec for OME pyramids is more recent than OpenSlide: Update on the OME-TIFF pyramidal format · The OME Blog
and therefore I wouldn’t expect OpenSlide to support it.

There are lots of complexities involved that I’m afraid I don’t know enough about, but I know that @jcupitt has done lots of work in supporting OME pyramids in libvips (which can also read images with OpenSlide) and there are numerous relevant discussions across this forum and GitHub, e.g.

Do I understand correctly that your goal is to write an OpenSlide-friendly pyramidal TIFF from QuPath?

If so, I don’t know personally how to do that and I don’t recall seeing a discussion on exactly that. If you don’t necessarily have to write the TIFF from QuPath, then perhaps libvips can already do what you need – or libvips might help with converting a Bio-Formats written TIFF to an OpenSlide-friendly form. However I haven’t tried any of this myself.

I’ve added the bio-formats tag to this thread as well.

Yes, libvips will write openslide-friendly pyramidal TIFFs. For example:

$ vips copy k2.jpg x.tif[tile,pyramid,compression=jpeg]
$ vips openslideload x.tif x.jpg
$ vipsheader k2.jpg x.jpg
k2.jpg: 1450x2048 uchar, 3 bands, srgb, jpegload
x.jpg: 1450x2048 uchar, 3 bands, srgb, jpegload

You can see you get the same image back again.

Converting OME-TIFFs to openslide-compatible TIFFs is a little more complex – you have to convert from plane-separate to plane-interleaved. Is that what you want to do?

1 Like

I found some old demo code for ome → openslide:

#!/usr/bin/python3
  
import sys
import pyvips

# ome images load as a tall, thin strip, with page-height indicating the breaks
image = pyvips.Image.new_from_file(sys.argv[1], n=-1)
page_height = image.get("page-height")

# chop into pages
pages = [image.crop(0, y, image.width, page_height)
         for y in range(0, image.height, page_height)]

# join pages band-wise to make an interleaved image
image = pages[0].bandjoin(pages[1:])

# set the rgb hint
image = image.copy(interpretation="srgb")

image.write_to_file(sys.argv[2])

You can run it like this:

$ ./ome2vips.py ~/pics/ome/multi-channel.ome.tiff x.tif[pyramid,tile,compression=jpeg]
$ vips openslideload x.tif x.png

To make:

x

That’s assuming you are dealing with colour images. It’s simpler if you only have one band.

1 Like

Actually the image was read successfully from QuPath , but it cant detect dimensions of this image, and as i guess due to lack of metadata .
ps: If the image is not pyramidal i wont be able to read it using openslide.

The code I posted was to turn an OME-TIFF (eg. one written by QuPath) into a TIFF that can be read by openslide.

Is that not what you needed?

1 Like