Manual annotation and measurements


Dear Qupath users,

I am not that skilled yet with qupath, but I have a question because for the measurements I have to do the classification is no option.

What I want to do is:

  1. Detect the tissue ( no problem with the simple tissue detection option)
  2. Mark the areas of peritumoral and tumor tissue (orange and yellow areas in the picture) manually.
  3. Count the nerves manually using the point tool in the total tissue


  1. areas: total tissue, tumor, peritumoral and rest of the stromal tissue
  2. Number of nerves in all of the areas above

Is there anybody who can help me with this?

Best regards,

Jack Cleutjenstumor-stroma

1 Like

A couple of questions for clarification:

  1. Which version are you using? This can be important when scripting.
  2. Can I assume the order of annotations will always be the same? Stroma > Peri-tumor > Tumor (it seems like it would be :slight_smile: )

Both measurements you want will probably require a script. If 2 is accurate, area will be the sum of each larger area type minus the next smaller area type. The number of annotation points could be more complicated since I assume you are using the same point class.

You could solve your second measurement problem very easily if you used separate classes for each location (classify points in the Tumor area as “Tumor-nerve,” create a new points object for the Peri-tumor and classify it differently, etc.), otherwise I think it will involve splitting the points object up, looking at each XY coordinate, and maybe finding whether it is inside of a given annotation type. Definitely more complicated.

Terrible example:

The point counts are separate since I used a different class when clicking inside the green circle vs within the outer rectangle. You would need to be very careful to keep the correct class selected when adding new points, but at least it wouldn’t require scripting.
The area is also circled for the outer annotation, that minus the inner area would be the area of the space between them, and can be accessed for any given annotation by it.getROI().getArea(). I can go into more detail if that is looking sort of like what you want.

1 Like

ad 1: I am using Version: 0.2.0-m2; Build time: 2019-03-15, 10:17

ad 2: I think the best order would be:

  1. First detect the total tissue
  2. Annotate the peritumoral area including the tumor area
  3. And then within the peritumoral area the area of the real tumor --> peritumor is then 2-3
  4. Total tissue (1) minus 2 is then the stromal area
    Because there is no single marker for the structures we have to count, we need to manually count the nerves by clicking on them. We need then the number of nerves per (either stroma, tumor or perotumoral area).

Is it possible to put this in a script?

Best regards,


1 Like

Hi Jack,

I don’t really know what your samples look like, and if you could show a representative image it may help us to better understand what the best appraoch would be, but my question would be: how do you manually define peritumoral area. Is “peritumoral area” qualitatively clearly different from “stroma”, so that you could created the peritumoral annotations in a meaningful and reproducible manner? In most of the models I work with, it is not, so I use a different approach: created the tumor annotation (manually or with the pixel classifier) and then define peritumoral area as x microns around the tumor, using Expand annotation. In most cases this makes more sense to us, as the tumor usually has distinct borders, but peritumor area does not.

Regarding a nerve marker, we usually use S100 (see attached example showing nerves in mouse mesentery). However, as you may know S100 stains other normal and neoplastic cell types (e.g. melanocytes), so whether this works in your case would depend on the tumor type and tissue in which the tumor is located.

S100 mesentery.tif (3.1 MB)

1 Like

I do agree with @VetPathologist that an annotation expansion might be easiest if the periTumor area is otherwise less well defined, but for the moment I will go off of the assumption that you are manually annotating it. Another thread on expansion/erosion can be found here.

Don’t mind the lack of tumor in the example! :slight_smile:

The easiest way to go about things, still, is to have classes for everything as you manually annotate it. So create your full tissue annotation, classify it as stroma (though that itself is easily scriptable). Draw your annotations for tumor and peri-tumor regions, and classify those as well.
When adding points for your nerves, make sure you change class as shown in the image. Each time you click the “Add” button you can start a new group of points, which can have an individual class. Highlighting a Points object in the list (and having the points tool selected!) will let you continue adding points of that class. Points are collections of objects, so doing it this way makes the next steps much easier.

Although… oops. I just realized, I am not sure if you want point density, or counts per area. Counts per area would involve the difficulties I was trying to avoid, while density (neurons per um^2, tumor, stroma etc.) across all regions of a type should be fairly easy. Could you clarify?

Here is the quick density script though. Note that it assumes certain things, listed in the script, and currently doesn’t clear previous results.
Results should be listed as shown. Red shows difference in annotation area to calculated area, blue shows the measurements showing up in a new “Results” whole image annotation. These results should be for all tumor/peritumor/stroma regions in total.

I can work on a different version later, but it would take a little more time to split the points annotations and figure out where they all go.


For this project this is exactly what the researcher wants to do. I am not a favorite of doing things manually, but in this case there is no other option.
There is still a problem with the script, and I cannot figure out what goes wrong.

The calculated data for the nerve per um2 are not calculated. The individual areas are correct. But the counted number per class not.

See attachment.


1 Like

I would need more information to troubleshoot. It doesn’t look like you have run the macro for that image since there is no Results annotation. If you could show the Results measurement list it might give me some more information. Are the density values 0, NA, don’t show up as columns at all?


I updated the script to include a few sanity checks that output the various values to the text window below the script. That might help identify the problem as well.


After running the script I can see these messages:
INFO: total stroma 3.326965722752977E8

INFO: peritumor area 9.897954700536871E7

INFO: tumor area 1.844414137299633E7

INFO: adjusted peritumor area 8.053540563237238E7

INFO: adjusted stroma area2.33717025269929E8

INFO: tumor nerves0

INFO: stroma nerves0

INFO: peritumor nerves0

INFO: Done!

I have attached the Annotation results after running the script.


after script.txt (939 Bytes)


Ahah. I figured it out. Ok, so the script assumes that you have classified your points as tumor/stroma/periTumor. You have not done that, though I thought you had because I misread the picture! You have named your points, but not classified them.
If you Set Properties on a points object in the annotation menu and choose a name, that results in what you see in the first line, “Stroma (Stroma)”, if the points are both named and classified. The other two lines, for Tumor and periTumor have only been classified, not renamed. The other line was 4 unclassified points that I renamed “New Points,” which looks exactly like what you are seeing. Thanks for including the screenshots, it would have been really difficult to troubleshoot otherwise!

Try selecting one of the points objects, and then right clicking somewhere near the points to get a context menu, and set the classes of each to Tumor/Stroma/periTumor and see if that works.


Now it works great.


1 Like

Thanks for your help. It works fine now.

I still have one small question:

  • Is it possible to read the filename and put it in to the results of the Show annotation measurements?



If you use
saveAnnotationMeasurements(“file path”, ), see another example at the bottom here, the first column should be the image name.

Other than that, I don’t think you can put strings as new measurements. The only other way might be to rename an annotation (or all annotations?).

def currentImageName = getCurrentImageData().getServer().getShortServerName()