H-watershed and LabelRegions

Hi all,

With @CellKai and @ehrenfeu , we’re having issues trying to look into LabelRegions after running H-watershed. Here is the snippet:

#@ OpService ops

from ij import IJ
from ij import ImagePlus
from net.imglib2.img.display.imagej import ImageJFunctions
from net.imglib2.img.imageplus import ImagePlusImgs
from net.imglib2.type.numeric import RealType
from net.imglib2.roi import Regions;
from net.imglib2.roi.labeling import LabelRegions, ImgLabeling

imp = IJ.openImage("http://imagej.nih.gov/ij/images/blobs.gif")
print type(imp)

# set up and run H-watershed
hMin = 40
thresh = 100
peakFlooding = 100
outputMask = False
allowSplit = True

# H-watershed returns a label map as an ImagePlus
labelmap = ops.run("H_Watershed", imp, hMin, thresh, peakFlooding, outputMask, allowSplit )
labelmap.show()
print "labelmap: ", type(labelmap) # is 32bits

# convert to 16-bits as ImgLabeling expects Int not not Float
IJ.run("Conversions...", " ");
IJ.run(labelmap, "16-bit", "")

# fist convert ImagePlus to img as suggested by curtis:
# from https://forum.image.sc/t/how-to-wrap-any-kind-of-imageplus-to-an-imglib2-img-floattype/178/6
wrapImg = ImageJFunctions.wrap(labelmap)
print "wrapImg: ", type(wrapImg)

# then convert img to ImgLabeling (net.imglib2.roi.labeling.ImgLabeling)
labeling = ImgLabeling(wrapImg)
print "labeling: ", type(labeling)

# get regions from ImgLabeling
regions =  LabelRegions(labeling)
print "regions: ", type(regions)

existing_labels = regions.getExistingLabels()
print "existingLabels: ", type(existing_labels)

The result of this script is this labeled image and an java.lang.IndexOutOfBoundsException: java.lang.IndexOutOfBoundsException: Index: 10, Size: 1 error.

image

Is this error coming from the fact that the labels are not following the “normal” order (aka 1 being the first object from the top left corner) ? Or is this coming from somewhere else ? I also have the equivalent in a groovy script (based on @haesleinhuepf answer in this post) but the error is the same.

Thanks a lot for your help.

1 Like

Hey @lguerard @CellKai and @ehrenfeu,

CC @tpietzsch @imagejan @maarzt

good question! It’s a bit complicated, but in simple words: You cannot just get an ImgLabelling from a label image as discussed earlier here:

There is a workaround making your script run: Binarizing the image and running connected components. I think a proper solution is on the way…

#@ OpService ops

from ij import IJ
from ij import ImagePlus
from net.imglib2.img.display.imagej import ImageJFunctions
from net.imglib2.img.imageplus import ImagePlusImgs
from net.imglib2.type.numeric import RealType
from net.imglib2.roi import Regions;
from net.imglib2.roi.labeling import LabelRegions, ImgLabeling
from net.imglib2.algorithm.labeling import ConnectedComponents
from net.imglib2.type.numeric.integer import UnsignedShortType;

imp = IJ.openImage("http://imagej.nih.gov/ij/images/blobs.gif")
print type(imp)

# set up and run H-watershed
hMin = 40
thresh = 100
peakFlooding = 100
outputMask = False
allowSplit = True

# H-watershed returns a label map as an ImagePlus
labelmap = ops.run("H_Watershed", imp, hMin, thresh, peakFlooding, outputMask, allowSplit )
labelmap.show()
print "labelmap: ", type(labelmap) # is 32bits

# convert to 16-bits as ImgLabeling expects Int not not Float
IJ.run("Conversions...", " ");
IJ.run(labelmap, "16-bit", "")

# fist convert ImagePlus to img as suggested by curtis:
# from https://forum.image.sc/t/how-to-wrap-any-kind-of-imageplus-to-an-imglib2-img-floattype/178/6
wrapImg = ImageJFunctions.wrap(labelmap)
print "wrapImg: ", type(wrapImg)

# then convert img to ImgLabeling (net.imglib2.roi.labeling.ImgLabeling)
#labeling = ImgLabeling(wrapImg)
# workaeound to convert a label image into an ImgLabeling
againBinary = ops.threshold().apply(wrapImg, UnsignedShortType(0));
labeling = ops.labeling().cca(againBinary, ConnectedComponents.StructuringElement.FOUR_CONNECTED);

print "labeling: ", type(labeling)

# get regions from ImgLabeling
regions =  LabelRegions(labeling)
print "regions: ", type(regions)

existing_labels = regions.getExistingLabels()
print "existingLabels: ", type(existing_labels)
print "num of existingLabels: ", existing_labels.size()

Cheers,
Robert

4 Likes

Hey @haesleinhuepf,

Thanks a lot for your answer, it indeed seems to do the job that way ! :smiley:

Would this still work with 3D images ? I guess it should with the watershed applied as the 1st step, it just needs to redo the labels.

1 Like

Unfortunately, I don’t know. Could you try and let us know? :slight_smile:

1 Like

It seems to be giving 3d labels indeed.

However, the number of labels we’re getting after binarization and CCA is different from the number of labels that we had after the h watershed. Is there a way to go back from ImgLabelling to ImagePlus to have an idea about the differences?

1 Like

Yes, there is. Intelij can tell you :wink:
image

In a project like yours it’s obviously beneficial to have Fiji and IntelliJ running in parallel to get the full power of Imglib2, just because of the auto-completion:

https://haesleinhuepf.github.io/run_jython_scripts_from_ide/

I hope that helps!

Cheers,
Robert

1 Like

I just set it up for Groovy scripting but I haven’t had time to look into Jython support in IntelliJ yet… :slight_smile:

Thanks a lot for your help, I will report once we figure out the difference in labels !

1 Like

Ok so the culprit was a mix of H watershed and the CCA done in ops.

The output of H_watershed needed to be a mask otherwise it wouldn’t create the new limits between touching objects.

Thanks a lot for your awesome help @haesleinhuepf ! :slight_smile:

1 Like

Ah, great to hear! Also note, there will be a simpler way to achieve a solution properly. Follow/watch this github PR to stay up-to-date:

Cheers,
Robert

2 Likes

This PR has been merged and is available in imglib2-roi-0.9.0 (just released)

4 Likes

Hi Tobias,
I would also like to create labelregions (so to use cursors etc) from an image with integer labels. I guess i should probably add the 0.9.0 dependency (scijava pom is at 0.7.0).
Do you think this will cause conflicts when running the plugin in an existing Fiji installation?

How one would get/construct the Set of labels from the image that contains integer labels?

fromImageAndLabels(RandomAccessibleInterval<I> img, List<T> labels)

Thanks
Antonio

1 Like

Hi Antonio,

No, there shouldn’t be any conflicts.

If you want to keep using integers as labels, you could for example create an ArrayList<Integer> and then add integers 1 through the highest integer label occurring in your image. (Note that you should not add 0 to the list for the background).

You could also use anything else for labels if that makes semantically more sense, e.g. have a List<String> with "cell 1", "cell 2", "nucleus 1", … etc. Then pixels with value 1 in your integer image would get labeled {"cell 1"}, etc.

3 Likes

Hi Tobias,
I managed to construct LabelRegions from a RAI Img that contains integer values.

final ArrayList<Integer> labellist = ArrayList<>(Arrays.asList(1, 2,3))
final ImgLabeling<Integer, UnsignedByteType> imgLabels = ImgLabeling.fromImageAndLabels(Img, labellist);
final LabelRegions< Integer > labelRegions = new LabelRegions<>(imglabels);

However, I have the impression that ImgLabeling.fromImageAndLabels expect Img values to start from 1 and sequential. Object identified by values like 10, 11, 20 will not work. I think it comes from LabelingMapping.java class that computes the maximal number of labels from the max in Img.

Let me know if I miss something

Antonio

1 Like

Hey @apoliti,

AFAIK, this is by design, as you can read in the javadoc: “The pixel values of the index image must be between 0 and the length of the list of labels. A pixel value of zero represents a pixel with no label. A pixel value of N represents the label, which is given by the Nth entry in the list of labels.”
https://javadoc.scijava.org/ImgLib2/net/imglib2/roi/labeling/ImgLabeling.html

Can you elaborate a bit where a label map containing labels 10, 11, 20 but not containing 12, 13,… comes from? Most tools do deliver label maps with continous numbering…

Cheers,
Robert

Side note: in CLIJ, we have a method for “repairing” label maps with missing entries:

Demonstrated e.g. in excludeLabelsOnEdges:

Hey @haesleinhuepf,
I see and it makes sense to have sequential indexing. I was creating the image from which to extract the labels programmatically and I now changed it to be compliant.

I still have a problem on how to deal with intersecting labels. In my case I have overlapping spheres and I need to know the intersection. With 2 spheres, I index them 1 and 2 , respectively and the intersection is 3. This gives me 3 LabelRegions and on each of them I can do different operations. However, this is not generic and will fail if I have more than 2 spheres. In fact I would need to create intersecting labels, the intersection should have label 1 and 2. In the java doc https://javadoc.scijava.org/ImgLib2/net/imglib2/roi/labeling/ImgLabeling.html is suggested to use a list of set of labels.

This method does not support intersection labels, for intersecting labels set see fromImageAndLabelSets(net.imglib2.RandomAccessibleInterval<I>, java.util.List<java.util.Set<T>>).

but I still don’t get how to do it.

Better would be to programmatically create the labels directly in ImgLabeling. I still struggle a little on how to do it. I suppose I can use a cursor and directly create labels.

BTW is there a way to find a documentation on all the differences in the row types (labelling, masks etc.).

Thanks

Antonio

1 Like

Yes, this particular method is for importing labelmaps (from other tools such as 3D Manager oder H-Maxima) where overlapping labels don’t exist.

If you have two overlapping spheres (that’s two ROIs, right?) why don’t you just compute overlap between them? Is there any reason to go via labeling?

Cheers,
Robert

I have an image where I do some computations on it and the regions/roi can well be described by intersecting spheres that have been placed ‘manually’. I use the labelling as convenience to extract regions where I can do operations on the original image (mean, texture etc).

I just managed to create labelling directly, but how do you get the region that has 2 labels? In fact I am not even sure if I create multiple labelling at all. I try to develop on the examples of https://github.com/imglib/imglib2-introductory-workshop/tree/master/src/main/java/t05labelings from @tpietzsch

final ImgLabeling<Integer, UnsignedByteType> imgLabel = new ImgLabeling<>(img);
final RandomAccess< LabelingType< Integer > > access = imgLabel.randomAccess();
for (int i = 0; i < nrspheres; i++) {
   final HyperSphereCursor cursor = hyperSpheres.get(i).cursor();
   while(cursor.hasNext()){
      cursor.fwd();
      access.setPosition(cursor);
      access.get().add(i+1);
   }
}
1 Like

I see. I think it might be possible to create an empty labeling (!) and then add labels to it for every sphere. Related code is used here (in the repository where you just were) and here.

Let us know if you can make it work! :slight_smile:

I tried the example and also in my code. It seems that I do create double labelling.
Now I just have to find a way on how to access the region of the intersection. This is a big plus of imglib2 roi but I have issues in using it.

Something like region.getLabelRegion(1,2) or region.getLabelRegion(1)*region.getLabelRegion(2)

1 Like