4
$\begingroup$

When applying Image3D to an array of binary images, how can I specify the Z-depth between these images? Consider the RegionPlot3D of $x y z < 1$:

IR = ImplicitRegion[x y z < 1, {x, y, z}];
RegionPlot3D[IR, Axes -> True]

enter image description here

Now, I create an image stack by applying RegionPlot (the 2D version) with varying Z coordinate.

Slices[zval_] := RegionPlot[{
   x y zval < 1
   }, {x, -4, 4}, {y, -4, 4}, PlotLegends -> Automatic, 
  Frame -> None, Background -> Black, PlotStyle -> White, 
  BoundaryStyle -> White]
binaryImageStack = Binarize /@ Slices /@ Range[-4, 4, 0.5]

enter image description here

Then I apply Image3D to the image stack.

Image3D[binaryImageStack]

enter image description here

How can I specify the spacing between slices to Image3D? I realise that you can use BoxRatios to adapt the Z scaling, but that is only a Graphics3D option. Surely 3D morphological image processing commands like Erosion, Dilation, MorphologicalComponents etc. must be affected by the wrong specification of Z spacing?

$\endgroup$
  • $\begingroup$ If it's for visualization purpose only, take a look at BoxRatios. ImageResize could be helpful too. $\endgroup$ – Chip Hurst Sep 7 '17 at 20:48
4
$\begingroup$

With Image3D[binaryImageStack], you're essentially defining your image using a 3D array of pixels (360x360x17 in your case). And you can't specify non-cubic pixels (it doesn't make much sense, just think about a 2D image).

Update 2

This is a bit of a refinement of the idea presented below. The idea is to pad the image using fixed padding before doing anything to remove border defects:

Image3D[
 Binarize@*
   Show@*
   (ImageMesh[#, BaseStyle -> White, Background -> Black, 
      AspectRatio -> 1, PlotRangePadding -> -50] &)@*
   (ImagePad[#, 50, "Fixed"] &) /@
  Image3DSlices[Image3D@binaryImageStack, All, 2]
 ]

Other than that, it works similarly to the earlier version, but is a bit cleaner.

Update

As noted by @szabolcs in the comments, you can simply resize the image using ImageResize, which produces something very similar to my original approach. The issue is still the same, i.e. you get more or less sharp steps.

The following demonstrates an idea how to fix this issue: (this is just an initial attempt, so don't expect perfect results)

Image3D[
 ColorNegate@Binarize@Show[#, AspectRatio -> 1] &@*
   DiscretizeRegion /@
  DeleteCases[EmptyRegion[2]][
   ImageMesh /@
    Image3DSlices[Image3D@binaryImageStack, All, 2]
   ]
 ]

The idea is to use the ability of ImageMesh to connect corners diagonally, providing us a nice interpolation. Since this only works on 2D images, we create slices from our 3D images (along the x or y axis, so that the z axis is in plane). After dropping empty slices (ideally you'd replace them with an empty image), we call DiscretizeRegion (this seems to improve the results a bit) and convert the slices back to images (this time with a square aspect ratio). As noted before, the result is far from perfect, but might give you a starting point if you need nicer images and can't get more slices.

Rest of original post

What you can do: Calculate your missing images using Interpolation (assuming calculating them takes too long, otherwise just calculate enough):

binaryImageStack = Binarize@*Slices /@ Range[-4, 4, 0.5]
interpol = Interpolation[binaryImageStack, InterpolationOrder -> 0]
Image3D@Array[interpol, 360, {1, 17}]

(The numbers in the last line are the image dimension (360) and the number of images in the interpolation (17) - alternatively, you could just specify the z-values in Interpolation)

This yields:

enter image description here

Note that 0th order interpolation gives you sharp steps in the resulting image, which isn't so nice - you might have to play a bit with it. One thing I tried was to not Binarize the images before the interpolation, but afterwards. (the result looked a bit strange though)

$\endgroup$
  • $\begingroup$ "you can't specify non-cubic pixels (it doesn't make much sense" With 3D images it's common—I would say typical—to use non-cubic pixels because often the data is scanned slice by slice (thus the inter-slice distance is independent of the xy plane pixel size). Take a look at the BoxRatios option example of Image3D in the docs. $\endgroup$ – Szabolcs Sep 7 '17 at 20:34
  • $\begingroup$ @Szabolcs As already noted by OP, this seems to only affect display, without changing the actual dimensions of the image, possibly causing unexpected results when using pixel based functions. $\endgroup$ – Lukas Lang Sep 7 '17 at 20:37
  • $\begingroup$ You are right, I didn't read carefully. The question is not very well phrased though, as he was asking about the inter-slice distance multiple times. It does look like he wants to upsample. Isn't ImageResize better for that? It also does interpolation (multiple methods available). $\endgroup$ – Szabolcs Sep 7 '17 at 20:41
  • $\begingroup$ It seems to always use nearest neighbour for 3D images, regardless of the Resampling option ... $\endgroup$ – Szabolcs Sep 7 '17 at 20:46
  • $\begingroup$ I played a bit more with this today, and it does appear that ImageResize can correctly use all resampling methods in 3D (contrary to what I said yesterday). It's also worth noting that the image is binary ("Bit" type) so there won't be any other values than 0 and 1. But the resampling is still done with real values, then the result is binarized again. Apart from ImageResize, there is also ArrayResample. $\endgroup$ – Szabolcs Sep 8 '17 at 10:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.