Opening next image in the Project

Hi all,

I’m running QuPath (0.2.0-m3) and trying my hand at scripting

What I am attempting to do is create a script that will allow me to manually annotate the regions that I want classified, then run cell detection, classify the results, export the results, and then open the next image in the project

To give me time to annotate I have a dialog box pop up, with the script continuing once I click on it

I can manage to get the script working just fine for single images, but am running into trouble with the Run, run for project (without save) function

Specifically, I need help finding a way to script closing the current image/opening the next image (specifically into the viewer) in the project so that I can create a manual annotation

Here’s the script (very rough version, but seems to work)


import javax.swing.*
def dialog_delay = {
  JFrame jframe = new JFrame()

// Show a dialog asking the user to select a String:
 Object[] possibleValues = [ "Continue"];
 String answer = JOptionPane.showInputDialog(jframe,
             "Hit Continue", "When finished annotating",
             JOptionPane.INFORMATION_MESSAGE, null,
             possibleValues, possibleValues[0]);

  jframe.dispose()
  answer
}

Object userInput = dialog_delay();
if (userInput == null || userInput.equalsIgnoreCase("Cancel") ) {
    println "Stopping..." 
    return
}

println "Continuing..." 

selectAnnotations();
//runs cell detection, with the specified parameters of threshold 10 and min size 50
runPlugin('qupath.imagej.detect.cells.WatershedCellDetection', '{"detectionImageFluorescence": 1,  "backgroundRadius": 15.0,  "medianRadius": 0.0,  "sigma": 3.0,  "minArea": 50.0,  "maxArea": 1000.0,  "threshold": 10.0,  "watershedPostProcess": true,  "cellExpansion": 5.0,  "includeNuclei": true,  "smoothBoundaries": true,  "makeMeasurements": true}');
//Runs the classifier (defines where and which one)
runClassifier('/Users/behrensr/Documents/4 Catagory Classifier.qpclassifier')
//used to PRINT the total number of positive detections (Extra just so that you can double check it worked)
posClass = getPathClass("Positive High Intensity Background")
nPos = 0
for (detection in getDetectionObjects()) {
    pathClass = detection.getPathClass()
    if (posClass.isAncestorOf(pathClass))
      nPos++
}
posClass2 = getPathClass("Positive Low Intensity Background")
for (detection in getDetectionObjects()) {
    pathClass = detection.getPathClass()
    if (posClass2.isAncestorOf(pathClass))
      nPos++
}
print("Number of positive detections: " + nPos)
//Save annotation measurements, to a specified annotation results folder in the project
def name = getProjectEntry().getImageName() + '.txt'
def path = buildFilePath(PROJECT_BASE_DIR, 'annotation results')
mkdirs(path)
path = buildFilePath(path, name)
saveAnnotationMeasurements(path, 'Num Positive High Intensity Background', 'Num Positive Low Intensity Background')
print 'Results exported to ' + pathPreformatted text```

The way I’d approach this is to annotate all the images first (saving the annotation as I go), and then just use Run for project for everything at the end. Is than an option?

Also, in case you haven’t seen it there is some more scripting info at https://petebankhead.github.io/qupath/2019/08/21/scripting-in-v020.html

Thanks for the quick response, that can work
I was just considering if there might be a simple/easy addition to my script that would preform the task for me like a ‘runinViewer’ or ‘openimage’ followed by ‘openViewer’ command or something of the like
If there isn’t I’m happy to pre-make the annotations as suggested

It should be possible (you can open an ImageData from a project entry, and set it in th current viewer), but things get tricky where the UI is involved and it could be harder to maintain and debug.

You might also want to look into changing the use of Swing to JavaFX, although this will require using Platform.runLater(). In the end, UI stuff needs to be handled in a particular thread for both Swing and JavaFX but JavaFX is a bit stricter in forcing the programmer to remember to do it.

So I’ve managed to the the script semi-working, here it is (not cleaned up yet)
The script opens the image into the viewer, allows me time (via dialog box) to annotate, and preforms all the analysis and exporting needed! (With it then repeating for every image in the project)

//this operation permitted on the event thread only; Current Thread rich script editor-1

//Avoids the set image type prompt
setImageType('FLUORESCENCE')
//Need a break here to allow for the placing of the desired annotations
//Note, due to the export method later used the order that the annotations are made is importaint
//Note down what regions are made first, left vs. right, etc. as they are simply saved as PathAnnotationObject when exported

//Load the image into the viewer
//import ij.plugin.filter.RankFilters
//import ij.process.ColorProcessor
//import qupath.lib.images.ImageData;
//import qupath.lib.common.ColorTools
//import qupath.lib.regions.RegionRequest
import qupath.lib.scripting.QP
def imageData = QP.getCurrentImageData()
def server = imageData.getServer()
getCurrentImageData()
getCurrentViewer().setImageData(imageData)

//Show a dialog, allow for time to annotate
import javax.swing.*
def dialog_delay = {
  JFrame jframe = new JFrame()

 Object[] possibleValues = [ "Continue"];
 String answer = JOptionPane.showInputDialog(jframe,
             "Hit Continue", "When finished annotating",
             JOptionPane.INFORMATION_MESSAGE, null,
             possibleValues, possibleValues[0]);

  jframe.dispose()
  answer
}

Object userInput = dialog_delay();
if (userInput == null || userInput.equalsIgnoreCase("Cancel") ) {
    println "Stopping..." 
    return
}

println "Continuing..." 

//Selects all annotations for processing
selectAnnotations();
//runs cell detection, with the specified parameters of threshold 10 and min size 50
runPlugin('qupath.imagej.detect.cells.WatershedCellDetection', '{"detectionImageFluorescence": 1,  "backgroundRadius": 15.0,  "medianRadius": 0.0,  "sigma": 3.0,  "minArea": 50.0,  "maxArea": 1000.0,  "threshold": 10.0,  "watershedPostProcess": true,  "cellExpansion": 5.0,  "includeNuclei": true,  "smoothBoundaries": true,  "makeMeasurements": true}');
//Runs the classifier (defines where and which one)
runClassifier('/Users/behrensr/Documents/4 Catagory Classifier.qpclassifier')
//used to PRINT the total number of positive detections (Extra just so that you can double check it worked)
posClass = getPathClass("Positive High Intensity Background")
nPos = 0
for (detection in getDetectionObjects()) {
    pathClass = detection.getPathClass()
    if (posClass.isAncestorOf(pathClass))
      nPos++
}
posClass2 = getPathClass("Positive Low Intensity Background")
for (detection in getDetectionObjects()) {
    pathClass = detection.getPathClass()
    if (posClass2.isAncestorOf(pathClass))
      nPos++
}
print("Number of positive detections: " + nPos)
//Save annotation measurements, to a specified annotation results folder in the project
def name = getProjectEntry().getImageName() + '.txt'
def path = buildFilePath(PROJECT_BASE_DIR, 'annotation results')
mkdirs(path)
path = buildFilePath(path, name)
saveAnnotationMeasurements(path, 'Num Positive High Intensity Background', 'Num Positive Low Intensity Background')
print 'Results exported to ' + path
//Run the 'merge exported txt results' script after finishing to merge all the exported results together


But… every time I run the script it gives me an extensive log of errors
This is despite the script actually preforming exactly as I want it to on Run->run for project (without save)

Here is the brief error that pops up for each image
"this operation permitted on the event thread only; Current Thread rich script editor-1"
And here is the log (I’ve kept the project size to 1 image to keep the log short…er)

ERROR: QuPath exception
    at com.sun.glass.ui.Application.checkEventThread(Application.java:441)
    at com.sun.glass.ui.Window.setTitle(Window.java:893)
    at com.sun.javafx.tk.quantum.WindowStage.setTitle(WindowStage.java:501)
    at javafx.stage.Stage$5.invalidated(Stage.java:736)
    at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:110)
    at javafx.beans.property.StringPropertyBase$Listener.invalidated(StringPropertyBase.java:231)
    at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:136)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.beans.binding.StringBinding.invalidate(StringBinding.java:169)
    at com.sun.javafx.binding.BindingHelperObserver.invalidated(BindingHelperObserver.java:52)
    at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:136)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(ObjectPropertyBase.java:106)
    at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:113)
    at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:147)
    at qupath.lib.gui.QuPathGUI.fireImageDataChangedEvent(QuPathGUI.java:4524)
    at qupath.lib.gui.QuPathGUI$MultiviewManager.imageDataChanged(QuPathGUI.java:5114)
    at qupath.lib.gui.viewer.QuPathViewer.fireImageDataChanged(QuPathViewer.java:1517)
    at qupath.lib.gui.viewer.QuPathViewer.setImageData(QuPathViewer.java:1478)
    at qupath.lib.gui.viewer.QuPathViewer$setImageData.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:127)
    at Script277.run(Script277.groovy:20)
    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317)
    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:155)
    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$ProjectTask.call(DefaultScriptEditor.java:1287)
    at qupath.lib.gui.scripting.DefaultScriptEditor$ProjectTask.call(DefaultScriptEditor.java:1236)
    at javafx.concurrent.Task$TaskCallable.call(Task.java:1425)
    at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
    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)
ERROR: QuPath exception
    at org.controlsfx.control.Notifications$NotificationPopupHandler.getScreenBounds(Notifications.java:364)
    at org.controlsfx.control.Notifications$NotificationPopupHandler.show(Notifications.java:343)
    at org.controlsfx.control.Notifications.show(Notifications.java:305)
    at org.controlsfx.control.Notifications.showError(Notifications.java:289)
    at qupath.lib.gui.helpers.DisplayHelpers.showErrorNotification(DisplayHelpers.java:457)
    at qupath.lib.gui.helpers.DisplayHelpers.lambda$showErrorNotification$1(DisplayHelpers.java:449)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
    at java.base/java.security.AccessController.doPrivileged(Unknown Source)
    at com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
    at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
    at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
    at java.base/java.lang.Thread.run(Unknown Source)
ERROR: QuPath exception
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:291)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:445)
    at javafx.scene.Parent$3.onProposedChange(Parent.java:473)
    at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
    at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
    at javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:272)
    at javafx.scene.control.skin.LabeledSkinBase.lambda$new$11(LabeledSkinBase.java:220)
    at com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler.lambda$new$1(LambdaMultiplePropertyChangeListenerHandler.java:49)
    at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:86)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:104)
    at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:111)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:145)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:50)
    at javafx.beans.property.StringProperty.setValue(StringProperty.java:65)
    at javafx.scene.control.Labeled.setText(Labeled.java:147)
    at qupath.lib.gui.panels.WorkflowCommandLogView$1$1.updateItem(WorkflowCommandLogView.java:255)
    at qupath.lib.gui.panels.WorkflowCommandLogView$1$1.updateItem(WorkflowCommandLogView.java:250)
    at javafx.scene.control.ListCell.updateItem(ListCell.java:478)
    at javafx.scene.control.ListCell.lambda$new$2(ListCell.java:168)
    at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
    at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329)
    at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
    at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
    at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
    at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
    at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
    at javafx.collections.ModifiableObservableListBase.setAll(ModifiableObservableListBase.java:90)
    at qupath.lib.gui.panels.WorkflowCommandLogView.imageDataChanged(WorkflowCommandLogView.java:450)
    at qupath.lib.gui.QuPathGUI.fireImageDataChangedEvent(QuPathGUI.java:4531)
    at qupath.lib.gui.QuPathGUI$MultiviewManager.imageDataChanged(QuPathGUI.java:5114)
    at qupath.lib.gui.viewer.QuPathViewer.fireImageDataChanged(QuPathViewer.java:1517)
    at qupath.lib.gui.viewer.QuPathViewer.setImageData(QuPathViewer.java:1478)
    at qupath.lib.gui.viewer.QuPathViewer$setImageData.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:127)
    at Script277.run(Script277.groovy:20)
    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317)
    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:155)
    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$ProjectTask.call(DefaultScriptEditor.java:1287)
    at qupath.lib.gui.scripting.DefaultScriptEditor$ProjectTask.call(DefaultScriptEditor.java:1236)
    at javafx.concurrent.Task$TaskCallable.call(Task.java:1425)
    at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
    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)
ERROR: QuPath exception
    at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:291)
    at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:445)
    at javafx.scene.Parent$3.onProposedChange(Parent.java:473)
    at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:113)
    at com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:108)
    at javafx.scene.control.skin.LabeledSkinBase.updateChildren(LabeledSkinBase.java:272)
    at javafx.scene.control.skin.LabeledSkinBase.lambda$new$11(LabeledSkinBase.java:220)
    at com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler.lambda$new$1(LambdaMultiplePropertyChangeListenerHandler.java:49)
    at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:86)
    at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181)
    at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)
    at javafx.beans.property.StringPropertyBase.fireValueChangedEvent(StringPropertyBase.java:104)
    at javafx.beans.property.StringPropertyBase.markInvalid(StringPropertyBase.java:111)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:145)
    at javafx.beans.property.StringPropertyBase.set(StringPropertyBase.java:50)
    at javafx.beans.property.StringProperty.setValue(StringProperty.java:65)
    at javafx.scene.control.Labeled.setText(Labeled.java:147)
    at qupath.lib.gui.panels.WorkflowCommandLogView$1$1.updateItem(WorkflowCommandLogView.java:255)
    at qupath.lib.gui.panels.WorkflowCommandLogView$1$1.updateItem(WorkflowCommandLogView.java:250)
    at javafx.scene.control.ListCell.updateItem(ListCell.java:478)
    at javafx.scene.control.ListCell.lambda$new$2(ListCell.java:168)
    at javafx.collections.WeakListChangeListener.onChanged(WeakListChangeListener.java:88)
    at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329)
    at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)
    at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
    at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
    at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
    at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
    at javafx.collections.ModifiableObservableListBase.setAll(ModifiableObservableListBase.java:90)
    at qupath.lib.gui.panels.WorkflowCommandLogView.imageDataChanged(WorkflowCommandLogView.java:450)
    at qupath.lib.gui.QuPathGUI.fireImageDataChangedEvent(QuPathGUI.java:4531)
    at qupath.lib.gui.QuPathGUI$MultiviewManager.imageDataChanged(QuPathGUI.java:5114)
    at qupath.lib.gui.viewer.QuPathViewer.fireImageDataChanged(QuPathViewer.java:1517)
    at qupath.lib.gui.viewer.QuPathViewer.setImageData(QuPathViewer.java:1478)
    at qupath.lib.gui.viewer.QuPathViewer$setImageData.call(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:47)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:115)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:127)
    at Script277.run(Script277.groovy:20)
    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:317)
    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:155)
    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$ProjectTask.call(DefaultScriptEditor.java:1287)
    at qupath.lib.gui.scripting.DefaultScriptEditor$ProjectTask.call(DefaultScriptEditor.java:1236)
    at javafx.concurrent.Task$TaskCallable.call(Task.java:1425)
    at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
    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)
INFO: Image data set to ImageData: Fluorescence, Phox2bxChR2MouseID_75_1#_10.tif
INFO: Continuing...
INFO: Training size: org.bytedeco.opencv.opencv_core.Size[address=0x2593d94e850,position=0,limit=1,capacity=1,deallocator=org.bytedeco.javacpp.Pointer$NativeDeallocator[ownerAddress=0x2593d94e850,deallocatorAddress=0x7ff9281e6dd0]]
INFO: Responses size: org.bytedeco.opencv.opencv_core.Size[address=0x2593d94e870,position=0,limit=1,capacity=1,deallocator=org.bytedeco.javacpp.Pointer$NativeDeallocator[ownerAddress=0x2593d94e870,deallocatorAddress=0x7ff9281e6dd0]]
INFO: RTrees classifier termination criteria: org.bytedeco.opencv.opencv_core.TermCriteria[address=0x2592fae8a70,position=0,limit=1,capacity=1,deallocator=org.bytedeco.javacpp.Pointer$NativeDeallocator[ownerAddress=0x2592fae8a70,deallocatorAddress=0x7ff93bf719a0]]
INFO: Classifier trained with 148 samples
INFO: Reading classifier qupath.opencv.classify.RTreesClassifier@24adac6d complete!
INFO: Classification time: 0.00 seconds
WARN: No objects classified!
INFO: Number of positive detections: 0
INFO: Results exported to C:\Users\behrensr\Documents\Classifier Creation\annotation results\Phox2bxChR2MouseID_75_1#_10.tif.txt
INFO: Processed 1 images
INFO: Total processing time: 9.54 seconds

Additionally I’ve noticed that running my script adds // to parts of my script (such as import qupath.lib.images.ImageData; -> //import qupath.lib.images.ImageData;) and has added the message //this operation permitted on the event thread only; Current Thread rich script editor-1 to the top of my script
I’m assuming that ‘Thread rich script editor-1’ is the main issue, but I’m having difficulty troubleshooting it

The problem is the one I mentioned at the end of my last post, see Platform.runLater().

A quick, possibly-working approach is to use something like this:

javafx.application.Platform {
   // Code that needs to interact with the GUI
}

But this does mean that any subsequent code in your script will continue to run and won’t wait for this to complete. It can be hard to get it working right.

Also, you should consider changing

import qupath.lib.scripting.QP
def imageData = QP.getCurrentImageData()

to just

def imageData = getCurrentImageData()

or if you want to be more specific

import static qupath.lib.scripting.QPEx.*
def imageData = getCurrentImageData()

See https://github.com/qupath/qupath/wiki/Writing-custom-scripts#technical-notes

As explanation, QP doesn’t know anything about the GUI (including the current viewer) but QPEx does. The current image data is set in QP when the script runs and any changes you make won’t be reflected in it.

When you run a script in the script editor and don’t explicitly state if you want QP or QPEx then it will use QPEx. Therefore

def imageData = QP.getCurrentImageData()
def imageData2 = getCurrentImageData()

don’t necessarily give the same whenever your scripts get more complicated and start opening new images.

In general, I’d recommend sticking with Run for project where suitable so you can use what is already there and often don’t need to worry too much about what is happening in the background.

Hello again,

I managed load the image into the viewer (using run -> for project) using Platform.runLater to fix the errors

def imageData = QP.getCurrentImageData()
def server = imageData.getServer()
getCurrentImageData()
Platform.runLater {
    getCurrentViewer().setImageData(imageData)
}

I’ve cleaned up most of the extra fluff in my script, and managed to get it all working exactly the way I wanted it to, thanks a lot for the help!

Here is the working script in-case anyone else would like to use it

//Avoids the set image type prompt
setImageType('FLUORESCENCE')

//Import statements (probably not all needed)
import qupath.lib.scripting.QP
import javafx.application.Platform
import qupath.lib.images.ImageData
import qupath.lib.images.servers.ConcatChannelsImageServer
import qupath.lib.images.servers.ImageServerProvider
import java.awt.image.BufferedImage
import static qupath.lib.gui.scripting.QPEx.*
import javax.swing.*

//load the current image into the viewer, key is Platform.runLater to avoid errors
def imageData = QP.getCurrentImageData()
def server = imageData.getServer()
getCurrentImageData()
Platform.runLater {
    getCurrentViewer().setImageData(imageData)
}

//Printed line for annotation start
//Note: the order that the annotations are made (if you have multiple) is importaint
println "====================================================" //Just for nice spacing
println "Create your annotations"
println "Hit 'ok' when finished"
println "----------------------------------------------------" //Just for nice spacing

//Define dialog box
def dialog_delay = {
  final JOptionPane pane = new JOptionPane("Press 'ok' after Annotating");
    final JDialog d = pane.createDialog((JFrame)null, "Dialog Delay Box");
    d.setLocation(1750,10);
    d.setVisible(true);
}

//Load dialog box to allow time for annotation
Object userInput = dialog_delay();

//Printed line indicating beginning of processing
println "Initiating processing..." 

//Selects all annotations for processing
selectAnnotations();

//runs cell detection, with the specified parameters of threshold 10 and min size 50
runPlugin('qupath.imagej.detect.cells.WatershedCellDetection', '{"detectionImageFluorescence": 1,  "backgroundRadius": 15.0,  "medianRadius": 0.0,  "sigma": 3.0,  "minArea": 50.0,  "maxArea": 1000.0,  "threshold": 10.0,  "watershedPostProcess": true,  "cellExpansion": 5.0,  "includeNuclei": true,  "smoothBoundaries": true,  "makeMeasurements": true}');

//Runs the classifier (defines where and which one)
runClassifier('/Users/behrensr/Documents/4 Catagory Classifier.qpclassifier')

//used to PRINT the total number of positive detections (Extra just so that you can double check it worked)
posClass = getPathClass("Positive High Intensity Background")
nPos = 0
for (detection in getDetectionObjects()) {
    pathClass = detection.getPathClass()
    if (posClass.isAncestorOf(pathClass))
      nPos++
}
posClass2 = getPathClass("Positive Low Intensity Background")
for (detection in getDetectionObjects()) {
    pathClass = detection.getPathClass()
    if (posClass2.isAncestorOf(pathClass))
      nPos++
}
println "----------------------------------------------------" //Just for nice spacing
println ("Number of positive detections: " + nPos)

//Save annotation measurements, to a specified annotation results folder in the project
def name = getProjectEntry().getImageName() + '.txt'
def path = buildFilePath(PROJECT_BASE_DIR, 'annotation results')
mkdirs(path)
path = buildFilePath(path, name)
saveAnnotationMeasurements(path, 'Num Positive High Intensity Background', 'Num Positive Low Intensity Background')
println 'Results exported to ' + path
println "====================================================" //Just for nice spacing
println "" //Just for nice spacing
println "" //Just for nice spacing
println "" //Just for nice spacing
println "" //Just for nice spacing

//Extra Notes:
//Run the 'merge exported txt results' script after finishing to merge all the exported results together
//If changing ANY directory make sure to double check the pathing in the script
2 Likes

In case it helps, the image prompt can also be avoided in the Preferences, though I suppose if this is intended for new users installing the software on their own, they might not be expected to know to set the preference.

Auto estimate is very good about picking up multichannel images.

Neat script, though. Could definitely be adapted for people who want a pathologist to give manual analysis of a data set. Looking forward to the pixel classifier for the automatic annotation of fluorescent image tissue.