Looking for way to export annotations while preserving original image size

Hello,

First of all, great software, very helpful and streamlines.

I’m looking for a way to export image annotations while preserving the original size of the image. Essentially, I want a binary mask with the ROI selected. I’ve been using the sample script from the tutorial; however, I find that the exported annotations have the borders of the image cut off. The image is only saved to the extent of the annotations. What I want is that “blank space” to be exported as well.

Here is the script:

import qupath.lib.images.servers.LabeledImageServer

def imageData = getCurrentImageData()

// Define output path (relative to project)
def name = GeneralTools.getNameWithoutExtension(imageData.getServer().getMetadata().getName())
def pathOutput = buildFilePath(PROJECT_BASE_DIR, 'export', name)
mkdirs(pathOutput)

// Define output resolution
double requestedPixelSize = 1.0

// Convert to downsample
double downsample = requestedPixelSize

// Create an ImageServer where the pixels are derived from annotations
def labelServer = new LabeledImageServer.Builder(imageData)
    .backgroundLabel(0, ColorTools.WHITE) // Specify background label (usually 0 or 255)
    .downsample(downsample)    // Choose server resolution; this should match the resolution at which tiles are exported
    .addLabel('Tumor', 1)      // Choose output labels (the order matters!)
    .addLabel('Stroma', 2)
    .addLabel('Other', 3)
    .setBoundaryLabel('Boundary*', 4) // Define annotation boundary label
    .multichannelOutput(false) // If true, each label refers to the channel of a multichannel binary image (required for multiclass probability)
    .build()


// Export each region
int i = 0
for (annotation in getAnnotationObjects()) {
    def region = RegionRequest.createInstance(
        labelServer.getPath(), downsample, annotation.getROI())
    i++
    def outputPath = buildFilePath(pathOutput, 'Region ' + i + '.png')
    writeImageRegion(labelServer, region, outputPath)
}

Appreciate any help that anyone can provide!

If you create a whole image annotation, does that make one of the images that is output the entire image?
Objects->Annotations->Create full image annotation
If so, instead of looping through all annotations, you could send that one annotation to the RegionRequest (instead of annotation.getROI()).

Thanks for the reply! I created full image annotation and ran the script again, and it gave me what I needed, which is great. How can I modify the script to only output the one image that I want? I understand that I shouldn’t loop through all the annotations, but I’m not sure how to just access that one annotation that I want.

If your whole image annotation is the only unclassified object, I would go with:

// Export each region

annotation = getAnnotationObjects().findAll{it.getPathClass() == null}[0]
def region = RegionRequest.createInstance(labelServer.getPath(), downsample, annotation.getROI())
def outputPath = buildFilePath(pathOutput, name + '.png')
writeImageRegion(labelServer, region, outputPath)

*have not tested, so please do verify if it works.

Works like a charm! Just one thing to note, it seems I have to zoom out a bit before I create the full image annotation. For example, If I create the full image annotation when, say, “zoom to fit” is selected, a full annotation is not exported for some reason.

But this works great for what I need. Thanks so much for your help!

1 Like

Not sure why that would be, but I’m not great at the image export stuff. Maybe @petebankhead or @melvingelbard would know.

Hi @Dro133, thanks for your nice words about QuPath, glad it’s useful for you!

There’s no need to create a whole image annotation. If it’s possible to export the entire image in one go (i.e. it’s not too big), you can remove the whole chunk of code using annotations at the bottom of the script and replace it with

writeImage(labelServer, pathOutput)

In this case, you’ll want to make sure that your path contains a suitable image name and file extension (e.g. .png), since QuPath will need this to determine the image file format.

2 Likes