Mask Script doesn't work in QuPath M4

Hey everyone,

I used to use a script on QuPath (0.1.2) that creates a binary mask of all the detections made in my image. The script is as follows:


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 = getDetectionObjects().collect({getShape(it.getROI())})

// Create a grayscale image, here it’s 10% of the full image size
double downsample = 1.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)
g2d.setColor(Color.WHITE)
for (shape in shapes)
g2d.fill(shape)
g2d.dispose()

// Save the result
def name = getProjectEntry().getImageName() //+ ‘.tiff’
def path = buildFilePath(PROJECT_BASE_DIR, ‘mask’)
mkdirs(path)
def fileoutput = new File( path, name+ ‘-mask.png’)
ImageIO.write(img, ‘PNG’, fileoutput)
println(‘Results exporting…’)


Now I’m trying to use the script on QuPath-m4 but it doesn’t work. The error is:

ERROR: Error: startup failed:
Script1.groovy: 2: unable to resolve class qupath.lib.roi.PathROIToolsAwt
@ line 2, column 1.
import static qupath.lib.roi.PathROIToolsAwt.getShape;
^

1 error

ERROR: Script error
at org.codehaus.groovy.control.ErrorCollector.failIfErrors(ErrorCollector.java:311)
at org.codehaus.groovy.control.CompilationUnit.applyToSourceUnits(CompilationUnit.java:980)
at org.codehaus.groovy.control.CompilationUnit.doPhaseOperation(CompilationUnit.java:647)
at org.codehaus.groovy.control.CompilationUnit.compile(CompilationUnit.java:596)
at groovy.lang.GroovyClassLoader.doParseClass(GroovyClassLoader.java:390)
at groovy.lang.GroovyClassLoader.access$300(GroovyClassLoader.java:89)
at groovy.lang.GroovyClassLoader$5.provide(GroovyClassLoader.java:330)
at groovy.lang.GroovyClassLoader$5.provide(GroovyClassLoader.java:327)
at org.codehaus.groovy.runtime.memoize.ConcurrentCommonCache.getAndPut(ConcurrentCommonCache.java:147)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:325)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:309)
at groovy.lang.GroovyClassLoader.parseClass(GroovyClassLoader.java:251)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.getScriptClass(GroovyScriptEngineImpl.java:331)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:153)
at qupath.lib.gui.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:766)
at qupath.lib.gui.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:696)
at qupath.lib.gui.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:676)
at qupath.lib.gui.scripting.DefaultScriptEditor$2.run(DefaultScriptEditor.java:1033)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)

I’m not sure what changes need to be made for the new version. What should the import functions be instead?

I’ve written about the changes here: https://petebankhead.github.io/qupath/2019/08/21/scripting-in-v020.html

However unless you’re really tied to v0.2.0-m4, I’d recommend v0.2.0-m8 - which will be considerably closer to the final stable release (and has fixed some bugs as well). The same notes on scripting still apply.

Yes yes It works! I’m not too experienced in this script so I’m sure there’s a more efficient way of writing this. Here’s the current code for m8 that works:

import qupath.lib.regions.*
import ij.*
import java.awt.Color
import java.awt.image.BufferedImage
import javax.imageio.ImageIO

// Read RGB image & show in ImageJ (won't work for multichannel!)
double downsample = 1.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)


def g2d = img.createGraphics()
g2d.scale(1.0/downsample, 1.0/downsample)
g2d.setColor(Color.WHITE)
for (detection in getDetectionObjects()) {
  roi = detection.getROI()
  def shape = roi.getShape()
  g2d.fill(shape)

}
g2d.dispose()
new ImagePlus("Mask", img).show()
def name = getProjectEntry().getImageName() //+ '.tiff'
def path = buildFilePath(PROJECT_BASE_DIR, 'mask')
mkdirs(path)
def fileoutput = new File( path, name+ '-mask.png')
ImageIO.write(img, 'PNG', fileoutput)
println('Results exporting...')
2 Likes

Thanks for reporting back! Note that you can format the code in a more readable way using this method: How to put code in a post?

2 Likes