Substract Annotations from each other throws error

Hi Svidro, Hi Pete,

I use a Script from Svidro to substract annotations from each other.
It is throwing this error:
ERROR: Error at line 29: Cannot combine - shapes Area and Area do not share the same image plane

I could not solve this issue since two days.

Do you have an idea why that happens?

import qupath.lib.roi.*
import qupath.lib.objects.*

classToSubtract = “Cell” //Cell is an annotation here

//def topLevel = getObjects{return it.getLevel()==1 && it.isAnnotation()}
def topLevel = getAnnotationObjects().findAll {it.isAnnotation() && it.getPathClass() == getPathClass(“Membrane”) || it.getPathClass() == getPathClass(“Milz”) && it.getROI() instanceof AreaROI} //&& it.getROI() instanceof AreaROI
for (parent in topLevel){

def total = []
def polygons = []
print("1")
//subtractions = parent.getChildObjects().findAll{it.isAnnotation() && it.getPathClass() == getPathClass(classToSubtract)}
subtractions = getAnnotationObjects().findAll{it.isAnnotation() && it.getPathClass() == getPathClass(classToSubtract)}
for (subtractyBit in subtractions){
    if (subtractyBit instanceof AreaROI){
       subtractionROIs = PathROIToolsAwt.splitAreaToPolygons(subtractyBit.getROI())
       total.addAll(subtractionROIs[1])
       print("2")
    } else {total.addAll(subtractyBit.getROI())}              
            
}     
if (parent instanceof AreaROI){
    polygons = PathROIToolsAwt.splitAreaToPolygons(parent.getROI())
    total.addAll(polygons[0])
} else { polygons[1] = parent.getROI()}

        
def newPolygons = polygons[1].collect {
updated = it
for (hole in total)
     updated = PathROIToolsAwt.combineROIs(updated, hole, PathROIToolsAwt.CombineOp.SUBTRACT)
     return updated
}
            // Remove original annotation, add new ones
annotations = newPolygons.collect {new PathAnnotationObject(updated, parent.getPathClass())}


addObjects(annotations)

//removeObjects(subtractions, false)
removeObject(parent, true)

}

Is your image a Z stack?

That would be my first thought. Also, if you are using other scripts to generate or manipulate ROIs prior to this one, they may be applying some default Z value (either 1, 0, or something else). So even if you don’t have a Z stack, the ROIs might be written in such a way as to cause an error if they came from two different scripts.

Other than that, not sure. Haven’t seen it before.

Just tested the script, though I had to edit it a bit since you didn’t catch the whole script in the formatted area. Seems to work on normal ROIs that are hand drawn, so guessing the problem is upstream in another script.

ok, thank you for your idea. How can I find out, if my annotations are in different Z values?

I tried to merge the cell annotations before applying the substract script. Then I have only one Cell annotation and one second annotation.

Same error still happens!

Not sure off the top of my head, would have to post the scripts you are using prior to the subtraction. I almost never use Z stacks, and only figured this out because I was curious what some of the other numbers in scripts generating ROIs were. And there was one script for a Z stack somewhere.

Oh wait, if you cycle through the ROIs specifically, or whatever annotations.getROI(), you could use getZ().

before that I convert detections (Cells) into ROIs by this script:

import qupath.lib.objects.PathAnnotationObject

// Create new annotations with the same ROIs and classifications as the detections
def detections = getDetectionObjects()
def newAnnotations = detections.collect {detection -> new PathAnnotationObject(detection.getROI(), detection.getPathClass())}

// Remove the detections, add the annotations
removeObjects(detections, false)
addObjects(newAnnotations)

the resulting annotation objects are separated from each other.
Also the merging before substraction does not help.
for merging I use this:

selectObjects {it.isAnnotation() && it.getPathClass() == getPathClass(“Cell”)}
mergeSelectedAnnotations();

Hmm, interesting. I get the same thing, and can confirm that the Z value for all of my annotations is the same.

so then it is probably about the conversion process from detection to annotation. Coz I do not have this problem with other annotations.

If Cell Anotations are merged the manual “Substract Annotations” function works fine!

Ah, I think I found it, the channel is being set to -1 for hand drawn objects, while it is 0 for programmatically drawn objects. So a createAllObject() will have a channel of 0, while manually creating a rectangle around the image will have a channel of -1.

I checked Z and T, but those were both equivalent.
image

I am not really sure how to set the C value, though, as imagine it inherits it from the previous object. I am guessing you would need to use a more explicit constructor (if I am even using that terminology correctly). @petebankhead probably knows more.

You might want to modify the script in the reduce annotations thread to do the subtraction that way, which might avoid the entire problem by using Area subtraction rather than ROIs.

**Interesting way of distinguishing if something was drawn manually, might be able to make use of that at some point… dun dun dunnnnn

thanks for digging into it.
Yes, seems to be smart to be able to distinguish manually drawn annotations.

concerning the reduce annotations I did not get, how this method helps with the substraction of many cell rois.
Is it a method that works in qupath 0.12 as well?

It’s more that the script does a lot of ROI subtraction, but handles it through Areas. I am not actually 100% sure it will help, but it’s the best idea I have short of finding a method to alter the channel manually. And I don’t know how to do that.

*Specifically, you would want to replace the ROI subtraction from the above script with the Area subtraction from the reduce annotations script. It would probably be tricky and I don’t have time to dive into it right now.