Can ROIs be linked to other ROIs in Fiji?

Hi there,

I was wondering if it would be possible to combine two (or more) ROI sets to relate ROIs. E.g., could I somehow link each nucleus-ROI (A) to its corresponding cell-ROI (B)? I mean, I would like to have an ROI that would be the parent of other ROI(s). It is doable within Fiji?

PS: I’m aware that this can be easily done within CellProfiler. However, I’m really interested in being able to do something similar within a Fiji workflow.

Thank you very much in advance.

Best

In a way, yes, finding that “relation” that you are after is done through something called “region connection calculus”.
The following plugins do not deal with ROIs but with binary regions instead, to generate tables of relations between regions in different images:

https://blog.bham.ac.uk/intellimic/spatial-reasoning-with-imagej-using-the-region-connection-calculus/

You might find some of these references relevant to your problem:
https://blog.bham.ac.uk/intellimic/references/
specially this one:
https://ieeexplore.ieee.org/document/6212514?arnumber=6212514

Regards

Gabriel

2 Likes

Thank you very much @gabriel, I will give it a try :smiley:
Best

I’m trying to install the ImpProps plugin. However, I’m not able to follow the installation instructions: Download ImpProps.java to the plugins folder and compile it using Plugins/Compile and Run.

I got this message:

The “Compile and Run” command is not currently supported. We recommend using the Script Editor or an IDE such as Eclipse for plugin development.

Sorry, I’ve never done this before. Could you please let me know how can I make it using the Script Editor? I’ve tried, but I think I’m not doing it properly.

Drag the java file in the IJ window (under the buttons) that should load it into the editor and from there there is a Run and Compile command. Please try that.

Thank you. I already tried copying the code and pasting it into the editor, then switching the language to Java and finally Compile and Run.

Fiji asks me to save the file and I place it into the plugins folder. However, I get this message from the console:

[INFO] Reading available sites from https://imagej.net/
No javac.jar found (looked in C:\Users\Pau\Desktop\Fiji.app\jars)!

I found this topic and added an underscore to the plugin filename. I’m still not getting a .class file

Why the Compile and Run command doesnt work in Fiji?

Now I get this:

javax.script.ScriptException: Class ImpProps in invalid directory: C:\Users\Pau\Desktop\Fiji.app\plugins\ImpProps_.java

Apologies for my lack of knowledge

Hi, I just tried that in Fiji (linux) and it also gives an error pointing to that missing file.
I can suggest two options before somebody else posts a workaround.

  • Download plain ImageJ and compile the way we discussed, https://imagej.nih.gov/ij/download.html
    (then copy the class file generated to the Fiji/plugins folder, or
  • Drop me an email me and I will send you the class file. I prefer not to post my address in a public forum (we are all nice people here, but I get too much spam already). Just get my email from the the blog I posted earlier.

Maybe @Wayne could provide the class file along the java file in the IJ site?
http://imagej.nih.gov/ij/plugins/imp-props.html

Just a detail: you should be able to run the RCC plugins without that plugin installed (although it will be useful to have it when retrieving (with a macro) the properties that the RCC plugins set to images it processes.

1 Like

I’ve done as you say, so now I have the ImpProps.class file in the plugins folder. However, it seems that something is going wrong when I run the RCC8D plugin.

  • I’ve generated 2 binary images from the 2 ROI sets at the first post of the topic.
    binary_cell.tif (89.3 KB) binary_nuclei.tif (89.3 KB)
  • But, when I try to run the RCC8D plugin, nothing happens and I get the message I’m attaching at the bottom of this post (it was happening before I had the ImpProps.class as well)

Regards,

Console details

RCC8D_ not up-to-date because 1 source files are not up-to-date (C:\Users\Pau\AppData\Local\Temp\java2220512344272182795\src\main\java\RCC8D_.java)
Compiling 1 file in C:\Users\Pau\AppData\Local\Temp\java2220512344272182795
[-classpath, C:\Users\Pau\AppData\Local\Temp\java2220512344272182795\target\classes;C:\Users\Pau\Desktop\Fiji.app\plugins\3D_Blob_Segmentation-3.0.1.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\3D_Objects_Counter-2.0.1.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\3D_Viewer-4.0.2.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\AnalyzeSkeleton_-3.3.0.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\Anisotropic_Diffusion_2D-2.0.1.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\Archipelago_Plugins-0.5.4.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\Arrow_-2.0.2.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\Auto_Local_Threshold-1.10.0.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\Auto_Threshold-1.17.1.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\BaSiC_.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\BalloonSegmentation_-3.0.1.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\Biovoxxel_Plugins-2.4.1.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\Bug_Submitter-2.1.1.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\CPU_Meter-2.0.1.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\Calculator_Plus-2.0.1.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\Cell_Counter-2.2.2.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\Colocalisation_Analysis-3.0.5.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\Color_Histogram-2.0.7.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\Color_Inspector_3D-2.3.2.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\Colour_Deconvolution-3.0.2.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\CorrectBleach_-2.0.2.jar;C:\Users\Pau\Desktop\Fiji.app\plugins\Correct_3D_Drift-
[many other lines]

So you have the ImpProps.class in the plugins folder. OK
Did you activate the Morphology update site?
Also please remove the RCC8D_.java and the RCC8D_UF_Multi.java files (only!) from the plugins folder (but leave the two corresponding .class files) and restart Fiji.
Let me know what you get.

When you run the plugin with nuclei as image X and cells as image Y you should get this result
---- RCC8D_Multi v2.5 ----
ImageX: binary_nuclei.tif
RegionsX: 7
ImageY: binary_cell.tif
RegionsY: 7
Mode: RCC5D
Relations: 49
O-relations: 7
Processing_time_(sec.): 0.143
0 DR: 42
1 PO: 5
2 EQ: 0
3 PP: 2
4 PPi: 0

That is only 2 of your nuclei are proper part of the cell. The other nuclei have been segmented too large or the cells are segmented too small (and hence are only PO, partial overlap).

1 Like

By the way thanks for reporting the errors, I updated the installation instructions in the blog to make it clear that the installation in Fiji is slightly different than for ImageJ.

Thank you very much, now it works. Would it be possible to obtain the information of which nucleus is linked to which cell? I’ve done something like this (to be ran with the masks I attached above) in order to obtain two segmented images where each nucleus and cell are linked by their intensity value:

selectImage("binary_nuclei.tif");
getDimensions(width, height, channels, slices, frames);
newImage("cell_count_mask", "8-bit Black", width, height, slices);
selectImage("binary_nuclei.tif");
run("Analyze Particles...", "show=[Count Masks] display clear");
rename("nuclei_count_mask");
n=nResults;
run("Clear Results");
for (i=1; i<=n; i++) {
	selectImage("nuclei_count_mask");
	setThreshold(i, i);
	run("Create Mask");
	rename("nucleus-"+i);
	run("BinaryReconstruct ", "mask=binary_cell.tif seed=nucleus-"+i+" create white");
	rename("cell-"+i);
	run("Analyze Particles...", "add");
	close("nucleus-"+i);
	close("cell-"+i);
	selectImage("cell_count_mask");
	roiManager("Select", i-1);
	setForegroundColor(i, i, i);
	roiManager("fill");
}
selectImage("nuclei_count_mask");
run("glasbey_inverted");
selectImage("cell_count_mask");
run("glasbey_inverted");
run("Tile");

I was looking for something more straightforward (and faster?) to get the results linked

You are welcome. Thank you very much for your help

Yes, you can! click on the Progress checkbox when you run the plugin. This tells you which region in X and Y hold a given relation:
---- RCC8D_Multi - Tests ----
Overlap 0 X:0 Y:1 -->3 PP
Overlap 1 X:1 Y:0 -->1 PO
Overlap 2 X:2 Y:3 -->1 PO
Overlap 3 X:3 Y:4 -->3 PP
Overlap 4 X:4 Y:2 -->1 PO
Overlap 5 X:5 Y:5 -->1 PO
Overlap 6 X:6 Y:6 -->1 PO
---- RCC8D_Multi v2.5 ----
ImageX: binary_nuclei.tif
RegionsX: 7
ImageY: binary_cell.tif
RegionsY: 7
Mode: RCC5D
Relations: 49
O-relations: 7
Processing_time_(sec.): 0.087
0 DR: 42
1 PO: 5
2 EQ: 0
3 PP: 2
4 PPi: 0

But perhaps more useful is to get this programmatically from the RCC image that is generated. X axis represents the index of each object in image X and the Y axis the index of the region in image Y.
Just treat the RCC image as a table, the meaning of the colours is given when you check the Legend option (you need to check this as well when running the plugin).
To know in your binary image which region is which, you can use the Binary_Label plugin from the Morphology folder (obviously the label is going to be the index region+1 because 0 is reserved for the background) or the NumberParticles macro in that same folder.
If you need to reconstruct a given region, use the XStart, YStart coordinates of every region given by the Particles8 plugin first and keep those in two arrays (a similar to the approach used in the NumberParticles macro mentioned above, but the macro does not store all the start points in arrays, but labels one region at a time).

Feel free to ask again if this is not clear.

1 Like

I do not understand why the ImpProps.class file would ever be needed. The purpose of ImpProps.java is to demonstrate how to define Java methods that can be called from a macro using the call() function. It is not supposed to be used by other plugins.

Hi @Wayne, you are right, my mistake. That plugin is not needed.
I just checked again the macros I used to get the properties that the RCC plugins set, and indeed they use the call() function.
Thanks for the heads up.

So can I remove the ImpProps.class file?

Yes, you can, sorry for my misunderstanding. I had assumed that getting the image properties via the call() function also required the plugin to be compiled, but as Wayne indicated it is not needed at all. I updated the instructions in the blog.

Thank you very much, I got it :smiley:

Using the binary images attached

I did it like this (just in case it may be useful to someone)

// get the starting coordinates of the objects (nuclei)
selectImage("binary_nuclei.tif");
run("Set Measurements...", "  redirect=None decimal=2");
run("Analyze Particles...", "display clear record");
nucX=newArray(nResults);
nucY=newArray(nResults);
for (i=0; i<nucX.length; i++) {
	nucX[i]=getResult("XStart", i);
	nucY[i]=getResult("YStart", i);
}

// get the starting coordinates of the objects (cells)
selectImage("binary_cell.tif");
run("Set Measurements...", "  redirect=None decimal=2");
run("Analyze Particles...", "display clear record");
cellX=newArray(nResults);
cellY=newArray(nResults);
for (i=0; i<cellX.length; i++) {
	cellX[i]=getResult("XStart", i);
	cellY[i]=getResult("YStart", i);
}

// get the RCC image
run("RCC8D UF Multi", "x=binary_nuclei.tif y=binary_cell.tif show=RCC5D details");
selectImage("RCC");
run("8-bit");
getDimensions(widthRCC, heightRCC, channelsRCC, slicesRCC, framesRCC);

// generate two 8-bit black images sizes as the binary images
selectImage("binary_nuclei.tif");
getDimensions(width, height, channels, slices, frames);
newImage("nuc_count_mask", "8-bit Black", width, height, slices);
newImage("cell_count_mask", "8-bit Black", width, height, slices);

// create the count masks of nuclei and cells, which will be linked by its object index
run("Wand Tool...", "tolerance=0 mode=Legacy");
count=1;
for (x=0; x<widthRCC; x++) {
	for (y=0; y<heightRCC; y++) {
		selectImage("RCC");
		grayValue=getPixel(x, y);
		if (grayValue != 255) {
			setForegroundColor(count, count, count);
			selectImage("binary_nuclei.tif");
			doWand(nucX[x], nucY[x]);
			roiManager("add");
			selectImage("nuc_count_mask");
			roiManager("fill");
			roiManager("reset");
			selectImage("binary_cell.tif");
			doWand(cellX[y], cellY[y]);
			roiManager("add");
			selectImage("cell_count_mask");
			roiManager("fill");
			roiManager("reset");
			count++;
		}
	}
}

// sort
selectImage("binary_nuclei.tif");
run("Select None");
selectImage("binary_cell.tif");
run("Select None");
selectImage("nuc_count_mask");
run("glasbey_inverted");
selectImage("cell_count_mask");
run("glasbey_inverted");
close("RCC");
run("Tile");

Hi Pau, Thanks for posting the macro. Below is an modification which is a bit shorter and faster (avoids using ROIs) that also checks whether the relation is PO and prints a warning to the log window. PO relations can be, but are are not necessarily, one-to-one, so the it might not be possible to have unique colour pairs when analysing multiple regions across 2 images.
The macro works fine in your test images because the PO relations are exclusive between one single cell and one single nucleus, but imagine an incorrectly segmented image where a nucleus overlaps two cell regions. The result in such instances will depend of the ordering of the regions.
The macro also won’t label (or rather will leave labelled as 255) the PPi relations (in this case would be “a cell inside a nucleus”) that could also could arise with erroneous segmentation.
The warnings in the log window give you an indication of which nuclei you could erode to try and fit an expected spatial model (i.e. a nucleus is expected to be completely contained by the cell, so PO indicates some segmentation artifact). There are more examples of model-based correction of segmentation results in the references links above.
Hope it helps!

// get the starting coordinates of the objects (nuclei)
selectImage("binary_nuclei.tif");
run("Particles8 ", "white show=Particles minimum=0 maximum=9999999 overwrite redirect=None");
nucX=newArray(nResults);
nucY=newArray(nResults);
for (i=0; i<nucX.length; i++) {
	nucX[i]=getResult("XStart", i);
	nucY[i]=getResult("YStart", i);
}

// get the starting coordinates of the objects (cells)
selectImage("binary_cell.tif");
run("Particles8 ", "white show=Particles minimum=0 maximum=9999999 overwrite redirect=None");
cellX=newArray(nResults);
cellY=newArray(nResults);
for (i=0; i<cellX.length; i++) {
	cellX[i]=getResult("XStart", i);
	cellY[i]=getResult("YStart", i);
}

// get the RCC image
run("RCC8D UF Multi", "x=binary_nuclei.tif y=binary_cell.tif show=RCC5D details");
selectImage("RCC");

getDimensions(widthRCC, heightRCC, channelsRCC, slicesRCC, framesRCC);

// generate two 8-bit black images sizes as the binary images
selectImage("binary_nuclei.tif");
run("Duplicate...", "title=nuc_count_mask");
run("glasbey_inverted");
selectWindow("binary_cell.tif");
run("Duplicate...", "title=cell_count_mask");
run("glasbey_inverted");

count=1;
for (x=0; x<widthRCC; x++) {
	for (y=0; y<heightRCC; y++) {
		selectImage("RCC");
		grayValue=getPixel(x, y);
		if (grayValue>0 && grayValue <4 ) {
			setColor(count);
			if (grayValue ==1)  print("Potential non-unique PO between nucleus "+x+" and cell "+y);	
			selectImage("nuc_count_mask");
			floodFill(nucX[x], nucY[x],"8");
			selectImage("cell_count_mask");
			floodFill(cellX[y], cellY[y],"8");			
			count++;
		}
	}
}

// sort
close("RCC");
run("Tile");

Hi Gabriel, thank you for your insights (and your macro). I was using the ROI Manager in order to remove particles that are not linked. E.g., if I had a nuclei binary mask as the one below:

binary_nuclei.tif (89.3 KB)

In that case, it would be a difference between our outputs:

Actually yours also works for that, just taking into account that not linked objects will be left labelled as 255. I see your point regaring the PO. I think it may be interesting to use it as a quality control for the segmentation when batch processing.
Best