Local Thickness for 3D image but results for 2D slice

Hello everyone, I need your help. The LocalThickness implemented in ImageJ is said for 3D image. However when I test for localthickness of fibers of fibrous assemply of a volume 1000x1000x5 voxel, I found some local with thickness > 15voxels, but logically I can’t find any thickness > 5 voxels, right? It gives me the impression that the local thickness operation works from slice to slice, not 3D.
¨Please help me for explanation. Thank you very much.

Hello @HuyenTran

LocalThickness in 3D could be in any direction. You can’t measure a local thickness > 5 voxel in Z-Direction but in X/Y-Direction or some other direction it is possible.

Apparently it makes a difference if your object touches the “border” in z direction, i.e. if there are foreground pixels in the first and last slice of the stack. Judging from this macro snippet:

newImage("Example", "8-bit black", 40, 40, 5);
makeOval(5, 2, 31, 75);
setForegroundColor(255, 255, 255);
run("Fill", "stack");
run("Duplicate...", "duplicate");
run("Local Thickness (masked, calibrated, silent)");
selectWindow("Example");
setSlice(5);
run("Add Slice");
run("Local Thickness (masked, calibrated, silent)");
selectWindow("Example_LocThk");
run("Set... ", "zoom=1200");
selectWindow("Example-2_LocThk");
run("Set... ", "zoom=1200");

the objects seem to be mirrored at all borders for the calculation of the distance map.

The distance map calculates the distance to the closest background pixel. When there is no background pixel in a certain direction (i.e. the object touches the border), there will be no distance.

To change that behavior, you can pad your image with a single row of background pixels in all dimensions.

3 Likes

Thank you very much, I will test and tell you the result

When I add the border (volume 1002x1002x7 voxel), the result becomes more reasonable. However I found local thickness even in the 1st and 7th slice where is originally background which make the maximal local thickness of 6 voxels in place of 5 voxels. I tried to add more border (1004x1004x9 voxel), same result is found from the 2nd slice. To me the result is more reasonable however I have overestimated the fiber thickness for 1 more voxel.

Indeed there seems to be some inconsistency here. The following macro creates two stacks differing in their thickness by 1 pixel in the z dimension. The resulting maximum thickness differs by 2 though:

// Stack of 7 slices, object of 5 slices
newImage("7_Slices", "8-bit black", 22, 22, 7);
run("Specify...", "width=20 height=20 x=1 y=1 slice=1");
for (i=2; i<nSlices; i++) {
    setSlice(i);
    run("Fill", "slice");
}
run("Local Thickness (masked, calibrated, silent)");
run("Set... ", "zoom=1200");
getMinAndMax(min, max);
print("Maximum thickness: " + max); // will print 6

// Stack of 6 slices, object of 4 slices
newImage("6_Slices", "8-bit black", 22, 22, 6);
run("Specify...", "width=20 height=20 x=1 y=1 slice=1");
for (i=2; i<nSlices; i++) {
    setSlice(i);
    run("Fill", "slice");
}
run("Local Thickness (masked, calibrated, silent)");
run("Set... ", "zoom=1200");
getMinAndMax(min, max);
print("Maximum thickness: " + max); // will print 4

Would you mind creating an issue in the github repository? @rimadoma and @ctrueden might be able to comment on implementation details.

1 Like

I would like to do but I’m not good at programation language of ImageJ

Hello @imagejan

Could you try running the original LocalThickness plugin in your test macro, please? I.e. Local Thickness (complete process) instead of Local Thickness (masked, calibrated, silent). I’d like to know if the problem is in B. Dougherty’s original code or in my alterations to it.

1 Like

It 's the local thickness (complete process) that I used

1 Like

The reason @mdoube added the masking was to remove stray artifact voxels created by the algorithm. This is done after the actual thickness calculations, i.e. it’s just a cosmetic thing that doesn’t affect the thickness values stored in the voxels. The point being that Dougherty’s method creates some artifact voxels that might alter the thickness results in some cases. @mdoube any thoughts?

1 Like

I get the same inconsistent results when running the single steps one-by-one: the distance ridge has a maximum value of 2.0 or 3.0 for the respective example image in my above macro, running Distance Ridge to Local Thickness results in maximum values of 4.0 and 6.0, respectively. (The inconsistency gets more obvious when comparing objects of 5 and 6 slices in thickness: the local thickness reported is 6.0 in both cases.)

The underlying problem is the discreteness of the distance map (integer pixel distances) that gets translated to a quantization by multiples of 2 for the resulting local thickness value.

This might be less of a problem in real world data though, where you rarely have long straight edges parallel to one of the main axes…

The run("Local Thickness (complete process)"); has serious limitations when used from macros. See my reply to a recent bug report.

2 Likes

Yes, that’s it in a nutshell. You are trying to measure a continuous property (distance) using severely discontinuous data: a pixel grid. In general very regular test images throw up lots of these artefacts, but real world images (for us, trabecular bone mainly) should average out over lots of measurements.

An important implementation point is that outside the stack is considered foreground. So spheres can easily grow into the virtual outside space, until they touch a boundary inside the stack. If the seed points are near the stack faces, you could get much bigger spheres than you expect. You can get around it by padding with background (as already suggested), or perhaps in bigger stacks by cropping the result to trim off this artefact, before recording your statistics.

2 Likes

Thank you very much for all. I think for me the error can be tolerated as it is only at the border and that part is quite small for my real volumes. I will pad the border. But I’m very appreciated for your new solutions.

Bear in mind that if you do this, you may artefactually reduce the thickness of the border-touching regions, by creating a flat side where there isn’t really one in your original structure.

1 Like

Yes, I will. Thank you.