Skip to content

Instantly share code, notes, and snippets.

@DanaCase
Last active May 6, 2023 09:05
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save DanaCase/9cfc23912fee48e437af03f97763d78e to your computer and use it in GitHub Desktop.
Save DanaCase/9cfc23912fee48e437af03f97763d78e to your computer and use it in GitHub Desktop.
Convert Exported Aperio Image Scope Annotations to QuPath Annotations
import qupath.lib.scripting.QP
import qupath.lib.geom.Point2
import qupath.lib.roi.PolygonROI
import qupath.lib.objects.PathAnnotationObject
import qupath.lib.images.servers.ImageServer
//Aperio Image Scope displays images in a different orientation
def rotated = true
def server = QP.getCurrentImageData().getServer()
def h = server.getHeight()
def w = server.getWidth()
// need to add annotations to hierarchy so qupath sees them
def hierarchy = QP.getCurrentHierarchy()
//Prompt user for exported aperio image scope annotation file
def file = getQuPath().getDialogHelper().promptForFile('xml', null, 'aperio xml file', null)
def text = file.getText()
def list = new XmlSlurper().parseText(text)
list.Annotation.each {
it.Regions.Region.each { region ->
def tmp_points_list = []
region.Vertices.Vertex.each{ vertex ->
if (rotated) {
X = vertex.@Y.toDouble()
Y = h - vertex.@X.toDouble()
}
else {
X = vertex.@X.toDouble()
Y = vertex.@Y.toDouble()
}
tmp_points_list.add(new Point2(X, Y))
}
def roi = new PolygonROI(tmp_points_list)
def annotation = new PathAnnotationObject(roi)
hierarchy.addPathObject(annotation, false)
}
}
@gagarin37
Copy link

Dear DanaCase,

thanks a lot for this srcipt. This imports the annotations very nice.
However I have the same problem as sankrish93. They are all above the image, and rotation seems not to be the case.
I also attach the image.
I would very appreciate if you can give me some ideas how to improve the situation..
screenshot from 2018-04-19 21-57-10

@gagarin37
Copy link

Dear DanaCase,

the rotation seems to be the problem. It seems to be so, that several images go with rotated = "false" and several with "true" and than the correspondence is perfect.

Thank's a lot once more time.

All best.

@JohnKPan
Copy link

JohnKPan commented Jan 14, 2019

I modified this script to import NDPA files from NDPVIEW.

It however involves some really hacky stuff because the NDPA coordinate system is relative to physical slide center, and not top left of scanned image. It works though. Hopefully someone will figure out how to get the necessary information from OpenSlide to make it simpler.

You will need the Groovy-xml.jar as well.

https://groups.google.com/forum/#!searchin/qupath-users/import$20annotation%7Csort:date/qupath-users/xhCx_nhbWQQ/0kW38lEXCAAJ

@SteveBoothHLI
Copy link

SteveBoothHLI commented Nov 20, 2019

Hi there, this is working perfectly for me now! However, in Aperio we have given our annotations names for the classes ("Airway", "Vessel"), these are stored in the xml file under name attributes. Is there a way to automatically set the classes of the imported annotations to match those from the Aperio xml?

Also I noticed that the order of the annotations is different each time I run the import - is this expected?
Thanks,
Steve

@Sabrina1948
Copy link

Hi all,
I have lots of xmls in folder. i want to import them on corresponding images. A batch import i mean. Would please help me in this regard? the script can only import one xml at a time while i got 300+ xmls.
Many thanks in advance.
Sincerely,
Sabrina

@Svidro
Copy link

Svidro commented May 21, 2020

In case @SteveBoothHLI or others want the name import included, Sabrina1948 opened a post on image.sc that ended up resolving the issue.
https://forum.image.sc/t/labelled-annotation-import/37787/11

@narminGhaffari
Copy link

Dear DanaCase,

thanks a lot for this srcipt. This imports the annotations very nice.
However I have the same problem as sankrish93. They are all above the image, and rotation seems not to be the case.
I also attach the image.
I would very appreciate if you can give me some ideas how to improve the situation..
screenshot from 2018-04-19 21-57-10

Hey, did you find any solution to this problem?

@PerfidAlbion
Copy link

I can't launch this script. I've got the following errors :

With Qupath 0.4.3 : ERROR: It looks like you've tried to import a class 'XmlSlurper' that couldn't be found

With Qupath 0.2.3 : ERROR: MissingMethodException at line 20: No signature of method: qupath.lib.gui.QuPathGUI.getDialogHelper() is applicable for argument types: () values: []

Could you help

Thanks

@MichaelSNelson
Copy link

The latter error and the solution can be found on the image.sc forum.

I am not sure about the XmlSlurper though. It is likely due to reorganization of the functions used: https://stackoverflow.com/questions/55169114/unable-to-add-xmlslurper-to-eclipse-groovy-project
https://stackoverflow.com/questions/11245073/groovy-2-cant-find-jsonslurper-and-xmlslurper-anymore
Etc. I am not sure what the exact solution for QuPath will be.

@PerfidAlbion
Copy link

Thanks for the answer. I have managed to make it work :

Here is the script that worked for me :

"import qupath.lib.scripting.QP
import qupath.lib.geom.Point2
import qupath.lib.roi.PolygonROI
import qupath.lib.objects.PathAnnotationObject
import qupath.lib.images.servers.ImageServer

//Aperio Image Scope displays images in a different orientation
def rotated = false

def server = QP.getCurrentImageData().getServer()
def h = server.getHeight()
def w = server.getWidth()

// need to add annotations to hierarchy so qupath sees them
def hierarchy = QP.getCurrentHierarchy()

//Prompt user for exported aperio image scope annotation file
def file = Dialogs.promptForFile('xml', null, 'aperio xml file', null)
def text = file.getText()

def list = new XmlSlurper().parseText(text)

list.Annotation.each {

it.Regions.Region.each { region ->

    def tmp_points_list = []

    region.Vertices.Vertex.each{ vertex ->

        if (rotated) {
            X = vertex.@Y.toDouble()
            Y = h - vertex.@X.toDouble()
        }
        else {
            X = vertex.@X.toDouble()
            Y = vertex.@Y.toDouble()
        }
        tmp_points_list.add(new Point2(X, Y))
    }

    def roi = new PolygonROI(tmp_points_list)

    def annotation = new PathAnnotationObject(roi)

    hierarchy.addObject(annotation, false)
}

}"

I changed the following ::

replace getQuPath().getDialogHelper().promptForFile( with Dialogs.promptForFile(
replace hierarchy.addPathObject(annotation, false) with hierarchy.addObject(annotation, false)
Used version groovy-xml-3.0.17

Hope it helps

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment