Custom segmentation by tiles

Dear All,

I would like to perform segmentation of objects but using an ImageJ script instead of the cell segmentation algorithm embeded in qupath.
I also would like to process by tiles.

I understood that I could generate tiles, then loop over each tiles, select the annotation and send them one by one to ImageJ to run my ImageJ script and return the segmentation result as detection or annotation from the overlay.

I would like to know if there is a n example / methods to merge continuous detection or annotation so at the end I could merge back the splitted objects due to the tiling ( similar to mergeSelectedAnnotations() but for touching annotation or detection).

Thanks,
Benjamin

Just to check, but would the detections actually be continuous or would there be a pixel or two of space between them? Have you checked yet?

Also, you might want to look into:


At which point you can merge them. *or may not have to merge them.

Thanks for your quick answer, maybe an image will explain better what I would like to achieve.

I have a fluorescent image multi-channel and I want to segment object based on one channel.
In order to reduce the memory usage, I tiled the image and process it by tiles using an ImageJ macro which return a result as detection

run("Duplicate...", "duplicate channels=3-3");
run("Median...", "radius=3");
setAutoThreshold("Default dark");
setThreshold(100, 65535);
setOption("BlackBackground", true);
run("Convert to Mask");
run("Analyze Particles...", "size=25-Infinity pixel show=Overlay");

Now, as you can see on the above screenshot, the contiguous detection are splitted due to the tiling.

I would like to know if there is a script example or a qupath method/function that allow to combine contiguous detection so the 2 yellow detection so my 2 detection will be merged into 1?

Also, I am not sure to understand how the command “Tile classifications to annotation” can help in this situation.

Benjamin

Ah, fair enough, I was thinking the area objects would be tiles (they are for SLICs, tiles doesn’t mean rectangular). In that case it might be easiest to classify your tile annotations, and also send back the objects as annotations. Then you can remove all of the annotation tiles (selecting them by whatever you classified them as), followed by going straight to the merge annotations step.

If you need them to be detections, you can also script converting them in that direction, but I don’t think you’ve said which version of QuPath you are using, so hard to be specific with scripts.

If you want to look up some options, there is an entire section on removing objects (among other things) here. Most of them are written for either 0.1.2 or M7-9, but there are a few all across the various versions.
https://gist.github.com/Svidro

*though it does seem that the annotations might slow down QuPath quite a lot. Checking with someone who has already written the code to merge detections in M9, but that would be M9 specific.

thanks a lot @Research_Associate , I am using the version 0.2.0-m9 so that’s would work for me.

Alright! @smcardle has agreed, so here is a subsection from another script of hers that grabs all detections and merges them into a single object.

To specify, this doesn’t merge detections, this grabs all of their ROIs, merges those, and creates new objects from the merged ROIs. It then deletes the original objects. Same result, but wanted to be clear on the pathway, as things like the name of the detection object will be gone.

def islets= getDetectionObjects()
def geos=islets.collect{it.getROI().getGeometry()}
def combined=GeometryCombiner.combine(geos)
def merged= UnaryUnionOp.union(combined)
def mergedRois=GeometryTools.geometryToROI(merged, islets[0].getROI().getImagePlane())
def splitRois=RoiTools.splitROI(mergedRois)

def newObjs=[]
splitRois.each{
    newObjs << PathObjects.createDetectionObject(it,getPathClass("Islet"))
}
addObjects(newObjs)

removeObjects(islets,true) 
//This step removes the original objects. Comment it out if for some reason you want both.

Obviously it was written for islets, but you can change the names and classes around

Wahou, thanks a lot @Research_Associate, it’s a great help!
There is a small typo in the code, here is the same code corrected for the typo ( splitRois instead of splitROIs) and with import which run for the version 0.2.0-m9 :

import org.locationtech.jts.geom.util.GeometryCombiner;
import org.locationtech.jts.operation.union.UnaryUnionOp
import qupath.lib.roi.GeometryTools
import qupath.lib.roi.RoiTools
import qupath.lib.objects.PathObjects

def islets= getDetectionObjects()
def geos=islets.collect{it.getROI().getGeometry()}
def combined=GeometryCombiner.combine(geos)
def merged= UnaryUnionOp.union(combined)
def mergedRois=GeometryTools.geometryToROI(merged, islets[0].getROI().getImagePlane())
def splitRois=RoiTools.splitROI(mergedRois)

def newObjs=[]
splitRois.each{
    newObjs << PathObjects.createDetectionObject(it,getPathClass("Region"))
}
addObjects(newObjs)

removeObjects(islets,true) 
1 Like

Ah, thanks. I had to edit it slightly and did mistype that. Glad you got it all figured out.

Are you scripting the imageJ macro across all images in the project? Just checking, since I haven’t really played with scripts like this one since earlier versions.
https://gist.github.com/Svidro/5e4c29630e8d2ef36988184987d1028f#file-imagej-plugin-macros-in-a-script-groovy
Plus a few others near the bottom. You seem to be handy with the coding, so might be able to get it working in M9 and can mention if there are any problems!