Skip to main content

image3d - Image segmentation and object separation in 3D using Mathematica


I currently work on an image segmentation scenario to detect cell nuclei in 3D using Mathematica. The data I have are image stacks in the TIFF file format. I would like to keep the sequence of image processing steps as small and simple as possible, since I expect the segmentation pipeline to work for several image stacks. I uploaded an example image stack containing several cell nuclei objects. Here is a screenshot of the image stack after creating an Image3D object out of it.


smallCropped = Image3D[Import["...\\smallExample.tif"], 
Axes -> True, AxesLabel -> {"X", "Y", "Z"}]

raw image stack as Image3D object


The image shows about 40-50 cell nuclei objects. Note that due to a different spacings in the z-dimension the objects appear flattened. However, this should not have any influence on the segmentation itself. If you have a look at the data you will find out that the signal to noise ratio (SNR) is very high. The segmentation of the cell nuclei objects should therefore be feasible. Here is a GIF animation of the image stack moving through the stack in the z-dimension:



enter image description here


I applied what seems to me as the most straight forward approach to segmentation in Mathematica (Binarize and MorphologicalComponents):


Show[#, ImageSize -> 512] & /@ {binary = Binarize@smallCropped, 
Image3D[Colorize@MorphologicalComponents@binary]}

enter image description here


So far so good. As you can see from the colorized components matrix on the left, several cell nuclei objects are assigned the same object label (e.g. the large pink object). Hence, preprocessing steps and a more sophisticated segmentation procedure seem to be required.


After going through many types of image filtering and segmentation in Mathematica I got lost at some point and ended up plugging together various different filtering and segmentation steps. In most of the cases, the more I tried the worse the results got ;) I know that there is no golden rule for segmentation in image processing but the task of correctly segmenting the objects in this scenario seems to me not that difficult and I wonder if there exists a nice and easy way.


The WatershedComponents method tries to separate connected objects. Since this method currently only works in 2D in Mathematica I implemented a method that first segments all the images of the stack slice by slice using a combination of GradientFilter, WatershedComponents, Binarize, MaxDetect and DistanceTransform. I then tried to merge the results from this 2D segmentation to end up with a 3D components matrix. I didn't put the code here but you can see the output of these two steps below (left: 2D WatershedComponents result, right: 3D result after merging).


compsMatrix = 

SelectComponents[
WatershedComponents[GradientFilter[#, 1],
MaxDetect[DistanceTransform@Binarize@ImageAdjust@#],
Method -> {"MinimumSaliency", .3}], "Count",
10 < # < 2000 &] & /@ Image3DSlices[Binarize@smallCropped]

enter image description here


This is where I am at so far. The segmentation accuracy and object separation still looks poor to me and I am curious what steps might improve the results. My questions are:


1. What preprocessing steps (e.g. filters) built in Mathematica might be suitable to increase the segmentation accuracy in this case? Is there a combination of standard filters that might work here?


2. How could I improve the separation of objects in 3D?



3. What about WatershedComponents for Image3D objects?


Edit: response comment on Erosion A suggestion in the comments was to use Erosion. I know that erosion helps to separate objects.however, in this scenario it did not help and I would lose much information about the objects.


Edit: response to answer by UDB


Thanks to the nice approach proposed by UDB I could go on with the 3D segmentation problem and try the approach with my data. At first the results looked very promising but when taking a closer look, I have to say that the solution proposed does not provide a satisfying result. I have checked the results for the test image stack provided above I then used the functions proposed by UDB:


distcompiled = 
Compile[{{dist, _Integer, 3}},
Module[{dimi, dimj, dimk, disttab, i, j, k,
ii}, {dimi, dimj, dimk} = Dimensions[dist];
disttab = Table[(i - ii)^2, {ii, dimi}, {i, dimi}];
Table[

Min@Table[disttab[[i, ii]] + dist[[ii, j, k]], {ii, dimi}], {i,
dimi}, {j, dimj}, {k, dimk}]], CompilationTarget -> "C"];

Options[EuclideanDistanceTransform3D] = {Padding -> 1};
EuclideanDistanceTransform3D[im3d_Image3D, OptionsPattern[]] :=
Module[{dist3d, i, j, k, ii, dimi, dimj, dimk},
dist3d =
Developer`ToPackedArray@
ParallelMap[
Round[ImageData[

DistanceTransform[#, DistanceFunction -> EuclideanDistance,
Padding -> OptionValue[Padding]]]^2] &,
Image3DSlices[
Image3D[ArrayPad[ImageData[im3d, "Bit"], 1,
Padding -> OptionValue[Padding]]]]];
If[OptionValue[Padding] != 0,
dist3d =
Developer`ToPackedArray@
ParallelMap[
If[Times @@ Dimensions[#] == Total[Flatten[#]], # +

1073741822, #] &, dist3d, {1}];];
dist3d = Developer`ToPackedArray@distcompiled[dist3d];
dist3d = ArrayPad[dist3d, -1];
Image3D[Sqrt@dist3d, "Real"]];

As you can see, I changed the CompilationTarget to "C" and added a missing comma after the local variable disttab in the distcompiled function. I then packed the function together and gave it a name:


splitSegmentation3D[img3d_Image3D] := 
Function[{image},
Image3D[Colorize[
MorphologicalComponents[

ImageMultiply[
Binarize[
LaplacianGaussianFilter[
EuclideanDistanceTransform3D[
ColorNegate[
Binarize[
ImageMultiply[MaxDetect[#], #] &[
EuclideanDistanceTransform3D[image]], 5]]], 1]],
image]]]]][img3d]


As proposed I set the threshold for accepted maxima to 5 in this function. For binarization I use the proposed TotalVariationFilter as pre-processing step and then do a simple Binarize. I then do the last of the pipeline using the function splitSegmentation3D:


{smallCropped = Image3D[Import["...\\smallExample.tif"],
prepro = TotalVariationFilter[smallCropped],
bin = Binarize[prepro],
comps = splitSegmentation3D[Binarize[prepro]]}

enter image description here


The components image looks just like the components image in the answer except for the colors...good. But when taking a closer look at the segmentation by slicing through the images in z-dimension, you see the details. I put the results of the complete image processing chain next to each other (from left to right: original, preprocessed, binary, components image):


enter image description here


I see multiple problems with this approach. First, using TotalVariationFilter as preprocessing step also blurs the image which results in many connected objects. Second, the splitting performs heavy over segmentation resulting in splitted objects. Third, many objects seem to be rounded by the segmentation. I think this results from the EuclideanDistanceTransform3D in combination with ImageMultiply.



I don't think that a perfect segmentation is possible on my data, but it should work out for at least some of the objects.



Answer



Introduction


I propose a solution to this object separation problem, which simply was inspired by one of the application examples of DistanceTransform. It uses a simple method to split virtually connected (blurred) objects in 3D. A LoG filter (Marr-Hildreth operator) is detecting edges in the Euclidean distance transform by this doing a tesselation required to split the abovementioned blurred objects. Since the DistanceTransform function presently is lacking 3D functionality, an own implementation of Euclidean distance transform is provided.


Using this method you might end up with this label image:


Mathematica graphics


But one step after another. This is our test data set for the moment:


img3d = Image3D[
ArrayPad[DiskMatrix[{3, 4, 5}], {{5, 10}, {20, 15}, {25, 30}}] +
ArrayPad[DiskMatrix[{5, 6, 3}], {{3, 8}, {18, 13}, {33, 26}}]]


Mathematica graphics


A self-made 3D distance transform


However, we still have to have a 3D distance transform. I will provide a poor man's solution of a 3D Euclidean distance transform, utilizing the built-in 2D DistanceTransform and adding some stuff according to algorithm 1 of the 1994 Pattern Recognition paper by Saito and Toriwaki entitled "New algorithm for Euclidean distance transformation of an n-dimensional digitized picture with applications", vol. 27, no. 11, pp. 1551-1565.


The following code provides an auxiliary function later on used to do the Euclidean distance measurements in the third dimension. It receives a 3D matrix dist consisting of a stack of 2D distance transformed images, whereas the squared Euclidean distances are assumed here. Along the stack direction, for each voxel it searches the minimum sum of the squared distance and the provided intermediate result:


distcompiled = Compile[{{dist, _Integer, 3}},
Module[{dimi, dimj, dimk, disttab, i, j, k, ii},
{dimi, dimj, dimk} = Dimensions[dist];
disttab = Table[(i - ii)^2, {ii, dimi}, {i, dimi}];
Table[

Min[disttab[[i, All]] + dist[[All, j, k]]],
{i, dimi}, {j, dimj}, {k, dimk}]
],
CompilationTarget -> "WVM"
(*this might be set to "C" if a compiler is installed*)
];

What still has to be defined is the function EuclideanDistanceTransform3D itself:


Options[EuclideanDistanceTransform3D] = {Padding -> 1};
EuclideanDistanceTransform3D[im3d_Image3D, OptionsPattern[]] :=

Module[{dist3d, i, j, k, ii, dimi, dimj, dimk},

dist3d =
Developer`ToPackedArray@
Map[Round[
ImageData[
DistanceTransform[#, DistanceFunction -> EuclideanDistance,
Padding -> OptionValue[Padding]]]^2] &,
Image3DSlices[
Image3D[ArrayPad[ImageData[im3d, "Bit"], 1,

Padding -> OptionValue[Padding]]]]];

If[OptionValue[Padding] != 0,
dist3d =
Developer`ToPackedArray@
Map[If[Times @@ Dimensions[#] == Total[Flatten[#]], # +
1073741822, #] &, dist3d, {1}];
];

dist3d = Developer`ToPackedArray@distcompiled[dist3d];


dist3d = ArrayPad[dist3d, -1];

Image3D[Sqrt@dist3d, "Real"]
];

This function assumes a binary Image3D handed over using the variable im3d.


The first part of the function does nothing but splitting the 3D stack into a sequence of 2D images and mapping them into the DistanceTransform function, wheras the result is re-squared. A one-voxel padding is applied using the Padding option provided.


The second part is circumventing the behavior of DistanceTransform when receiving images only consisting of 1's, in such a case it again returns an image purely filled with 1's, however, it should be full with Infinity entries, since there was not any 0. So we add $$2^{30}-2=1073741822$$ in such cases, by this ensuring that $$2^{31}-1$$ is never being exceeded as long as $$2^{15}=32768$$ is taken as largest extent in either of the volume dimensions: $$2^{30}-1+(2^{15})^2=2^{31}-1=2147483647$$.


In the third part we just use the auxiliary function already defined above.



In the fourth part the padding made in the beginning is being eliminated.


In the last part an Image3D is being assembled as return value, whereas for all elements of dist3d the square root is extracted, since we want to provide the Euclidean distances.


Now we have this EuclideanDistanceTransform3D.


A first test


Let us make a first test:


EuclideanDistanceTransform3D[img3d] // ImageAdjust

Mathematica graphics


Well, we are interested in those voxels indicating the local maxima:


MaxDetect[EuclideanDistanceTransform3D[img3d]]


Mathematica graphics


We might want to exclude some local maxima with not exceeding some certain distance values (3 in this example; this makes no difference to the result above for this test data set):


Binarize[ImageMultiply[MaxDetect[#], #] &[EuclideanDistanceTransform3D[img3d]], 3]

Mathematica graphics


Using these local maxima voxels, we apply another EuclideanDistanceTransform3D to measure the distances from them (we need to negate the maxima voxel intermediate result):


EuclideanDistanceTransform3D[
ColorNegate[
MaxDetect[EuclideanDistanceTransform3D[img3d]]]] // ImageAdjust


Mathematica graphics


What follows is the detection of 3D edges inside this second distance transform using an LoG filter:


LaplacianGaussianFilter[
EuclideanDistanceTransform3D[
ColorNegate[MaxDetect[EuclideanDistanceTransform3D[img3d]]]],
1] // ImageAdjust

Mathematica graphics


By binarizing the latter we will obtain a template to tesselate our data set, so objects might be split as intended. Here is the full image processing chain:



Function[{img3d},
Image3D[
Colorize[
MorphologicalComponents[
ImageMultiply[
Binarize[
LaplacianGaussianFilter[
EuclideanDistanceTransform3D[
ColorNegate[
Binarize[

ImageMultiply[
MaxDetect[#], #
] &[EuclideanDistanceTransform3D[img3d]],
3(*allow only maxima with distance values > 3*)
]
]
], 1(*LoG filter kernel size*)
]
], img3d
]

]
]
]
][img3d]

Mathematica graphics


A second test


To apply this method to the smallCropped data set provided above, I recommend to do an edge-preserving smoothing before binarization:


img3d = Binarize[TotalVariationFilter[smallCropped]]


Mathematica graphics


What simply has to be done here is to apply the same image processing chain given above, whereas the minimum threshold for accepted maxima was set to 5 here instead of 3 (as for the synthetic example).


Mathematica graphics


Please compare this output with the original data set:


Mathematica graphics


We are done.


Problems I still see here may be mainly caused due to the non-convexity of a certain number of objects in this data set.


Parallelized version of Euclidean 3D distance transform


I am happy to show you a parallelized version of my self-made 3D distance transform. Again, we use a compiled auxiliary function to treat the 3rd dimension, but this time with two option set: RuntimeAttributes -> {Listable}, Parallelization -> True, so we later on will be able to hand over a series of 3D lists, and we have enabled the execution of these series in parallel:


distcompiledparallelized = Compile[{{dist, _Integer, 3}},

Module[{dimi, dimj, dimk, disttab, i, j, k, ii},
{dimi, dimj, dimk} = Dimensions[dist];
disttab = Table[(i - ii)^2, {ii, dimi}, {i, dimi}];
Table[Min[disttab[[i, All]] + dist[[All, j, k]]], {i, dimi}, {j, dimj}, {k, dimk}]],
CompilationTarget -> "C",
(*this should be set to "WVM" if a compiler is not installed*)
RuntimeAttributes -> {Listable}, Parallelization -> True
];

Even though Wolfram virtual machine does a good job here, we have turned on C compilation.



Now we come to the function itself:


Options[EuclideanDistanceTransform3Dparallelized] = {Padding -> 1};
EuclideanDistanceTransform3Dparallelized[im3d_Image3D,
OptionsPattern[]] :=
Module[{dist3d, forwardtransposition, backwardtransposition,
addmargin},

backwardtransposition = Ordering[Reverse[ImageDimensions[im3d]]];
forwardtransposition = Ordering[backwardtransposition];


dist3d =
Developer`ToPackedArray@
Map[Round[
ImageData[
DistanceTransform[#, DistanceFunction -> EuclideanDistance,
Padding -> OptionValue[Padding]]]^2] &,
Image3DSlices[
Image3D[ArrayPad[
Transpose[ImageData[im3d, "Bit"], forwardtransposition], 1,
Padding -> OptionValue[Padding]]]]];


If[OptionValue[Padding] != 0,
dist3d =
Developer`ToPackedArray@
Map[If[Times @@ Dimensions[#] == Total[Flatten[#]], # +
1073741822, #] &, dist3d, {1}];
];

addmargin =
Ceiling[#1/#2]*#2 - #1 &[Dimensions[dist3d][[3]], $ProcessorCount];


dist3d = ArrayPad[dist3d, {{0, 0}, {0, 0}, {0, addmargin}}];

dist3d =
Function[{len, procs},
dist3d[[All,
All, # ;; Min[# + Ceiling[len/procs] - 1, len]]] & /@
Range[1, len, Ceiling[len/procs]]][
Last[Dimensions[dist3d]], $ProcessorCount];


dist3d = Developer`ToPackedArray@distcompiledparallelized[dist3d];

dist3d = Join[Sequence @@ dist3d, 3];

dist3d = ArrayPad[dist3d, {{-1, -1}, {-1, -1}, {-1, -addmargin - 1}}];

dist3d = Transpose[dist3d, backwardtransposition];

Image3D[Sqrt@N@dist3d, "Real"]
];


First of all we make sure that the 3D data will be transposed in a way that the extent of the 1st coordinate is smaller than the 2nd and 3rd, by this generally minimizing the computational load of distcompiledparallelized called later on. To manage this, we determine appropriate lists forwardtransposition and backwardtransposition for the respective Transpose calls later on.


Then we do the processing of the series of 2D images as done in the initial version.


Once this was done, we do some preparations for the parallelization. We have to make sure that all 3D sublists we want to hand over to distcompiledparallelized have the same dimensions. However, depending on the im3d dimensions and the $ProcessorCount this requires some padding. We do this padding solely at the upper margin of the 3rd dimension.


Once we have done the padding, we continue with equally splitting dist3d depending on both its 3rd dimension and the $ProcessorCount, so distcompiledparallelized can be called.


What remains to be done is some maintenance: Re-joining the dist3d, removing the margin (together with the overall padding), and undoing the transposition.


The last very effective way for speeing up is to numericalize dist3d before drawing the square root ...


A further approach to the object separation problem


Looking on the smallCropped data set one might get the feeling that its height requires stretching in order to let the cells appear as spheres (anisotropy correction o f course will have some influence to our distance based approach), we may assume a factor of approx. 3:


smallCroppedinterpolationfunction = 

ListInterpolation[ImageData[smallCropped, "Real"], Method -> "Spline"];
smallCroppedinterpolated =
ParallelTable[
smallCroppedinterpolationfunction[i, j, k], {i, 1,
Dimensions[ImageData[smallCropped]][[1]], 0.33}, {j, 1,
Dimensions[ImageData[smallCropped]][[2]]}, {k, 1,
Dimensions[ImageData[smallCropped]][[3]]}];
smallCroppedstreched = Image3D[smallCroppedinterpolated, "Real"]

Mathematica graphics



Then we again should do a TV filtering, but for fluorescence data we better should assume a Poisson noise:


img3d = Binarize[
TotalVariationFilter[smallCroppedstreched, Method -> "Poisson", MaxIterations -> 100]
]

Without option specification (or by setting Method -> "Cluster") Binarize takes "Otsu's method" doing some minimization of the intra-class variances.


The distance transformed img3d then looks like this:


EuclideanDistanceTransform3Dparallelized[img3d] // ImageAdjust

Mathematica graphics



Having detected the maxima inside this distance transform (and selecting those exceeding a certain minimum), we can look how both the distance field between these maxima and the tesselation look like:


EuclideanDistanceTransform3Dparallelized[ColorNegate[
Binarize[
ImageMultiply[MaxDetect[#], #] &[
EuclideanDistanceTransform3Dparallelized[img3d]],
15]]] // ImageAdjust

Mathematica graphics


LaplacianGaussianFilter[
EuclideanDistanceTransform3Dparallelized[ColorNegate[

Binarize[
ImageMultiply[MaxDetect[#], #] &[
EuclideanDistanceTransform3Dparallelized[img3d]], 15]]],
3] // ImageAdjust

Mathematica graphics


So, here is the code doing all the steps:


Function[{im3d},
Image3D[
Colorize[

MorphologicalComponents[
ImageMultiply[
Binarize[
LaplacianGaussianFilter[(*Marr-Hildreth operator*)
EuclideanDistanceTransform3Dparallelized[
ColorNegate[
Binarize[
ImageMultiply[
MaxDetect[#], #
] &

[EuclideanDistanceTransform3Dparallelized[im3d]],
15(*allow only maxima with distance values > 15*)
]
]
], 3(*LoG filter kernel size*)
]
], im3d
]
]
]

]
][img3d]

Mathematica graphics


You may complain that small cells are missing, well, I did my best (mixed-sized cells in an object separation problem make things sophisticated), and you may play with the two parameters, i.e. the LoG filter kernel size and the minimum maxima threshold. I personally think that it should not be considered an artifact that almost all cells touching the data set margins have been canceled.


Hope you enjoy the parallelized Euclidean 3D distance transform. It now can easily handle large volumes (not exceeding 32768 for all dimensions). If there is anybody who is inspired to extend my self-made 3D distance transform approach to ManhattanDistance and ChessboardDistance, please feel free to do so...


Addition: Extended 3D Distance Transform


I am happy to introduce my extended parallelized solution DistanceTransform3D for Image3D data, in analogy to the kernel function DistanceFunction presently only processing Image data, it now includes these distances: EuclideanDistance, ManhattanDistance, and ChessboardDistance.


We utilize three different compiled auxiliary functions:


eucldistcompiledparallelized = Compile[{{dist, _Integer, 3}},

Module[{dimi, dimj, dimk, disttab, i, j, k, ii},
{dimi, dimj, dimk} = Dimensions[dist];
disttab = Table[(i - ii)^2, {ii, dimi}, {i, dimi}];
Table[
Min[disttab[[i, All]] + dist[[All, j, k]]], {i, dimi}, {j,
dimj}, {k, dimk}]
],
CompilationTarget -> "C", (*should be set to "WVM" if no compiler is installed*)
RuntimeAttributes -> {Listable}, Parallelization -> True
];


manhdistcompiledparallelized = Compile[{{dist, _Integer, 3}},
Module[{dimi, dimj, dimk, disttab, i, j, k, ii},
{dimi, dimj, dimk} = Dimensions[dist];
disttab = Table[Abs[(i - ii)], {ii, dimi}, {i, dimi}];
Table[
Min[disttab[[i, All]] + dist[[All, j, k]]], {i, dimi}, {j,
dimj}, {k, dimk}]
],
CompilationTarget -> "C", (*should be set to "WVM" if no compiler is installed*)

RuntimeAttributes -> {Listable}, Parallelization -> True
];

chessdistcompiledparallelized = Compile[{{dist, _Integer, 3}},
Module[{dimi, dimj, dimk, disttab, i, j, k, ii},
{dimi, dimj, dimk} = Dimensions[dist];
disttab = Table[Abs[(i - ii)], {ii, dimi}, {i, dimi}];
Table[
Min@Map[Max[#] &,
Transpose[{disttab[[i, All]], dist[[All, j, k]]}]], {i,

dimi}, {j, dimj}, {k, dimk}]
],
CompilationTarget -> "C", (*should be set to "WVM" if no compiler is installed*)
RuntimeAttributes -> {Listable}, Parallelization -> True
];

The 3D distance transform reads now:


Options[DistanceTransform3D] = {Padding -> 1, DistanceFunction -> EuclideanDistance};
DistanceTransform3D[im3d_Image3D, thresh_Real: 0., OptionsPattern[]] :=
Module[{dist3d, forwardtransposition, backwardtransposition, addmargin},


backwardtransposition = Ordering[Reverse[ImageDimensions[im3d]]];
forwardtransposition = Ordering[backwardtransposition];

Switch[OptionValue[DistanceFunction],
EuclideanDistance,
dist3d =
Developer`ToPackedArray@
Map[Round[
ImageData[

DistanceTransform[#,
DistanceFunction -> EuclideanDistance,
Padding -> OptionValue[Padding]]]^2] &,
Image3DSlices[
Image3D[ArrayPad[
Transpose[ImageData[Binarize[im3d, thresh], "Bit"],
forwardtransposition], 1,
Padding -> OptionValue[Padding]]]]];
,
_,

dist3d =
Developer`ToPackedArray@
Map[ImageData[
DistanceTransform[#,
DistanceFunction -> OptionValue[DistanceFunction],
Padding -> OptionValue[Padding]]] &,
Image3DSlices[
Image3D[ArrayPad[
Transpose[ImageData[Binarize[im3d, thresh], "Bit"],
forwardtransposition], 1,

Padding -> OptionValue[Padding]]]]];
];

If[OptionValue[Padding] != 0,
dist3d =
Developer`ToPackedArray@
Map[If[Times @@ Dimensions[#] == Total[Flatten[#]], # +
1073741822, #] &, dist3d, {1}];
];


addmargin = Ceiling[#1/#2]*#2 - #1 &[Dimensions[dist3d][[3]], $ProcessorCount];

dist3d = ArrayPad[dist3d, {{0, 0}, {0, 0}, {0, addmargin}}];

dist3d =
Developer`ToPackedArray@
Function[{len, procs},
dist3d[[All,
All, # ;; Min[# + Ceiling[len/procs] - 1, len]]] & /@
Range[1, len, Ceiling[len/procs]]][

Last[Dimensions[dist3d]], $ProcessorCount];

Switch[OptionValue[DistanceFunction],
EuclideanDistance,
dist3d = Developer`ToPackedArray@eucldistcompiledparallelized[dist3d];
,
ManhattanDistance,
dist3d = Developer`ToPackedArray@manhdistcompiledparallelized[dist3d];
,
ChessboardDistance,

dist3d = Developer`ToPackedArray@chessdistcompiledparallelized[dist3d];
];

dist3d = Join[Sequence @@ dist3d, 3];

dist3d = ArrayPad[dist3d, {{-1, -1}, {-1, -1}, {-1, -addmargin - 1}}];

dist3d = Transpose[dist3d, backwardtransposition];

Switch[OptionValue[DistanceFunction],

EuclideanDistance,
Return[Image3D[Sqrt@N@dist3d, "Real"]];
,
_,
Return[Image3D[dist3d, "Bit16"]];
];
];

We generate a synthetic data set:


SeedRandom[2405];

img3d = Image3D[Table[If[RandomReal[] > N[1/1000], 1, 0], {10}, {20}, {15}]];

We do a short performance test:


eucldist3d = DistanceTransform3D[img3d, DistanceFunction -> EuclideanDistance];
manhdist3d = DistanceTransform3D[img3d, DistanceFunction -> ManhattanDistance];
chessdist3d = DistanceTransform3D[img3d, DistanceFunction -> ChessboardDistance];

Here we have the results:


ImageAdjust[ColorNegate[#]]& /@ {img3d, eucldist3d, manhdist3d, chessdist3d}


test data set Euclidean distance Manhattan distance Chessboard distance


Hope one day the same 3D functionality will be included in the built-in DistanceTransform.


Comments

Popular posts from this blog

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Mathematica: 3D plot based on combined 2D graphs

I have several sigmoidal fits to 3 different datasets, with mean fit predictions plus the 95% confidence limits (not symmetrical around the mean) and the actual data. I would now like to show these different 2D plots projected in 3D as in but then using proper perspective. In the link here they give some solutions to combine the plots using isometric perspective, but I would like to use proper 3 point perspective. Any thoughts? Also any way to show the mean points per time point for each series plus or minus the standard error on the mean would be cool too, either using points+vertical bars, or using spheres plus tubes. Below are some test data and the fit function I am using. Note that I am working on a logit(proportion) scale and that the final vertical scale is Log10(percentage). (* some test data *) data = Table[Null, {i, 4}]; data[[1]] = {{1, -5.8}, {2, -5.4}, {3, -0.8}, {4, -0.2}, {5, 4.6}, {1, -6.4}, {2, -5.6}, {3, -0.7}, {4, 0.04}, {5, 1.0}, {1, -6.8}, {2, -4.7}, {3, -1.