QuPath multiple channel in separate files - how to merge them?

I have a few .tif files where each one contains a different channel (A, B, C, …). I can open each one of this. Is there a known way to add the other channels?

Some more info:

  1. I can not perform this option via e.g. FIJI since the files are too big and pyramidal.
  2. There is no problem with alignment, registration, etc. These files were exported from SlideScanner software. It was a multichannel fluorescent file.

Thank you for any suggestion or web reference if somebody already solved this issue. I googled, but could not get relevant information.

Cheers,
s/

1 Like

Not officially… not yet.

Although since it is something I’m expecting to need, there is some code in the recent milestone releases that you can try to use (API will no doubt change in the future).

import javafx.application.Platform
import qupath.lib.images.ImageData
import qupath.lib.images.servers.ConcatChannelsImageServer
import qupath.lib.images.servers.ImageServerProvider

import java.awt.image.BufferedImage

import static qupath.lib.gui.scripting.QPEx.*

def paths = [
       '/path/to/first/image',
      '/path/to/second/image'
]

// Build servers for each channel (could be faster if we know which kind of server we need)
def channelServers = paths.collect {
    return ImageServerProvider.buildServer(it, BufferedImage)
}
print channelServers

// Create a concatenated server
def server = new ConcatChannelsImageServer(channelServers[0], channelServers)

// Show the image
def imageData = new ImageData(server)
Platform.runLater {
    getCurrentViewer().setImageData(imageData)
}

Basically, give it the paths of the images in order and QuPath should dynamically concatenate them along the channels dimension.

I don’t have any images like you describe, but I’ve just tried it with a fluorescence whole slide image and it worked fine; I gave it the same 5-channel image twice and got the right 10-channel image out of it. However I also tried it with RGB images unsuccessfully… I guess RGB will need some extra effort.

NDPIS is the only format I know of that outputs images in this way, but the individual channels are (rather oddly?) RGB - and so they didn’t work. But fortunately Bio-Formats handles them fine.

I’ve just fixed a couple of related bugs (although they might not affect you):

Also, if you want to add the image to a project rather than display it in the viewer, you can try this:

import javafx.application.Platform
import qupath.lib.gui.commands.ProjectImportImagesCommand
import qupath.lib.images.ImageData
import qupath.lib.images.servers.ConcatChannelsImageServer
import qupath.lib.images.servers.ImageServerProvider

import java.awt.image.BufferedImage

import static qupath.lib.gui.scripting.QPEx.*

def paths = [
      '/path/to/first/image',
      '/path/to/second/image'
]

// Build servers for each channel (could be faster if we know which kind of server we need)
def channelServers = paths.collect {
    return ImageServerProvider.buildServer(it, BufferedImage)
}

// Create a concatenated server
def server = new ConcatChannelsImageServer(channelServers[0], channelServers)

// Show the image
def imageData = new ImageData(server)
Platform.runLater {
    def project = getProject()
    ProjectImportImagesCommand.addSingleImageToProject(project, server, null)
    project.syncChanges()
    getQuPath().refreshProject()
}
2 Likes

Cool, actually adding images to the project is what I needed (not displaying images). Thank for super quick answer! Much appreciated!

s/

Much as I like my QuPath solution, I wonder whether this is something that could also be done with a pattern file using Bio-Formats:
https://docs.openmicroscopy.org/bio-formats/6.2.1/formats/pattern-file.html

I haven’t used pattern files and may be misunderstanding their purpose, but I would be curious as to whether that’s an alternative.

How could you adapt this to be faster if you know you need BioFormats as your image server?
Still getting my head around the API. Do I need BioFormatsServerBuilder?

Thank you!

Rather than rely on ImageServerProvider to figure out what kind of server to create, you could use BioFormatsImageServer directly. It requires a URI as argument, e.g. something like this:

import qupath.lib.images.servers.bioformats.*

def file = promptForFile()
def server = new BioFormatsImageServer(file.toURI())

I admit the API for creating servers isn’t terribly intuitive… while evolving to handle more complex things (like dynamic channel concatenation) the classes involved got a bit out of hand and it really ought to be streamlined at some point.

1 Like

Thanks! I’ll have to give this a go.

Hi Pete,

I’m trying to import a multi-page .tif image into Qupath. When I do the pages get separated and I get the error message “Failed to load one image”. I’ve been trying out the scripts above, which work well. However, each page is assigned the channel “Channel 1”

Could you please advise what might be causing this to happen or if there is a way to rename the channels by script?

Many thanks,
Kris

1 Like

If you’re using v0.2.0-m8, there should be two tricks available:

  1. In the Brightness/Contrast pane, select all the channels (I mean use Ctrl + A to highlight them all, not necessarily turn them on for display) and use Ctrl + C to copy them. If you edit them in a text editor you can use Ctrl + V to paste the edited names back in (must be in the same format, i.e. one channel name per row of plain text).

  2. Use a script like this (with as many entries as you need):

setChannelNames(
    'Something',
    'Something else',
    'Another')

Note: I have a vague memory of changing channel names and then reopening the image later to find they were back to their defaults… but when I went to investigate this behavior it seemed to be behaving properly, so it might be my memory was faulty. Anyhow, please be on the lookout for weirdness and let me know if you find any.

2 Likes

I have tried this script with ndpis multichannel files and didnt work (Error at line 17: Unable to build ImageServer for file:/C:/path/to/first/image (args=[])
Is it possible to fix it or use an alternative method to merge mutlichannel ndpis?
I am using m9 version. Thanks!

In m9 you should just be able to open the ndpis file directly, no script needed.

1 Like

Hi Peter,
I have tried to make this script work for me, but I really struggle.
Today I managed to merge 3 channels on 1.3G images, but as soon as I add the 4th one I get an error.
The same seems to happen even with 2 channels on bigger images - which are the ones I really need.

Additionally, I can open the single channels of the bigger images (6G) individually, but as soon as I try to open them in a project, it fails to open the highest resolution of the pyramid. I suspect the two are related.
Any advice?

Thank you
log Merge channels.txt (31.1 KB)

@APellicoro it looks like the images aren’t pyramidal, and so at some point running the above scripts results in trying to create a single array that contains more pixels than Java supports.

Therefore it seems you need something that both concatenates channels and converts the image to become pyramidal… all while avoiding any intermediate step that requires a large array (since it will crash at this point). This is pretty challenging, and beyond anything QuPath tries to offer a prepared solution for.

Something like the following might work (replace the similar part of the existing script):

def channelServers = paths.collect {
    return qupath.lib.images.servers.ImageServers.pyramidalize(ImageServerProvider.buildServer(it, BufferedImage))
}

but I’m not sure.

You could also try converting your TIFF images to OME-TIFF first (which will pyramidalize them in QuPath), and then concatenate the channels. QuPath’s command line interface can help with that.

Alternatively, it may be worth exploring if libvips can help: https://libvips.github.io/libvips/
I haven’t used it for this purpose, but it supports image pyramids and has a lot of options.

1 Like

It does make sense, as when I open them, it opens 7 separate images and then asks me if I want to pyramidalize the bigger one.
Thanks, I’ll try both

Just to update you: both ways worked on the smaller images, but for the bigger ones only the second one.
Again, thanks a million!

2 Likes

Does the ‘second one’ mean libvips or converting first to OME-TIFF before merging?

I ask because I’d be interested to find libvips commands that are useful for QuPath users and add them to the documentation somewhere… but I haven’t explored very much myself :slight_smile:

‘Pyramidalize’ will do everything in memory, so I guess it makes sense it fails for anything very big.

yes, sorry. That’s what I mean.
After that, the script for merging the channels was actually very fast

1 Like

Sorry, I completely misunderstood your question. I just exported the images as OME-TIFF. Probably way longer than converting from command line or libvips. I’m going to try both again - would be helpful for next time!

1 Like

@petebankhead When I used your script, it works well on merging them.I merge two RGB images which only respectively have one marker, but I get R,G,B, three channel for one image.So I want to know if we can merge channels so that I can get one image for one channel.
image