Annotations with "Area" or "Geometry" values for "ROI" key

What determines an annotation to be assigned “Area” or “Geometry” values under the “ROI” key?

I am running my script through a batch of WSI, shrinking and expanding annotations (among many other steps) to get my desired ROI, and one of them failed to complete the script. On closer inspection, the script stopped because the DilateAnnotationPlugin cannot be run on “Geometry” annotations.

INFO: Completed with error java.lang.IllegalArgumentException: Operation does not support GeometryCollection arguments
qupath.lib.plugins.objects.DilateAnnotationPlugin  {"radiusMicrons": 50.0,  "lineCap": "Round",  "removeInterior": false,  "constrainToParent": true}

Weirdly, only this one particular WSI resulted in the generation of a “Geometry” annotation, instead of “Area”, after shrinking using DilateAnnotationPlugin but not the others. This meant I could not restore the annotation to its original size by expansion using the same plugin.

It’s a slightly involved story…

In m6, Area is likely to be removed (or retained only so that old files can be opened) and replaced with Geometry only.

Basically, GeometryROIs use Java Topology Suite to represent the region. This is needed for lots of useful spatial processing, including dilation and maintaining a spatial cache to help find objects more quickly, as well as finding distances between objects…

AreaAWT ROIs use java.awt.geom.Area instead, and AreaROI is pretty much the same as AreaAWT except it just stores the vertices (and an AreaROI is converted to an AreaAWT ROI if you start to modify it… or possibly a GeometryROI if you’re using a more recent milestone).

In the end, conversion between ROI representations is needed for different purposes: Java AWT shapes are necessary for painting on screen, JTS Geometries are needed for interesting queries. The difference in how QuPath should choose to represent the ROI comes down to which representation is needed more often.

Previously, QuPath didn’t use JTS at all: the only option was Area. Then in a recent milestone, JTS was introduced and it opens up so many more processing opportunities that I intend to use it more and more. But still if you wanted to save a ROI, it would be converted to an Area to keep backwards-compatibility as much as possible.

The trouble is that converting between a Java AWT shape/area and a JTS Geometry has proven really difficult, error-prone and slow, and has been the source of bugs and performance problems throughout the recent milestones. I reported one of the bugs here but because it remains I’ve had to to write a lot of specific code to try to workaround it.

Java AWT is a lot less strict about some things, so converting the other way (Geometry → Area) is generally much easier and faster.

That is why GeometryROI will replace the other two in m6: it means storing the ROIs in the more strict & useful form, from which other representations can be generated quickly when they are needed. This should considerably improve performance and make it possible to add useful new features to the software to query the data in more interesting ways. This wasn’t an easy decision because it has some unwelcome implications (e.g. it won’t be possible to read GeometryROIs in older versions of QuPath) but I think it’s necessary to finally resolve the problems.

The error you’ve seen is somewhat related, although I haven’t actually seen it before in this context and will need to change some code in QuPath to guard against it. Basically, a Geometry can be composed of points, lines and polygons. Usually in QuPath you only have polygons. But in your case, you’ve somehow ended up with a collection of different kinds of shape (e.g. both polygons and lines). It appears that the dilate command doesn’t support that, and requires them all to be of the same type (e.g. all lines or all polygons).

I hope that makes some kind of sense… the conclusion is: hopefully m6 will resolve this.


Haha, yep. My first thought was the object was constricted down to a point or line.

Hopefully resolved here.


Apologies for the delayed response. Thanks Pete for the in-depth explanation. It seems like it is very possible that I had shrunken an annotation down to a point. I’ll try using your latest commit to see if it’s fixed.

Many thanks,

1 Like