How to save each core of a TMA as RBG, Hämatoxylin channel and the nuclei segmentation as a mask as individual .png images?

Hi there,

I use a script of Pete, which exports single TMA cores as .png:

import javax.imageio.ImageIO
import qupath.lib.regions.RegionRequest

// Define resolution - 1.0 means full size
double downsample = 1.0

// Create output directory inside the project
def dirOutput = buildFilePath('C:/Users/PabiG/Desktop/TMASingleCores', 'TryToExportAllTMA1')

// Write the cores
def server = getCurrentImageData().getServer()
def path = server.getPath()
for (core in getTMACoreList()){
    // Stop if Run -> Kill running script is pressed   
    if (Thread.currentThread().isInterrupted())
    // Write the image
    img = server.readBufferedImage(RegionRequest.createInstance(path, downsample, core.getROI()))
    ImageIO.write(img, 'PNG', new File(dirOutput, core.getName() + '.png'))

Now, for every TMA core I also would like to export the nuclei segmentation as a binary mask. For this task I also found a script from Pete:

import static qupath.lib.roi.PathROIToolsAwt.getShape;
import java.awt.image.BufferedImage
import java.awt.Color
import javax.imageio.ImageIO

// Get java.awt.Shape objects for each annotation
def shapes = getAnnotationObjects().collect({getShape(it.getROI())})

// Create a grayscale image, here it's 10% of the full image size
double downsample = 10.0
def server = getCurrentImageData().getServer()
int w = (server.getWidth() / downsample) as int
int h = (server.getHeight() / downsample) as int
def img = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY)

// Paint the shapes (this is just 'standard' Java - you might want to modify)
def g2d = img.createGraphics()
g2d.scale(1.0/downsample, 1.0/downsample)
for (shape in shapes)

// Save the result
def outputFile = getQuPath().getDialogHelper().promptToSaveFile("Save binary image", null, null, "PNG", ".png")
ImageIO.write(img, 'PNG', outputFile)
type or paste code here

But I am not able to combine the two scripts in order to do what I want.
Can somebody help me?
Every help is appreciated!


I don’t really do much exporting, but this sounds like the sort of thing the AI export tool will be useful for in the future!
For now though, all I can quickly say is that the two lines of interest are:

    img = server.readBufferedImage(RegionRequest.createInstance(path, downsample, core.getROI()))
    ImageIO.write(img, 'PNG', new File(dirOutput, core.getName() + '.png'))


def img = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_GRAY)

plus everything after that point.
The first takes a small region (the RegionRequest), and uses that to define the borders of the image. The second takes the size of the entire image, and then modifies the g2d based on the desired downsample. You would need to create the buffered image based off of the size and location of the TMA core, while still making it a TYPE_BYTE_GRAY.

Once you have the img set, the rest of the second script may just work as written. Though I am not 100% sure of that due to the coordinates of the shape objects.

Speaking of those, you would also probably want the cells in the particular core you are interested in, so you would want your

def shapes = getAnnotationObjects().collect({getShape(it.getROI())})

to reflect that you only want the cells in that particular TMA core.

Hmm, I almost think it might be easier to create the entire image buffered image and then crop out the various RegeionRequests from that for export… but that is more of an @petebankhead question.

1 Like

I also just came across this when browsing for something else entirely.

1 Like

The source (and explanation) is at

It has been a while since I wrote it & I don’t have time to look into the details right now, but note the line:

// Get the annotations that have ROIs & are have classifications (if required)
def annotations = hierarchy.getFlattenedObjectList(null).findAll {
    it.isAnnotation() && it.hasROI() && (!skipUnclassifiedAnnotations || it.getPathClass() != null) }

You could try changing it.isAnnotation() to become it.isDetection() and see if it’s closer to what you what.