Write Polygon Coordinates to text or csv file

Hello,

I got some .qpdata files from a colleague associated to some whole-slide images in ndpi format, in which he created polygon annotations. Inside of these
polygons he also automatically created rectangular tile objects.
I woul like to create a groovy script to extract only the coordinates of the edge points of the polygons
and save them to a text or csv file. I woul like to have the coordinates as x and y coordinates
of the pixels with the point of origin in the upper left corner with regard to level 0 of the wsi.
Since I am new to groovy and QuPath I am a bit lost here. I have already searched the forum, but could not find anything working with the latest QuPath version.
If someone could help me out or give me a hint in the right direction, that would be great.

Best regards and thanks in advance

Christoph

Hi @Christoph_Neuner,

It sounds like using Json formatting might be more appropriate to export your annotations.

But anyway, if you still want to export them in a csv, here is a small script that would export the annotations’ (your polygons, not tiles) x & y coordinates for the currently opened image to a csv file:

import java.util.stream.Collectors

def output = 'path/to.your/csv/file.csv'
def header = "Annotation ID,nPoints,Points"
def listOfPoints = getAnnotationObjects().stream().map(e -> e.getROI().getAllPoints()).collect(Collectors.toList())

int index = 0
new File(output).withWriter { fw ->
    fw.writeLine(header)
    listOfPoints.forEach { points ->
        fw.write(index++ + "," + points.size() + ",")
        String line = points.stream().map(e -> String.format("(%f,%f)", e.getX(), e.getY())).collect( Collectors.joining(",") )
        fw.writeLine(line)
    }
}

print "Done!"

1 Like

Hi @melvingelbard,

thanks a lot for your quick reply and the script!
You are right. Json formatting would be totally fine for me either and probably the better choice.
Unfortunately the script above does not filter out the tiles. I could filter them out in a next step, by just
deleting all lines where the “nPoints” value is 4, but I guess this would lead to problems for polygons with 4 points.

I tried your linked script for exporting the annotations in a json file.

def annotations = getAnnotationObjects()
boolean prettyPrint = true
def gson = GsonTools.getInstance(prettyPrint)

def path = './N472-14.json'
def file = new File(path)

print gson

file.write(gson.toJson(annotations))

print "Done!"

Unfortunately the gson Object is Null and therefore a NullPointerException is thrown in the
penultimate line.
The problem is I have never worked with groovy before and therefore the language an all the libraries are completely new to me.
If you could help me one more time, I would be very grateful and it would save me so much time.

Thanks in advance and best regards

Christoph

2 Likes

Hi @Christoph_Neuner,

I thought that you need to add the following import at the beginning of your script:

import qupath.lib.io.GsonTools

Or simply do Run > Include default imports in your script editor window.

But it seems like you’ve got that done already? Could you post the script output here to get a more detailed explanation of the Exception? Are you sure it is thrown because the gson object is null? What version of QuPath are you using? Are you sure you have an image opened when running the script?


Ah yes, I assumed that your Tiles were Detections. But if you ticked Make annotation tiles, they will indeed be annotations and will need to be filtered.

When it comes to filtering your annotations, you could use the name automatically generated for the Tiles as a predicate (if you haven’t modified it). For instance, the getAnnotationObjects line could be changed to:

getAnnotationObjects().findAll {it.getName() == null || !it.getName().startsWith("Tile ")}

1 Like

Ah, the null might be caused by this bug:


If so, workarounds are also provided in my linked post.

1 Like

Thanks a lot to you two! You saved me a lot of time and struggle.

Here the two working scripts for future readers of this topic:

To json:

// workaround for a bug in current version: https://forum.image.sc/t/export-import-re-export-annotation-question/44075/2
def defaultColor = getColorRGB(200, 0, 0)
getAnnotationObjects().each {
   if (it.getColorRGB() == null) {
     def newColor = it.getPathClass() == null ? defaultColor : it.getPathClass().getColor()
     it.setColorRGB(newColor)
  }
}
fireHierarchyUpdate()


//filter out Tiles in annotations
def annotations = getAnnotationObjects().findAll {it.getName() == null || !it.getName().startsWith("Tile ")}
boolean prettyPrint = true
def gson = GsonTools.getInstance(prettyPrint)

import org.apache.commons.io.FilenameUtils
//get the wsi name without the type extension
def imageData = QPEx.getCurrentImageData()
def server = imageData.getServer()
String wsi_path = server.getPath()
String wsi_name = FilenameUtils.getBaseName(new File(wsi_path).getName());

def output_path = "./" + wsi_name + ".json"
def file = new File(output_path)

file.write(gson.toJson(annotations))

print "Done!"

and to csv:

import java.util.stream.Collectors
import org.apache.commons.io.FilenameUtils;

//get the wsi name without the type extension
def imageData = QPEx.getCurrentImageData()
def server = imageData.getServer()
String wsi_path = server.getPath()
String wsi_name = FilenameUtils.getBaseName(new File(wsi_path).getName());


def output_path = "./" + wsi_name + ".csv"
def header = "Annotation ID,nPoints,Points"
def listOfPoints = getAnnotationObjects().findAll {it.getName() == null || !it.getName().startsWith("Tile ")}.stream().map(e -> e.getROI().getAllPoints()).collect(Collectors.toList())

int index = 0
new File(output_path).withWriter { fw ->
    fw.writeLine(header)
    listOfPoints.forEach { points ->
        fw.write(index++ + "," + points.size() + ",")
        String line = points.stream().map(e -> String.format("(%f,%f)", e.getX(), e.getY())).collect( Collectors.joining(",") )
        fw.writeLine(line)
    }
}

print "Done!"
3 Likes