save screenshots of fluorescent image with script

297 views
Skip to first unread message

David

unread,
Aug 7, 2018, 5:42:50 PM8/7/18
to QuPath users

Hi Pete,

you once published a script for screenshots.

I have a FL CZI image. For this image, the script does not work. This message appears:

ERROR: Unable to write C:\Users\...     No compatible writer found.


Do you know the reason? Is it possible to create screenshots for FL Images?


Best
David

This was your script:

/**
 * Export a thumbnail image, with and without an overlay, using QuPath.
 *
 * For tissue microarrays, the scripting code written by the 'File -> Export TMA data'
 * command is probably more appropriate.
 *
 * However, for all other kinds of images where batch export is needed this script can be used.
 *
 * @author Pete Bankhead
 */


import qupath.lib.gui.ImageWriterTools
import qupath.lib.gui.QuPathGUI
import qupath.lib.gui.viewer.OverlayOptions
import qupath.lib.regions.RegionRequest
import qupath.lib.scripting.QPEx

// Aim for an output resolution of approx 0.5 um/pixel
double requestedPixelSize = 0.5

// Create the output directory, if required
def path = QPEx.buildFilePath(QPEx.PROJECT_BASE_DIR, "screenshots")
QPEx.mkdirs(path)

// Get the imageData & server
def imageData = QPEx.getCurrentImageData()
def server = imageData.getServer()

// Get the file name from the current server
def name = server.getShortServerName()

// We need to get the display settings (colors, line thicknesses, opacity etc.) from the current viewer, if available
def overlayOptions = QuPathGUI.getInstance() == null ? new OverlayOptions() : QuPathGUI.getInstance().getViewer().getOverlayOptions()

// Calculate downsample factor depending on the requested pixel size
double downsample = requestedPixelSize / server.getAveragedPixelSizeMicrons()
def request = RegionRequest.createInstance(imageData.getServerPath(), downsample, 0, 0, server.getWidth(), server.getHeight())

// Write output image, with and without overlay
def dir = new File(path)
def fileImage = new File(dir, name + ".jpg")
def img = ImageWriterTools.writeImageRegion(server, request, fileImage.getAbsolutePath())
def fileImageWithOverlay = new File(dir, name + "-overlay.jpg")
ImageWriterTools.writeImageRegionWithOverlay(img, imageData, overlayOptions, request, fileImageWithOverlay.getAbsolutePath())

micros...@gmail.com

unread,
Aug 7, 2018, 6:28:41 PM8/7/18
to QuPath users
I can confirm that the above script does not work for my CZI images either.  It also does not work for my .SCN file, both of which open through BioFormats.  I am wondering if it is more of an issue with files opened by BioFormats needing a different script.

micros...@gmail.com

unread,
Aug 7, 2018, 6:31:21 PM8/7/18
to QuPath users
Nevermind, it also does not work with the Ventana tif files, opened either with Openslide or Bioformats.

micros...@gmail.com

unread,
Aug 7, 2018, 6:33:51 PM8/7/18
to QuPath users
Further edit :(  It DOES work with Openslide on the Ventana tifs, once I downsample enough for it to write out as a JPEG :)  It still fails with BioFormats as the server, so guessing that is the problem again.

Pete

unread,
Aug 8, 2018, 4:56:39 AM8/8/18
to QuPath users
It only works with RGB; the error is because it's not possible to save a JPEG/PNG with the raw pixel values for a general multichannel fluorescence image.

The following modified script will convert the image to RGB during the export, using whatever the current brightness/contrast settings are.

/**
 * Export a thumbnail image, with and without an overlay, using QuPath.
 *
 * For tissue microarrays, the scripting code written by the 'File -> Export TMA data'
 * command is probably more appropriate.
 *
 * However, for all other kinds of images where batch export is needed this script can be used.
 *
 * @author Pete Bankhead
 */





import qupath.lib.gui.ImageWriterTools
import qupath.lib.gui.QuPathGUI
import qupath.lib.gui.viewer.OverlayOptions
import qupath.lib.regions.RegionRequest
import qupath.lib.scripting.QPEx


// Aim for an output resolution of approx 0.5 um/pixel
double requestedPixelSize = 0.5


// Define format
def formatExtensions = [
       
'PNG': '.png',
       
'JPEG': '.jpg'
]
def format = 'PNG'



// Create the output directory, if required
def path = QPEx.buildFilePath(QPEx.PROJECT_BASE_DIR, "screenshots")
QPEx.mkdirs(path)


// Get the imageData & server
def imageData = QPEx.getCurrentImageData()
def server = imageData.getServer()
def viewer = QPEx.getCurrentViewer()



// Get the file name from the current server
def name = server.getShortServerName()


// We need to get the display settings (colors, line thicknesses, opacity etc.) from the current viewer, if available
def overlayOptions = QuPathGUI.getInstance() == null ? new OverlayOptions() : viewer.getOverlayOptions()



// Calculate downsample factor depending on the requested pixel size
double downsample = requestedPixelSize / server.getAveragedPixelSizeMicrons()
def request = RegionRequest.createInstance(imageData.getServerPath(), downsample, 0, 0, server.getWidth(), server.getHeight())


// Write output image, with and without overlay
def dir = new File(path)
def fileImage = new File(dir, name + formatExtensions[format])


def img = server.readBufferedImage(request)
img
= viewer.getImageDisplay().applyTransforms(img, null)


javax
.imageio.ImageIO.write(img, format, fileImage)
def fileImageWithOverlay = new File(dir, name + "-overlay" + formatExtensions[format])

David

unread,
Aug 8, 2018, 6:10:49 AM8/8/18
to QuPath users
the script works perfect! Thank you Pete.

Thomas Kilvær

unread,
Mar 2, 2019, 3:07:28 PM3/2/19
to QuPath users
Hi
Can this be modified to only export the overlay within the ROI of an annotation? I Have tried and failed... but I think my grap of these concepts are a bit vague...

Pete

unread,
Mar 2, 2019, 3:23:18 PM3/2/19
to QuPath users
I'm not sure... I'm also not sure what elements of the overlay you want to be clipped to the ROI and why?  Depending upon what you want it might require delving into painting objects directly... or even, perhaps more troublingly, the heart of Java2D.

Thomas Kilvær

unread,
Mar 3, 2019, 2:58:30 AM3/3/19
to QuPath users
Detections within a specified annotation. Perhaps I need to collect the shapes from the detections/cellobjects and paint them in a new BufferdImage with coordinates starting at bounding box of the desired annotation?

Thomas Kilvær

unread,
Mar 3, 2019, 2:38:24 PM3/3/19
to QuPath users
In case someone needs to do the same as me the following code (blocks of code adapted from Pete) 

import java.awt.Color
import java.awt.image.BufferedImage
import java.awt.image.IndexColorModel
import javax.imageio.ImageIO
import qupath.lib.regions.RegionRequest
import qupath.lib.scripting.QPEx
import static qupath.lib.roi.PathROIToolsAwt.getShape

def server = imageData.getServer()
def path = server.getPath()

def pathOutput = QPEx.buildFilePath(QPEx.PROJECT_BASE_DIR, 'exported_tiles')
QPEx.mkdirs(pathOutput)

def write_image(PathAnnotationObject anno, downsample,server, path, pathOutput, writeOvrl){
    def request = RegionRequest.createInstance(path, downsample, anno.getROI())
    def img = server.readBufferedImage(request)
    ImageIO.write(img, 'PNG', new File(pathOutput, anno.getName()+"_tile.png"))
    if (writeOvrl) {
        def imgOverlay = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_4BYTE_ABGR)
        def g2d = imgOverlay.createGraphics()
        g2d.translate(-request.getX()/downsample, -request.getY()/downsample)
        g2d.scale(1.0/downsample, 1.0/downsample)
        def shapes = []
        def pClass = []
        anno.getChildObjects().each{
            shapes.add(getShape(it.getROI()))
            pClass.add(it.getPathClass().getName())
        }
        i = 0
        shapes.each{shape->
            if(pClass[i]=="Negative"){
                g2d.setColor(new java.awt.Color(0,0,255,127))
            }else if(pClass[i]=="Positive"){
                g2d.setColor(new java.awt.Color(255,0,0,127))
            }else{
                g2d.setColor(new java.awt.Color(255,255,255,127))
            }
            g2d.fill(shape)
            i++
        }
        g2d.setColor(new java.awt.Color(0, 0, 255, 255))
        def nucleus = []
        anno.getChildObjects().each{
            nucleus.add(getShape(it.getNucleusROI()))
        }
        i = 0
        nucleus.each{nuc->
            if(pClass[i]=="Negative"){
                g2d.setColor(new java.awt.Color(0,0,255,255))
            }else if(pClass[i]=="Positive"){
                g2d.setColor(new java.awt.Color(255,0,0,255))
            }else{
                g2d.setColor(new java.awt.Color(255,255,255,255))
            }
            g2d.fill(nuc)
            i++
        }
        g2d.dispose()
        ImageIO.write(imgOverlay, 'PNG', new File(pathOutput, anno.getName()+'_overlay.png'))

    }
}


Reply all
Reply to author
Forward
0 new messages