Script to select Only Cells in the target ROI/Annotation with a defined name

2,391 views
Skip to first unread message

David Haumann

unread,
Nov 2, 2017, 9:09:02 AM11/2/17
to QuPath users
Dear all, dear Pete,

thank you first, for this unbelievable help - which makes QuPath so brilliant application with up to closely no limits at all :)

Now I face a very tricky task for cell classification. It turned out that we achieve our goal only in combination of Tissue segmentation via Superpixel in a first step and then celldetection and classification in the different ROIs in the second step.

For this, we need to find a way to detect cells only in a selected ROI - which works well with:
 
selectObjects {it.isAnnotation() && it.getPathClass("NameOfROI")};    ==> then runPlugin for Celldetection.

After Celldetection, Statistics need to be generated for subsequent classification.
For this, we need to select the cells.
For this command we know only

"selectDetections();"

Which selects all Cells in all different ROI compartments and will finally classifiy all cells in the whole image with the same classifier. But what we need is to run another classifier for each ROI!

The solution could be: "Select cells only in a ROI with a defined name".

Is there a way to do that with scripting?

Thank you very much for the help.
Best
David


David Haumann

unread,
Nov 2, 2017, 9:21:05 AM11/2/17
to QuPath users
I need to correct myself:
selectObjects { p -> p.getPathClass() == getPathClass("NameOfROI") }

is the script to select ROI specificly. The one above does not work.

micros...@gmail.com

unread,
Nov 2, 2017, 4:35:42 PM11/2/17
to QuPath users
That code will work for the class of the ROI, not the name.   Generally the class name does replace the "Name" field, but that is technically checking something different.
If you want to select the annotations of a specific class,
selectObjects { p -> p.getPathClass() == getPathClass("Stroma") && p.isAnnotation() }

after which you can run the plugin for cell detection.

The only time I have run any kind of classifier on only cells within a specific annotation, I used something like the following:
for (cell in getSelectedObject().getChildObjects())
    cell
.setPathClass(cell.getParent().getPathClass())
fireHierarchyUpdate
()

That specific code sets the cells within an annotation to the parent annotation, but you can also use sets of if statements to handle simple classification. 
If you have a feature that you are interested in, you could place this within the for loop.  You would, of course, set the thresholds and features earlier in the script.

        double val = pathObject.getMeasurementList().getMeasurementValue(feature)
       
// Set positive or negative class
           
if (val < threshold || val > threshold2){
            pathObject
.setPathClass(Positive)
           
}else pathObject.setPathClass(Negative)


You would, in essence, be creating your own classifier.  I am not sure of any way to use the built in classifier on limited annotations within a simple script.



micros...@gmail.com

unread,
Nov 2, 2017, 5:13:14 PM11/2/17
to QuPath users
****** Oh, I didn't read that quite right.  If you just want to select the cells within a specific annotation, all you need to do is check the parent.
selectObjects { p -> p.getParent().getPathClass() == getPathClass("Stroma") && p.isDetection() }
That should select all cells within annotations of class Stroma.



****maybe not relevant
If you need to use the crafted "Create Detection Classifier" on cells within certain annotations, the only other way I can think of is to delete all except one kind of annotation.  Once you only have one annotation type, you run the desired classifier on it, then export the results.  If you want to run an entire slide as a single script, you can then export the results to a folder, then continue running a second copy of the script (pasted below the first), which would then deposit the results into a second folder.  Repeat as many times as you have types of annotations.  You will need to rename and/or be careful with any global variables for the script.

David Haumann

unread,
Nov 2, 2017, 5:38:41 PM11/2/17
to QuPath users
Hello Micros,

thank you for your detailed ideas. I am shure I can use part of the scripts nor or later anyway.
I post a small picture of my situation:


The black cells are classified already. They are in an outer compartment.
Now I selected the green ROI and run a celldetection inside - these are now the unclassified red cells.

In a final step, I want to classify the red cells into 2 categories. But if I select the green ROI or the red cells by  the scritp

selectObjects {it.getPathClass() == null};

I end up with classifying the black cells as well - and of course into a class that I dont want!




That is sad. The combination of Tissuesegmentation via Superpixel with subsequent cell classification seemd to be the solution of a long riddle like stony way.

Your first post sounds like it might be the solution. I try that now.

Auto Generated Inline Image 1
Auto Generated Inline Image 2

David Haumann

unread,
Nov 2, 2017, 5:54:15 PM11/2/17
to QuPath users
I tried the first suggested code.

After
selectObjects { p -> p.getPathClass() == getPathClass("Tissue") };
I end up with selected ROI that contains the red cells:


If i use this script before classification:

for
(cell in getSelectedObject().getChildObjects())
    cell
.setPathClass(cell.getParent().getPathClass())

fireHierarchyUpdate
();

I end up with violet cells again.
Do I need to adapt it to my case?
I also get a Error list:

INFO: Starting script at Thu Nov 02 23:52:44 GMT+01:00 2017
ERROR: Error at line 1: Cannot invoke method getChildObjects() on null object

ERROR: Script error
    at org.codehaus.groovy.runtime.NullObject.invokeMethod(NullObject.java:91)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:48)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.NullCallSite.call(NullCallSite.java:35)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:117)
    at Script16.run(Script16.groovy:2)
    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:343)
    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:152)
    at qupath.lib.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:765)
    at qupath.lib.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:695)
    at qupath.lib.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:677)
    at qupath.lib.scripting.DefaultScriptEditor.access$400(DefaultScriptEditor.java:136)
    at qupath.lib.scripting.DefaultScriptEditor$2.run(DefaultScriptEditor.java:1029)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)



Auto Generated Inline Image 1

Pete

unread,
Nov 2, 2017, 6:18:08 PM11/2/17
to QuPath users
Hopefully the following script helps answer some of the questions above...


import static qupath.lib.classifiers.PathClassifierTools.*
import qupath.lib.objects.PathDetectionObject


// Load two detection classifiers
c1
= loadClassifier(new File('/path/to/classifier1.qpclassifier'))
c2
= loadClassifier(new File('/path/to/classifier2.qpclassifier'))

// Find annotations (could use a predicate)
annotations
= getAnnotationObjects()

// Apply first classifier to all detections inside the first annotation
hierarchy
= getCurrentHierarchy()
cells1
= hierarchy.getDescendantObjects(annotations[0], null, PathDetectionObject)
c1
.classifyPathObjects(cells1)

// Apply the second classifier to all detections inside the second annotation
cells2
= hierarchy.getDescendantObjects(annotations[1], null, PathDetectionObject)
c2
.classifyPathObjects(cells2)

// Set classification for all objects in the third annotation
cells3
= hierarchy.getDescendantObjects(annotations[2], null, PathDetectionObject)
cells3
.each {it.setPathClass(getPathClass('Something else'))}

fireHierarchyUpdate
()

micros...@gmail.com

unread,
Nov 2, 2017, 7:07:41 PM11/2/17
to QuPath users

for (cell in getSelectedObject().getChildObjects())
    cell.setPathClass(cell.getParent().getPathClass())
fireHierarchyUpdate();


This script was just a demonstration of  how to run a for loop on the cells within a specific annotation object class.  The actual function of it is to set all of the cells within the object to the class of the parent object, which is not what you want.  You need to replace

cell.setPathClass(cell.getParent().getPathClass())

with whatever code you want to run on each cell. 

On the upside, Pete's post just showed how to do what I was not sure how to do!
I am still trying to figure out how to get Pete's script to work with multiple annotations of a particular class, but am getting an error where cells1 is null, despite it containing many polygon objects.
println(cells1)
results in INFO: [Polygon, Polygon, Polygon, Polygon, Polygon, etc etc.
and the error:
ERROR: Error at line 18: Cannot invoke method classifyPathObjects() on null object

I will update if I can figure it out!

Sample code modification: (attempting to use annotations directly in ClassifyPathObjects fails, I think because it only expects a single pathObject.  I hoped I could iterate)

import static qupath.lib.classifiers.PathClassifierTools.*
import qupath.lib.objects.PathDetectionObject


// Load classifier
c1 = loadClassifier(new File('C:\\Users\\Svidro\\Desktop\\Endometrium project\\classifiers'))

hierarchy = getCurrentHierarchy();
selectObjects { p -> p.getPathClass() == getPathClass("Stroma") };
annotations = hierarchy.getSelectionModel().getSelectedObjects()
println(annotations)
cells = []
// Apply classifier to all detections inside annotations
for (annotation in annotations){
  cells.addAll(hierarchy.getDescendantObjects(annotation, null, PathDetectionObject));
}
println(cells)
c1.classifyPathObjects(cells);
fireHierarchyUpdate();
println("Done");



Pete

unread,
Nov 2, 2017, 7:10:53 PM11/2/17
to QuPath users
My suspicion is that it's the classifier that is null - the full path is needed to load it successfully (in the code above it looks like only the path to the parent directory is given).

micros...@gmail.com

unread,
Nov 2, 2017, 7:15:39 PM11/2/17
to QuPath users
Yep, just realized that!  And it works perfectly once the classifier is listed correctly with "test.qpclassifier" on the end


micros...@gmail.com

unread,
Nov 2, 2017, 7:26:05 PM11/2/17
to QuPath users
If anyone does want to use that code though, they should probably take out the println statements.  One of those includes every cell that will be analyzed :)
Here is the cleaned up and corrected code for one classifier across multiple annotations of the same class:


import static qupath.lib.classifiers.PathClassifierTools.*
import qupath.lib.objects.PathDetectionObject


// Load detection classifier, make sure to use the full path and file name!  The following is written for a Windows based system
c1
= loadClassifier(new File('C:\\File Path\\test.qpclassifier'))

// Select the annotations that you want to classify the cells from, based on their class.  In this case all "Stroma" annotations

selectObjects
{ p -> p.getPathClass() == getPathClass("Stroma") };

//Store the annotations in "annotations" and create an empty array where you can keep the list of cells you want to classify.
hierarchy
= getCurrentHierarchy();
annotations
= hierarchy.getSelectionModel().getSelectedObjects()
cells
= []
//Iterate over all annotations and keep track of the cells within them in the "cells" list

for (annotation in annotations){
  cells
.addAll(hierarchy.getDescendantObjects(annotation, null, PathDetectionObject));
}
// Apply classifier to all detections inside the selected annotations

David Haumann

unread,
Nov 3, 2017, 5:19:59 AM11/3/17
to QuPath users
Hello Micros and Pete,

I really wonder how you support all the posts here so fast! I am impressed by that :) Thank you so much for that.

I tried the script above to run a classifier in multiple annotations of the same class and I get an error message:

INFO: Starting script at Fri Nov 03 11:10:43 GMT+01:00 2017
INFO: Starting script at Fri Nov 03 11:10:59 GMT+01:00 2017
ERROR: Error at line 4: No signature of method: org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.loadClassifier() is applicable for argument types: (java.io.File) values: [C:\Users\d-hau\Jobs on SSD\third round random images\Projekt 11_SLIC2ROI_2 RIM-Tissue\classifiers\783 TrainingObjects_130params_Classifier Fibroblasts & StromaCells ONLY_Limited2PresentedClasses.qpclassifier]

ERROR: Script error
    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.callGlobal(GroovyScriptEngineImpl.java:415)
    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.access$000(GroovyScriptEngineImpl.java:97)
    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl$2.invokeMethod(GroovyScriptEngineImpl.java:329)
    at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:69)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:52)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:154)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:166)
    at Script33.run(Script33.groovy:5)

    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:343)
    at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:152)
    at qupath.lib.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:765)
    at qupath.lib.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:695)
    at qupath.lib.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:677)
    at qupath.lib.scripting.DefaultScriptEditor.access$400(DefaultScriptEditor.java:136)
    at qupath.lib.scripting.DefaultScriptEditor$2.run(DefaultScriptEditor.java:1029)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
INFO: Starting script at Fri Nov 03 11:13:10 GMT+01:00 2017

Do you have an idea what need to be changed? 
Thanks again for your support!

David Haumann

unread,
Nov 3, 2017, 5:28:54 AM11/3/17
to QuPath users
Another thing:
i found out that there is an option in the "Create detection Classifier" Menu called "Limit to represented classes".
The tooltip shows: "Limit classification to only objects that are unclassified, or have classifications withing the training set. Turn this setting on if you want to ignore objects that have already been classified as something else, rather than classify them."

This is exactly what I need!

If  I select the ROI, activate this checkbox then only the cells in the ROI are classified.
It looks like this:


But - if i save the classifier, and run the script with activation of my ROI and then run the classifier that is saved with activated checkbox, it will NOT work - all cells allover the image are classified again.
So the checkbox activation is not saved with the trained classifier.

Best
David



Auto Generated Inline Image 1

David Haumann

unread,
Nov 3, 2017, 5:45:35 AM11/3/17
to QuPath users
thank you Pete.
I failed to try.
Same Error happens like below.
Any idea why this happens?

INFO: Starting script at Fri Nov 03 11:43:46 GMT+01:00 2017
ERROR: Error at line 1: No signature of method: org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.loadClassifier() is applicable for argument types: (java.io.File) values: [C:\Users\d-hau\Jobs on SSD\third round random images\Projekt 11_SLIC2ROI_2 RIM-Tissue\classifiers\Classifier_130Params_Trophoblasts ONLY.qpclassifier]

micros...@gmail.com

unread,
Nov 3, 2017, 12:49:04 PM11/3/17
to QuPath users
Wow, nice find!  I had not seen that option, but I imagine it is implemented by selecting the cells that are sent to the classifier, much like Pete's script selects the cells within certain annotations.  Since the selection is handled outside the function, you also would need to do that in your script.

micros...@gmail.com

unread,
Nov 3, 2017, 1:04:54 PM11/3/17
to QuPath users
Is there any chance you removed the new File part of the code?

I only get a similar error when I replace:
Right:

c1 = loadClassifier(new File('C:\\File Path\\test.qpclassifier'))

with Wrong:
c1 = loadClassifier('C:\\File Path\\test.qpclassifier')

Note that the new File is missing in the latter.  If that is not the problem, you may want to list your operating system and type of classifier for Pete, and maybe he can help.

Pete

unread,
Nov 3, 2017, 1:52:38 PM11/3/17
to QuPath users
The error message states
No signature of method: org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.loadClassifier() is applicable for argument types
which in this case is a long way of saying that the loadClassifier() method can't be found.

Most likely the import statement at the top of the script is missing, i.e.

import static qupath.lib.classifiers.PathClassifierTools.*

since that's the line that makes the method available.

David Haumann

unread,
Nov 4, 2017, 4:22:23 AM11/4/17
to QuPath users
yes, i selecte the desired cells via scripting and then I run the classifier that was generated with activated checkbox. But it still rund over the whole image - classifying all cells, also the ones that have been classified already.
So in scripting workflow the function does not work.

David Haumann

unread,
Nov 4, 2017, 4:42:02 AM11/4/17
to QuPath users
Trying this script now again seems to work until "Find annotations".

Pete is right - the error message did not appear running the first 5 rows of the script together.
I generated the ROIs in my Slide with "Tiles to annotation" function. And i splitted both different ROI classes into separated ROI: ""splitAnnotations": true}"
In my image is no while slide ROI. Just a lot of ROIs with two different classes. Non is inside another one. They are all "alone", means free standing. No annotation is inside of another annotation.

If I rund the whole Script - just nothing happens.
Probably I need to further define the two different ROI/Annotation classes to adress them?
The names I chose are: "RIM" and "Tissue".

import static qupath.lib.classifiers.PathClassifierTools.*;
import qupath.lib.objects.PathDetectionObject;
// Load two detection classifiers
c1 = loadClassifier(new File('C:\\Users\\d-hau\\Jobs on SSD\\AnaPath\\third round random images\\Projekt 11_SLIC2ROI_2 RIM-Tissue\\classifiers\\Classifier_130Params_Trophoblasts ONLY.qpclassifier'));
c2 = loadClassifier(new File('C:\\Users\\d-hau\\Jobs on SSD\\AnaPath\\third round random images\\Projekt 11_SLIC2ROI_2 RIM-Tissue\\classifiers\\Classifier_130params_783 TrainingObjects_Fibroblasts & StromaCells ONLY.qpclassifier'));

// Find annotations (could use a predicate)
annotations = getAnnotationObjects();
// Apply first classifier to all detections inside the first annotation
hierarchy = getCurrentHierarchy();
cells1 = hierarchy.getDescendantObjects(annotations[0], null, PathDetectionObject);
c1.classifyPathObjects(cells1);
// Apply the second classifier to all detections inside the second annotation
cells2 = hierarchy.getDescendantObjects(annotations[1], null, PathDetectionObject);
c2.classifyPathObjects(cells2);
// Set classification for all objects in the third annotation
cells3 = hierarchy.getDescendantObjects(annotations[2], null, PathDetectionObject);
cells3.each {it.setPathClass(getPathClass('Something else'))};

micros...@gmail.com

unread,
Nov 4, 2017, 7:04:52 AM11/4/17
to QuPath users
Note that Pete's code (what you have listed in your last post) would only be expected to run on 2 of your SLIC annotations (regardless of how many annotations you have total).  So if you are trying to tell if it truly did nothing, you would need to find the two annotations it was supposed to change (annotation[0] and annotation[1] in a list of many annotations).  Also, if you kept the code as is, it would try to actually apply the "Something else" class to every cell in the third annotation.  Probably not what you want.

The reason I edited the code was to make one classifier run across all copies of a particular class of annotation, without affecting cells in other annotations.  I think, based on what I understand of the classifyPathObjects function, that it expects a single annotation so you require a loop for your purposes.

Pete

unread,
Nov 4, 2017, 7:07:07 AM11/4/17
to QuPath users
It looks like the last line of the script is missing:

fireHierarchyUpdate()

That's crucial for everything to update properly and display the changes after you've finished modifying the objects.  It's generally a good idea to call this at the end of any script that modifies individual objects.  In this case, QuPath won't fire an update itself on every change because each update takes at least a few milliseconds... which could be really slow when thousands/millions of objects are involved.

For the annotation selection and classification bit, it might look like this:

def annotations = getAnnotationObjects()
def rim = annotations.findAll {it.getPathClass() == getPathClass("RIM") }
rim
.each {
  cells
= hierarchy.getDescendantObjects(it, null, PathDetectionObject)
  c1
.classifyPathObjects(cells)
}
def tissue = annotations.findAll {it.getPathClass() == getPathClass("Tissue") }
tissue
.each {
  cells
= hierarchy.getDescendantObjects(it, null, PathDetectionObject)
  c2
.classifyPathObjects(cells)
}
fireHierarchyUpdate
()

I haven't actually checked this code - typos are likely, but hopefully it helps.

micros...@gmail.com

unread,
Nov 4, 2017, 2:53:27 PM11/4/17
to QuPath users
That looks much cleaner without diverting through the "select" code!

David Haumann

unread,
Nov 5, 2017, 7:05:20 AM11/5/17
to QuPath users
also with fire hierarchy update nothing happended in the last script.

I comined the definition of c1 and c2 with the last suggestion of Pete:

import static qupath.lib.classifiers.PathClassifierTools.*;
import qupath.lib.objects.PathDetectionObject;
// Load two detection classifiers
c1 = loadClassifier(new File('C:\\Users\\d-hau\\Jobs on SSD\\xx\\third round random images\\Projekt 11_SLIC2ROI_2 RIM-Tissue\\classifiers\\Classifier_130Params_Trophoblasts ONLY.qpclassifier'));
c2 = loadClassifier(new File('C:\\Users\\d-hau\\Jobs on SSD\\xx\\third round random images\\Projekt 11_SLIC2ROI_2 RIM-Tissue\\classifiers\\Classifier_130params_783 TrainingObjects_Fibroblasts & StromaCells ONLY.qpclassifier'));

def annotations = getAnnotationObjects();
def rim = annotations.findAll {it.getPathClass() == getPathClass("RIM") };
rim.each {
  cells = hierarchy.getDescendantObjects(it, null, PathDetectionObject)
  c1.classifyPathObjects(cells)
};
def tissue = annotations.findAll {it.getPathClass() == getPathClass("Tissue") };
tissue.each {
  cells = hierarchy.getDescendantObjects(it, null, PathDetectionObject)
  c2.classifyPathObjects(cells)
};
fireHierarchyUpdate();

Withouth success. It still nearly nothing happens. Do I set the semicolons right?
In one corner of the images some cells became yellow. But not activated. Just yellow classified. Even there was no class that is yellow.


The Error-Message I get is this: somethings wrong in line 8.


INFO: Starting script at Sun Nov 05 13:27:04 GMT+01:00 2017
INFO: Training size: 130x86
INFO: Responses size: 1x86
INFO: RTrees classifier termination criteria: { type: 1, maxCount: 50, epsilon: 0.0}
INFO: Classifier trained with 86 samples
INFO: Reading classifier qupath.opencv.classify.RTreesClassifier@4ba7ae34 complete!
INFO: Training size: 130x783
INFO: Responses size: 1x783
INFO: RTrees classifier termination criteria: { type: 1, maxCount: 50, epsilon: 0.0}
INFO: Classifier trained with 783 samples
INFO: Reading classifier qupath.opencv.classify.RTreesClassifier@300b9d65 complete!
ERROR: Error at line 8: No such property: hierarchy for class: Script51

ERROR: Script error
    at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:53)
    at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:52)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:307)
    at Script51$_run_closure2.doCall(Script51.groovy:10)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
    at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
    at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
    at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1024)
    at groovy.lang.Closure.call(Closure.java:414)
    at groovy.lang.Closure.call(Closure.java:430)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2030)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2015)
    at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2056)
    at org.codehaus.groovy.runtime.dgm$162.invoke(Unknown Source)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:274)
    at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)

    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
    at Script51.run(Script51.groovy:9)
Auto Generated Inline Image 1

micros...@gmail.com

unread,
Nov 5, 2017, 11:19:39 AM11/5/17
to QuPath users
It looks like you are missing the

hierarchy
= getCurrentHierarchy();



line.

Also, if you want to make the code easier to read, you can use the curly brackets button at the end of the Google group forum bar {} with your code selected!

David Haumann

unread,
Nov 7, 2017, 10:34:49 AM11/7/17
to QuPath users
Hi Micros, yes! The script works :-)  Thank you. I am very happy.  It rescued a whole project!



Reply all
Reply to author
Forward
0 new messages