Command line cannot invoke method getImagePlane()

I try to run a script from command line that creates annotations, but no results on the project images. I guess it is because of the following warning message : cannot invoke method getImagePlane(), this method is present in all scripts where I have to create annotations. ( same problem with getCurrentViewer() ). Is there restrictions for cmd to access ImagePlane?
Anyone can help ?

Yasmine

getCurrentViewer() and getImagePlane() are linked to the viewer in the GUI, which doesn’t exist when using the command line interface. These methods should just return null.
You can provide an image or project with the -i (for image) and -p (for project) flags, which will tell QuPath what to apply your script to. But with the CLI you need to find a way to process your images without directly refering to GUI elements.

If you post your script here, we could find a way to make it work (e.g. removing all statements directly refering to the GUI).

2 Likes

Here is a script example :
import qupath.lib.objects.PathObjects
import qupath.lib.roi.ROIs
import qupath.lib.regions.ImagePlane

def imageData = getCurrentImageData()
def plane = getCurrentViewer().getImagePlane()
def server = imageData.getServer()
int tileSize = 1024

def tiles =
for (int y = 0; y < server.getHeight() - tileSize; y += tileSize) {
for (int x = 0; x < server.getWidth() - tileSize; x += tileSize) {
def roi = ROIs.createRectangleROI(x, y, tileSize, tileSize, plane)
tiles << PathObjects.createAnnotationObject(roi)
}
}
addObjects(tiles)

Command line output

17:02:41.945 [main] [INFO ] qupath.ScriptCommand - Setting tile cache size to 16344.00 MB (25.0% max memory)
17:02:42.065 [main] [INFO ] qupath.ScriptCommand - Running script for image.svs
17:02:42.098 [main] [INFO ] q.l.i.s.o.OpenslideServerBuilder - OpenSlide version 3.4.1
17:02:42.439 [main] [WARN ] q.l.i.s.b.BioFormatsImageServer - Temp memoization directory created at C:\Users\usr\AppData\Local\Temp\qupath-memo-11980296580391954284
17:02:42.440 [main] [WARN ] q.l.i.s.b.BioFormatsImageServer - If you want to avoid this warning, either disable Bio-Formats memoization in the preferences or specify a directory to use
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.esotericsoftware.kryo.serializers.FieldSerializer (file:/C:/Users/usr/Desktop/QuPath-0.2.2/app/kryo-2.24.0.jar) to field java.util.regex.Pattern.pattern
WARNING: Please consider reporting this to the maintainers of com.esotericsoftware.kryo.serializers.FieldSerializer
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
17:02:43.452 [main] [WARN ] q.l.i.s.ImageServerMetadata$ImageResolutionLevel - Calculated downsample values differ for x & y for level 4: x=110.18546637744035 and y=110.0 - will use value 110.09273318872017
17:02:43.499 [main] [WARN ] q.l.i.s.ImageServerMetadata$ImageResolutionLevel - Calculated downsample values differ for x & y for level 4: x=110.18546637744035 and y=110.0 - will use value 110.09273318872017
17:02:43.727 [main] [INFO ] qupath.lib.scripting.QP - Initializing type adapters
NullPointerException at line 6: Cannot invoke method getImagePlane() on null object
org.codehaus.groovy.runtime.NullObject.invokeMethod(NullObject.java:91)
org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:44)
org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
org.codehaus.groovy.runtime.callsite.NullCallSite.call(NullCallSite.java:34)
org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:130)
Script1.run(Script1.groovy:7)
org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317)
org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:155)
qupath.lib.gui.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:926)
qupath.ScriptCommand.runScript(QuPath.java:384)
qupath.ScriptCommand.run(QuPath.java:295)
picocli.CommandLine.executeUserObject(CommandLine.java:1839)
picocli.CommandLine.access$1100(CommandLine.java:145)
picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2255)
picocli.CommandLine$RunLast.handle(CommandLine.java:2249)
picocli.CommandLine$RunLast.handle(CommandLine.java:2213)
picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2080)
picocli.CommandLine.execute(CommandLine.java:1978)
qupath.QuPath.main(QuPath.java:182)

Hi @Yasmine can you clarify what exactly you call from the command line, and what exactly is the error message you see?

There is no reference to getImagePlane() in your example script so I’m a bit confused.

Sorry, I posted the wrong script ( even if the that one does not generate any annotation neither). I Just edited the script that corresponds to the error mentioned in the title + the corresponding command line output. @Pete there is no error, only warnings…but still no result on the image.
I use the same command line instruction format to export annotations, and it works, so I don’t think it is related to the format of the instruction.

It can be – the export script doesn’t have to save any changes to the data the data. If you want to do that, you need to use the --save argument, see

I added --save. Nothing changes

The null pointer exception is for the reason @melvingelbard already gave: there is no current viewer if you aren’t running QuPath interactively.

This is a totally separate issue from whether the original script you posted adds an object that is saved or not.

If this isn’t enough to resolve the issues, can you please state exactly the command and script you use, and the error you see?

1 Like

Thanks @melvingelbard and @petebankhead for the clarification about the “* null object*” warning, I understand better now. Well, since it is not the main issue, this is the command that I am running, and one of the scripts to create annotations. You can notice there is no error, but there is nothing on the slide neither:

Command
“QuPath-0.2.2 (console).exe” script -s -i “image.svs” -p Q:\Home\usr\Work\Invasive\project.qpproj Q:/Home/usr/Work\project\CreateBox.groovy

Script

import qupath.lib.objects.PathAnnotationObject
import qupath.lib.objects.classes.PathClassFactory
import qupath.lib.roi.RectangleROI

// Get main data structures
def imageData = QPEx.getCurrentImageData()
def server = imageData.getServer()

int sizePixels = 1000

// Get the current viewer & the location of the pixel currently in the center
def viewer = QPEx.getCurrentViewer()
double cx = viewer.getCenterPixelX()
double cy = viewer.getCenterPixelY()

// Create a new Rectangle ROI
def roi = new RectangleROI(cx-sizePixels/2, cy-sizePixels/2, sizePixels, sizePixels)

// Create & new annotation & add it to the object hierarchy
def annotation = new PathAnnotationObject(roi, PathClassFactory.getPathClass(“Region”))
imageData.getHierarchy().addPathObject(annotation, true)

Command line output
15:49:45.671 [main] [INFO ] qupath.ScriptCommand - Setting tile cache size to 16344.00 MB (25.0% max memory)
15:49:45.789 [main] [INFO ] qupath.ScriptCommand - Running script for image.svs
15:49:45.809 [main] [INFO ] q.l.i.s.o.OpenslideServerBuilder - OpenSlide version 3.4.1
15:49:46.165 [main] [WARN ] q.l.i.s.b.BioFormatsImageServer - Temp memoization directory created at C:\Users\usr\AppData\Local\Temp\qupath-memo-6575884842347574384
15:49:46.165 [main] [WARN ] q.l.i.s.b.BioFormatsImageServer - If you want to avoid this warning, either disable Bio-Formats memoization in the preferences or specify a directory to use
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.esotericsoftware.kryo.serializers.FieldSerializer (file:/C:/Users/usr/Desktop/QuPath-0.2.2/app/kryo-2.24.0.jar) to field java.util.regex.Pattern.pattern
WARNING: Please consider reporting this to the maintainers of com.esotericsoftware.kryo.serializers.FieldSerializer
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
15:49:47.165 [main] [WARN ] q.l.i.s.ImageServerMetadata$ImageResolutionLevel - Calculated downsample values differ for x & y for level 4: x=110.18546637744035 and y=110.0 - will use value 110.09273318872017
15:49:47.205 [main] [WARN ] q.l.i.s.ImageServerMetadata$ImageResolutionLevel - Calculated downsample values differ for x & y for level 4: x=110.18546637744035 and y=110.0 - will use value 110.09273318872017
15:49:47.430 [main] [INFO ] qupath.lib.scripting.QP - Initializing type adapters
NullPointerException at line 18: Cannot invoke method getCenterPixelX() on null object
org.codehaus.groovy.runtime.NullObject.invokeMethod(NullObject.java:91)

  • org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:44)*
  • org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)*
  • org.codehaus.groovy.runtime.callsite.NullCallSite.call(NullCallSite.java:34)*
  • org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)*
  • org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)*
  • org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:130)*
  • Script1.run(Script1.groovy:19)*
  • org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317)*
  • org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:155)*
  • qupath.lib.gui.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:926)*
  • qupath.ScriptCommand.runScript(QuPath.java:384)*
  • qupath.ScriptCommand.run(QuPath.java:295)*
  • picocli.CommandLine.executeUserObject(CommandLine.java:1839)*
  • picocli.CommandLine.access$1100(CommandLine.java:145)*
  • picocli.CommandLine$RunLast.executeUserObjectOfLastSubcommandWithSameParent(CommandLine.java:2255)*
  • picocli.CommandLine$RunLast.handle(CommandLine.java:2249)*
  • picocli.CommandLine$RunLast.handle(CommandLine.java:2213)*
  • picocli.CommandLine$AbstractParseResultHandler.execute(CommandLine.java:2080)*
  • picocli.CommandLine.execute(CommandLine.java:1978)*
  • qupath.QuPath.main(QuPath.java:182)*
    15:49:48.313 [main] [INFO ] qupath.lib.io.PathIO - Writing object hierarchy with 0 object(s)…
    15:49:48.313 [main] [INFO ] qupath.lib.io.PathIO - Image data written in 0.00 seconds

You’re still requesting the current viewer – this won’t work if QuPath hasn’t been opened, therefore there isn’t a current viewer.

The next line fails because the viewer is null (i.e. it’s not available).

What about this script (current viewer not requested) :

Script
import qupath.lib.objects.PathObjects
import qupath.lib.roi.ROIs
import qupath.lib.regions.ImagePlane

int z = 2000
int t = 2000
def plane = ImagePlane.getPlane(z, t)
def roi = ROIs.createRectangleROI(z,t , 1000, 1000, plane)
def annotation = PathObjects.createAnnotationObject(roi)
addObject(annotation)

Output
16:18:29.913 [main] [INFO ] qupath.ScriptCommand - Setting tile cache size to 16344.00 MB (25.0% max memory)
16:18:30.034 [main] [INFO ] qupath.ScriptCommand - Running script for image.svs
16:18:30.054 [main] [INFO ] q.l.i.s.o.OpenslideServerBuilder - OpenSlide version 3.4.1
16:18:30.437 [main] [WARN ] q.l.i.s.b.BioFormatsImageServer - Temp memoization directory created at C:\Users\usr\AppData\Local\Temp\qupath-memo-8371551669557039360
16:18:30.437 [main] [WARN ] q.l.i.s.b.BioFormatsImageServer - If you want to avoid this warning, either disable Bio-Formats memoization in the preferences or specify a directory to use
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.esotericsoftware.kryo.serializers.FieldSerializer (file:/C:/Users/usr/Desktop/QuPath-0.2.2/app/kryo-2.24.0.jar) to field java.util.regex.Pattern.pattern
WARNING: Please consider reporting this to the maintainers of com.esotericsoftware.kryo.serializers.FieldSerializer
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
16:18:31.477 [main] [WARN ] q.l.i.s.ImageServerMetadata$ImageResolutionLevel - Calculated downsample values differ for x & y for level 4: x=110.18546637744035 and y=110.0 - will use value 110.09273318872017
16:18:31.514 [main] [WARN ] q.l.i.s.ImageServerMetadata$ImageResolutionLevel - Calculated downsample values differ for x & y for level 4: x=110.18546637744035 and y=110.0 - will use value 110.09273318872017
16:18:31.719 [main] [INFO ] qupath.lib.scripting.QP - Initializing type adapters
16:18:34.466 [main] [INFO ] qupath.lib.io.PathIO - Writing object hierarchy with 0 object(s)…
16:18:34.468 [main] [INFO ] qupath.lib.io.PathIO - Image data written in 0.01 seconds

You may want to look here:

Not when loading an image! oops

For some code snippets about interacting with the project without accessing the “current” anything. “Current” commands are, I think, relevant to the currently open image. With no open project, you should not have an open image, so they will not work.

Look through your project image list, or cycle through it, and do stuff with the image data from each entry in the list. If you search the forum for those commands, you can find examples of scripts that cycle through the project without dealing with an open file very much.

*though I noticed now that you are passing a specific image to the script, so I guess that handles some of those concerns?

@Yasmine you’re creating a rectangle on the 2000th z-slice and 2000th time-point (as well as with 2000,2000 as the top left coordinate). Unless you have an extremely large image stack, that won’t work.

int z = 2000
int t = 2000
def plane = ImagePlane.getPlane(z, t)
def roi = ROIs.createRectangleROI(z,t , 1000, 1000, plane)

Edit: actually 2001… since the planes start at 0. If you don’t have a z-stack/time-series, use

def plane = ImagePlane.getDefaultPlane()

This script uses Default plane, notice (in bold) that it prints (object created), but again nothing on the slide.

Script :
import qupath.lib.objects.PathObjects
import qupath.lib.roi.ROIs
//import qupath.lib.regions.ImagePlane
import qupath.lib.roi.RectangleROI

//call plane
def plane = ImagePlane.getDefaultPlane()

// Create a new Rectangle ROI
def roi = ROIs.createRectangleROI(2000,2000, 512,512,plane)
def annotation = PathObjects.createAnnotationObject(roi)
addObject(annotation)
print(‘Object created’)

Output:
16:59:12.378 [main] [INFO ] qupath.ScriptCommand - Setting tile cache size to 16344.00 MB (25.0% max memory)
16:59:12.511 [main] [INFO ] qupath.ScriptCommand - Running script for CD3 J04-20356-3H.svs
16:59:12.532 [main] [INFO ] q.l.i.s.o.OpenslideServerBuilder - OpenSlide version 3.4.1
16:59:12.929 [main] [WARN ] q.l.i.s.b.BioFormatsImageServer - Temp memoization directory created at C:\Usersusr\AppData\Local\Temp\qupath-memo-10422427095695583258
16:59:12.929 [main] [WARN ] q.l.i.s.b.BioFormatsImageServer - If you want to avoid this warning, either disable Bio-Formats memoization in the preferences or specify a directory to use
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.esotericsoftware.kryo.serializers.FieldSerializer (file:/C:/Users/usr/Desktop/QuPath-0.2.2/app/kryo-2.24.0.jar) to field java.util.regex.Pattern.pattern
WARNING: Please consider reporting this to the maintainers of com.esotericsoftware.kryo.serializers.FieldSerializer
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
16:59:13.949 [main] [WARN ] q.l.i.s.ImageServerMetadata$ImageResolutionLevel - Calculated downsample values differ for x & y for level 4: x=110.18546637744035 and y=110.0 - will use value 110.09273318872017
16:59:13.985 [main] [WARN ] q.l.i.s.ImageServerMetadata$ImageResolutionLevel - Calculated downsample values differ for x & y for level 4: x=110.18546637744035 and y=110.0 - will use value 110.09273318872017
16:59:14.205 [main] [INFO ] qupath.lib.scripting.QP - Initializing type adapters
Object created16:59:14.990 [main] [INFO ] qupath.lib.io.PathIO - Writing object hierarchy with 0 object(s)…
16:59:14.990 [main] [INFO ] qupath.lib.io.PathIO - Image data written in 0.00 seconds

1 Like

@Research_Associate, not really… It seems to execute correctly, but nothing changes on the slide. Thanks for the reply.

Thanks @Yasmine, that helps reveal the issue – it looks like a bug.

You can work around it by adding the following like to the end of your script

getProjectEntry().saveImageData(getCurrentImageData())

and don’t use the -save option (which looks like it is saving the data that it read… not the data that has been changed).

1 Like

I’ve created an issue on GitHub to track when it’s fixed:

1 Like

works perfectly indeed! Thank you @petebankhead

2 Likes