Importing annotations

Hi Everyone,

I have a list of x-y coordinates of the boundary of different regions and their name of a WSI.
What is the best way to import these regions as annotations in QuPath keeping the names?
Ideally I would like QuPath to read it from a file, csv etc.
I have seen that GeoJSON could be a nice way QuPath friendly of proceeding (like explained here Drawing custom annotations)… but where can i find the JsonSlurper class?

Thank you for your help !

Hi @TreyRollit,

This script should import GeoJSON format back into QuPath:

import qupath.lib.io.GsonTools


// Instantiate tools
def gson = GsonTools.getInstance(true)

// Prepare template
def type = new com.google.gson.reflect.TypeToken<List<qupath.lib.objects.PathObject>>() {}.getType();
def json_fp = promptForFile(null)

// Read annotations
bufferedReader = new BufferedReader(new FileReader(json_fp))
deserializedAnnotations = gson.fromJson(bufferedReader.text, type)

// Add to dataset
addObjects(deserializedAnnotations)

// Resolve hierarchy
resolveHierarchy()

print "Done!"

I can’t quite remember if names are kept during the import though.

Thank you @melvingelbard !
is it the right template/content which should be in a json file?

{
    "type": "Feature",
    "id": "PathAnnotationObject",
    "geometry": {
      "type": "Polygon",
      "coordinates": [
        [
          [15992, 6551],
          [10451, 18011],
          [16118, 21159],
          [9569, 31485],
          [17503, 24937],
          [37526, 13225],
          [15866, 16625],
          [15992, 6551]
        ]
      ]
    },
    "properties": {
      "name": "ROI1",
      "color": [
        255,
        0,
        0
      ],
      "isLocked": false,
      "measurements": []
    }
  }

I got that from a polygonal annotation created within QuPath:

def annotations = getAnnotationObjects()
def gson = GsonTools.getInstance(true)
def json = gson.toJson(annotations)

println json

That I have naively copy-pasted in a text editor and saved as a *.json file. However when I use your code I have the following error:

ERROR: IllegalStateException at line 13: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $

ERROR: com.google.gson.stream.JsonReader.beginArray(JsonReader.java:351)
com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:80)
com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
com.google.gson.Gson.fromJson(Gson.java:932)
com.google.gson.Gson.fromJson(Gson.java:897)
com.google.gson.Gson.fromJson(Gson.java:846)
com.google.gson.Gson$fromJson$0.call(Unknown Source)
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:148)
Script59.run(Script59.groovy:14)
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.lib.gui.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:859)
qupath.lib.gui.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:782)
qupath.lib.gui.scripting.DefaultScriptEditor$2.run(DefaultScriptEditor.java:1271)
java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
java.base/java.util.concurrent.FutureTask.run(Unknown Source)
java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
java.base/java.lang.Thread.run(Unknown Source)

Do you have any idea? What am I doing wrong?

Thank you!!!

Yes, you need to add [ at the beginning of the file and ] at the end!

Gosh… :slight_smile: I feel stupid!
A big thank you for your help @melvingelbard !!!
It works like a charm now.

1 Like

I believe both are valid GeoJSON :slight_smile: Surrounded by [...] you have an array of objects, otherwise just a single object. But if you just have a single object, then you’d have to change your type to

def type = qupath.lib.objects.PathObject

(Haven’t actually tested this to confirm no other changes are needed… but that’s the right idea anyway)

Thank you @petebankhead for the clarification!