Processing time of CLIJ2 pullLabelsToROIManager

Hi @haesleinhuepf and CLIJ2 fans,

I’m regularly using the CLIJ2 the method PullLabelsToROIManager. I have noticed that this procedure becomes very slow for images with many ROIs, to the point where it becomes almost impossible to use. (A single large image with ~5000 ROIs took 30 minutes to process.)

To map out the performance I ran an IJ macro (script below; adapted from one of yours) to test the processing times for different number of ROIs. Below are the results in a log-log plot:


The fit indicates that this process scales (almost) with the square of the number of ROIs (b=1.9).

I realized afterwards that in this macro the image size obviously also scales with the number ROIs, so we’re actually looking at two effects here. So I added a line in the macro to downsample the image and keep the image size constant. Below are the results:


Now we see that b~1, telling us that both image size and number of ROIs contribute roughly equally to the processing time.
It also seems that under the hood this method is carried out on the CPU, am I right? The bottleneck is a ‘converting selection to threshold’ action, shown in the Fiji status bar.

Now my question of course is: Do you think this operation could be sped up in any way?

Arguably using labelmaps is anyhow better that using the good old ROI Manager, but in some cases I find it still more convenient.

Here’s the macro that I used:

run("CLIJ2 Macro Extensions", "cl_device=");
Ext.CLIJ2_clear();

roiManager("reset");
run("Clear Results");
run("Close All");
run("Blobs");

tiles = getNumber("Number of tiles in x and y", 1);	//Tile the input image

original = getTitle;
getDimensions(width, height, channels, slices, frames);
Ext.CLIJ2_push(original);

Ext.CLIJ2_create2D(tiled, width * tiles, height * tiles, 16);
for (x = 0; x < tiles; x++) {
	for (y = 0; y < tiles; y++) {
		Ext.CLIJ2_paste2D(original, tiled, width * x, height * y);
	}
}
Ext.CLIJ2_downsample2D(tiled, tiled2, 1/tiles, 1/tiles);
Ext.CLIJ2_pull(tiled2);

Ext.CLIJ2_startTimeTracing();

//Create labelmap from blobs
Ext.CLIJ2_thresholdOtsu(tiled2, thresholded);
Ext.CLIJ2_connectedComponentsLabelingBox(thresholded, labelmap);
Ext.CLIJ2_release(thresholded);

Ext.CLIJ2_pullLabelsToROIManager(labelmap);

Ext.CLIJ2_pull(labelmap);
run("glasbey_on_dark");

Ext.CLIJ2_stopTimeTracing();
Ext.CLIJ2_getTimeTracing(time_traces);
print(time_traces);
print(roiManager("count") + " ROIs");
Ext.CLIJ2_clear();

Thanks (again) for your help!
Bram

1 Like

Hi Bram @bramvdbroek,

this method is really not the fastest. To speed your script up, I suggest avoiding it. :grimacing: To maintain the functionality of your script, it’s important to know what you would do next with the ROIs in the RoiManager. For simply counting them, you could use maximumOfAllPixels on the labelmap and for visualisation also detectLabelEdges is useful. What else would you like to do with the ROIs?

Cheers,
Robert

1 Like

Hi @haesleinhuepf,

Haha, yes, avoiding it is what I’ve been doing. :grinning:
Mostly, I use it to show the outlines as overlay, the key point being that it is easy to quickly switch on and off for visual inspection.
And I use it to measure things. In CLIJ2 the latter is done with statisticsOfBackgroundAndLabelledPixels. That method produces all kinds of handy measures, but it misses e.g. median intensity. I now use MorphoLibJ to measure that on the labelmap instead of the ROI manager, which works well, but involves swapping images from GPU memory to RAM. Would it be easy to incorporate that measurement, in a next release or so?

Incidentally, the reverse action (ROI Manager to labelmap) should be much faster, right? Your friends at the Scientific Computing Facility at MPI-CBG hold a nice Fiji Update site with such a function. I use that one sometimes.

Best regards,
Bram

1 Like

You could do this by showing the labels edges in a second channel, which you turn on/off with the Channels Tool. That should be very fast and also works in 3D.

Point taken. I added this to the wishlist. Can you provide a real use-case where measuring the median of lables delivers completely different values compared to measuring the mean of labels? Curious!

I’m afraid a median measurement might be better suited on the CPU anyway. Nevertheless CLIJ should have an easily accessible method for measuring that.

Hehe, glad that you like the stuff on that update site. Guess who wrote that plugin :wink: I put it on the wishlist as well.

Thanks again for the great feedback in input for future clij versions! It’s appreciated.

Have a great weekend!

Cheers,
Robert

1 Like

Hi @haesleinhuepf,

Thanks for taking these things along!

I could have guessed. :upside_down_face:

1 Like

As for the median: a situation where it’s helpful is when measuring the ratio between nuclear and cytoplasmic signals. Cell segmentation does not work, so cytoplasm is defined as a ring around the nucleus. When cells get very close, such a ROI may for a small part extend into another cell with a possibly totaly different intensity. By taking the median value instead of the mean you take out the outliers.

For the implementation I’m afraid I can’t be of much help. But I was just browsing and found this repository. Might that be helpful? (though it is written for CUDA). And @rharkes suggested using quickselect.

Thanks for taking these things along for the next update!
Bram

1 Like