Automatic counting

Good morning everyone,

I am a new QuPath user and I was triying to develop a system which would allow the sofware to automatically count some cells regularly disposed side by side in long rows. In particular, we are talking about RGB pictures that show empty cells that only present blue colored cell walls and a space in the middle which results white (background’s color). So, I though to delimitate a cells row with the Polygon tool, create a new set of points (Counting tool) and then start a script which would allow the software to put a point in the center of each cell. I would then provide to correct any mistake and assign the set of points to one of the annotation classes that I already created.

Thanks in advance!
Mikey

Hi @Bulb,

Why not just using the Cell detection tool and then count the number of detected cells?
It is probably the quickest way to get a cell count in a specified region of your image!

Hello melvingelbard, thank you for your response. I tried to use the cell detection tool but I had several issues:

  • Most of the cells are not detected (same bad result for each detection channel RGB)
  • The few detected cells are marked as objects but not as points so they do not appear in the objects list of the counting tool
  • The detection is not limited to the empty center of each cell but the software highlights the whole cell (cell walls+space inside).

Thanks

Probably playing round with the parameters should help you make the whole process automatic.
Cells are DetectionObjects but this allows you to perform a lot of useful things. I would refer you to the docs to see all you can do!

If anyway you want to do it that way: if I understood right, you have annotated all your cells by hand and are looking to create Points annotation objects at their centre? Here is a small script that should do it:

import qupath.lib.roi.ROIs
import qupath.lib.objects.PathAnnotationObject

xs = []
ys = []
getAnnotationObjects().forEach {
    xs << it.getROI().getCentroidX()
    ys << it.getROI().getCentroidY()
}

def roi = ROIs.createPointsROI(xs as double[], ys as double[], QPEx.getCurrentViewer().getImagePlane())
def newAnn = new PathAnnotationObject(roi, null)
addObject(newAnn)

Hello melvingelbard,

Thank you again.I read the docs as you suggested.

I tried to change the parameters and it detects all the cells in the polygon I drew with few mistakes.

What I would like to do is to avoid counting by hand (putting a point in each cell’s space) but let the sofware create a point in each cell or if not possible, let it detect the empty center of cells and then convert the Detection objects to Points.

By running the script, it creates some random (wrong) points out of the polygon area and the Detection objects remain the same.

The script I provided just gets the center of each annotation and creates a point there. I thought you had manually annotated each cell, in which case the script would have been useful.

So are you happy with the performance of the detection?

I am not sure what you mean by ‘empty center of cells’. Also what is your actual objective? Points and detections are just types of objects, and it would be useful to know what you want to do with them to guide you better.

The following script will iterate through all the detected cells (the cell detection will create PathCellObjects), and create points in their centers (though I’m not sure I understand the motive for this):

xs = []
ys = []
QP.getCellObjects().forEach {
    xs << it.getROI().getCentroidX()
    ys << it.getROI().getCentroidY()
}

createPointsROI(xs, ys, QPEx.getCurrentViewer().getImagePlane())

Maybe posting your image (if possible/allowed) here would help us understand the context of this thread?

Definitely this. It sounds like you might need a pixel classifier or thresholder, if you have RGB images, but I am still not sure if the RGB images are brightfield (it sounds like they are, but you did not really explain the experimental setup. RGB can also be fluorescent). If everything is white except the cell walls, it should be fairly easy to threshold on white and then remove all large objects (assuming the background outside of the cell walls is “large” relative to the inside of the cell walls).

1 Like

First of all, melvingelbard and Research_Associate: thank you very much for your help.

I am very sorry but I am not allowed to upload any image.

With “empty center of cells” I just meant the inner part of each cell which is empty since the cells are dead and the protoplasm is absent. Since the RGB images are brightfield, it is possible to see the white background, meaning that each cell is white except for the cell walls. My goal is create a point in each cell belonging to the same row, in order to count the number of cells in a row. I chosen the Counting tool to do that manually but it is a time-consuming process. Since the picture is rather simple, I thought that it might be easy to use an automatic detection of single cells and creation of a point inside them.

I am happy with the automatic detection after setting up some parameters. It would be nice to set the new values of the parameters as standard values. In this way, I should not set the parameters everytime I open the Cell detection tool.

[quote=“Research_Associate, post:7, topic:42313”]
If everything is white except the cell walls, it should be fairly easy to threshold on white and then remove all large objects (assuming the background outside of the cell walls is “large” relative to the inside of the cell walls).
[/quote] Keeping in mind that the cells are arranged side by side in a row (horizontally oriented), the background outside the cell walls is only below and above the row. In order to exclude these portions, I drew a narrow polygon which does not touch the upper and lower cell walls.

To wrap up:

  1. I have more polygons and each polygon covers a whole row
  2. in each row, single cells have been detected by the automatic cell detection tool. In the cell detection tool, It would be nice to set the new values of the parameters as standard values. In this way, I should not set the parameters everytime I open the Cell detection tool.
  3. I would need to “convert” each cell (Detection object) in a single Point and it needs the script proposed by melvingelbard, I guess.
  4. Would it be possible to restrict the effect of the script to the rows inside the polygons?
  5. By using the script form melvingelbard, I obtained an error and the sofware suggested me to add “import qupath.lib.scripting.QP” at the beginning of the script. However it does not work:

import qupath.lib.scripting.QP
xs = [ ]
ys = [ ]
QP.getCellObjects().forEach {
xs << it.getROI().getCentroidX()
ys << it.getROI().getCentroidY()
}
createPointsROI(xs, ys, QPEx.getCurrentViewer().getImagePlane())

Feedback from the sofware:
ERROR: MissingMethodException at line 10: No signature of method: org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.createPointsROI() is applicable for argument types: (ArrayList, ArrayList, qupath.lib.regions.ImagePlane) values: [[6936.4296875, 6853.93505859375, 6765.14990234375, 6681.50537109375, …], …]

ERROR: org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.callGlobal(GroovyScriptEngineImpl.java:404)
org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.access$100(GroovyScriptEngineImpl.java:90)
org.codehaus.groovy.jsr223.GroovyScriptEngineImpl$3.invokeMethod(GroovyScriptEngineImpl.java:303)
org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:73)
org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:51)
org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:171)
org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:203)
Script18.run(Script18.groovy:11)
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)

Just to check, which version of QuPath are you using?

For the script, this should fix the error:

import qupath.lib.roi.ROIs
import qupath.lib.objects.PathAnnotationObject

xs = []
ys = []
getCellObjects().forEach {
    xs << it.getROI().getCentroidX()
    ys << it.getROI().getCentroidY()
}

def roi = ROIs.createPointsROI(xs as double[], ys as double[], getCurrentViewer().getImagePlane())
def newAnn = new PathAnnotationObject(roi, null)
addObject(newAnn)

If you use Create script in the Workflow tab, you will have the specific command for the cell detection with the exact parameters you chose.

For your task, why not just counting the cells instead of creating a Point inside each cell?
For instance: this will give you the number of cells in your image:

print getCellObjects().size()

and this will give you the number of cells inside an annotation called myAnnotation:

print getCellObjects().findAll {
    return it.getParent().getName() == "myAnnotation"
}.size()
1 Like

I am using QuPath v. 0.2.0

The script does not give any error, however it always creates random points out of the polygons so I can not say it works properly.

Regarding the conversion detection objects-point: I would need the points together with the quantitative information (number of cells in the row n. 1, 2, 3…) per each set of points so I think it is crucial to have the same result as counting cells manually.

Sorry, I just edited my post (I had forgotten to change the call to getAnnotationObjects()), you can try again the updated script.

It’s hard to say since they have not really shown their project, but I think part of the issue is that they are detecting cells outside of the polygons, which they probably should not be doing.

Restricting the cell detection to the polygons might be a cleaner solution.

Unfortunately, we are kind of guessing at the hierarchy structure.

1 Like

The solution was easy.

In the counting tool there is the button “Convert detections to points”, so the best thing to do is (parts with asterisk to be solved already):

  1. create a polygon around the cells we are interested in (if possible, it is better to exclude the background outside the cell walls)
  2. select the polygon
  3. start the automatic cell detection after having set the parameters (*also by using the script, however it would be nice to set a shortcut to run it without opening the Automate menu/Show script editor)
  4. after having corrected any mistakes committed by the automatic detection, open the Counting tool, create a new set of points and click on the “Convert detections to points” button (*it would be nice to create a script and set a shortcut to run it without opening the Automate menu/Show script editor)
  5. *It would be nice to have a script which converts the polygon to a line

Thank you again for your time

Hello together,

I have a problem which is pretty similar to the one described above. I am counting wood cells. My cells look like the ones Bulb described (see image). So I have my samples and I have to count 3 cell rows and in the end I measure the length of my measurements (the width of the tree ring).

Unfortunately, the rows do not always follow a straight line like in the image above, sometimes they can make curves. Using the polygon tool means that I have to follow the shape of the line at the one side and back on the other side (blue line), to only detect the cells I want to. Otherwise I would detect many cells, which are not in my cell row (red line).

I was wondering if it is also possible to work with a single line (green line)? Perhaps along this line QuPath could detect the cell walls and put a counting point at the line in the middle of 2 cell walls. This would halve my work and I would also receive the measurement of the length.

I am not very experienced with these things but isn’t that possible with grey levels or so?

Thank you very much for ideas and help

Tobi

It might depend on how consistent your widths are, but you could try expanding a polyline that goes down the middle of your cell line.
image
Objects->Annotations->Expand annotations


Deleting the polyline, I get

Thank you very much,

that is a perfect solution!

1 Like