diff --git a/_includes/volume_slicing/volume_slicing_act1_imagej-gui.md b/_includes/volume_slicing/volume_slicing_act1_imagej-gui.md index 791d3035..bf59ce74 100644 --- a/_includes/volume_slicing/volume_slicing_act1_imagej-gui.md +++ b/_includes/volume_slicing/volume_slicing_act1_imagej-gui.md @@ -1,7 +1,7 @@ - Open a 3D image - Use `Image > Properties` to check for anisotropic voxel sizes - Use `Image > Stacks > Orthogonal views` to view the data in XY, XZ and YZ planes - - Understand how the anisotropy is dealt with +- Understand how the anisotropy is dealt with - Use `Image > Stacks > Reslice` to resample the data, exploring the below options for dealing with anisotropy - `Output spacing` - `[ ] avoid interpolation`, if this is checked, `Output spacing` is ignored diff --git a/_includes/volume_slicing/volume_slicing_act1_imagej-macro.ijm b/_includes/volume_slicing/volume_slicing_act1_imagej-macro.ijm index 1da35060..9a1f12b7 100644 --- a/_includes/volume_slicing/volume_slicing_act1_imagej-macro.ijm +++ b/_includes/volume_slicing/volume_slicing_act1_imagej-macro.ijm @@ -1,32 +1,45 @@ -// This macro opens the head image stack and reslices it to view it from different angles (side, front, top and diagonal view) -// and then selects slices corresponding to Z=60, X=135, and Y=160. -// Close other open images -run("Close All"); +// open volumetric image +open("https://github.com/NEUBIAS/training-resources/raw/master/image_data/xyz_16bit_calibrated__dna_metaphase.tif"); -// open head image stack -open("http://imagej.net/images/t1-head.zip"); -rename("Head viewed from the side"); -run("Duplicate...", "duplicate range=60"); -rename("Head Z=60"); +// explore the dimensions +width = getWidth(); +height = getHeight(); +depth = nSlices(); +print("Original Image Dimensions: Width = " + width + ", Height = " + height + ", Depth = " + depth); -// Reslice the head image stack to view the head from the front and from the top, and select the slices for X=135 and Y=160 in the orginal view. -selectWindow("Head viewed from the side"); -run("Reslice [/]...", "output=1.500 start=Left rotate"); // view head from the front -rename("Head viewed from the front"); -run("Duplicate...", "duplicate range=135-135"); -rename("Head X=135"); +// reslice YZ with interpolation +run("Reslice [/]...", "output=0.300 start=Left flip rotate"); +rename("YZ view"); +width = getWidth(); +height = getHeight(); +depth = nSlices(); +print("Image Dimensions YZ view: Width = " + width + ", Height = " + height + ", Depth = " + depth); -selectWindow("Head viewed from the side"); -run("Reslice [/]...", "output=1.500 start=Top rotate"); // view head from the top -rename("Head viewed from the top"); -run("Duplicate...", "duplicate range=160-160"); -rename("Head Y=160"); +// reslice XZ with interpolation +selectImage("xyz_16bit_calibrated__dna_metaphase.tif"); +run("Reslice [/]...", "output=0.300 start=Top"); +rename("XZ view"); +width = getWidth(); +height = getHeight(); +depth = nSlices(); +print("Image Dimensions XZ view: Width = " + width + ", Height = " + height + ", Depth = " + depth); -// Rotate the head stack that is viewed from the side and reslice to obtain diagonal view -selectWindow("Head viewed from the side"); -run("Duplicate...", "duplicate"); -run("Rotate... ", "angle=45 grid=1 interpolation=Bilinear enlarge stack"); // rotate the stack -run("Reslice [/]...", "output=1.500 start=Top rotate"); -rename("Head in diagonal view"); +// reslice YZ without interpolation +selectImage("xyz_16bit_calibrated__dna_metaphase.tif"); +run("Reslice [/]...", "output=0.300 start=Left flip rotate avoid"); +rename("YZ view"); +width = getWidth(); +height = getHeight(); +depth = nSlices(); +print("Image Dimensions YZ view WITHOUT interpolation: Width = " + width + ", Height = " + height + ", Depth = " + depth); -run("Tile"); +// reslice XZ with interpolation +selectImage("xyz_16bit_calibrated__dna_metaphase.tif"); +run("Reslice [/]...", "output=0.300 start=Top avoid"); +rename("XZ view"); +width = getWidth(); +height = getHeight(); +depth = nSlices(); +print("Image Dimensions XZ view WITHOUT interpolation: Width = " + width + ", Height = " + height + ", Depth = " + depth); + +run("Tile") diff --git a/_includes/volume_slicing/volume_slicing_act1_python-napari.py b/_includes/volume_slicing/volume_slicing_act1_python-napari.py new file mode 100644 index 00000000..8bfcfe14 --- /dev/null +++ b/_includes/volume_slicing/volume_slicing_act1_python-napari.py @@ -0,0 +1,30 @@ +# !First start Napari and drag and drop the image (https://github.com/NEUBIAS/training-resources/raw/master/image_data/xyz_16bit_calibrated__dna_metaphase.tif) in the Napari viewer. + +# Access the 3D array in the image layer +image = viewer.layers[0].data # assuming the image is in the first layer +shape = image.shape +print("Image shape:", shape) + +# Compare the non-scaled image to the scaled image. You can also use the 3D view to better appreciate the dimensions. +voxel_dims = (0.300, 0.1377745, 0.1377745) # original voxel dimensions in µm, in order ZYX +scale_factor = 1 / voxel_dims[0] +scaled_voxel_dims = [voxel_dims[0] * scale_factor, voxel_dims[1] * scale_factor, voxel_dims[2] * scale_factor] # rescaling the voxel dimensions to maintain proportions but display with a z-step of 1. +print("New voxel dimensions scaled relative to original", scaled_voxel_dims) +viewer.add_image(d, name="Scaled to physical world dimensions", scale=scaled_voxel_dims) # Note that the scaled image layer contains the exact same data as the non-scaled image, but is only visualized differently to show the correct proportions. + +# Create 2D slices (YX, ZX, and ZY) +mid_z_slice_ind = int(shape[0] / 2) +mid_y_slice_ind = int(shape[1] / 2) +mid_x_slice_ind = int(shape[2] / 2) + +viewer.add_image(d[mid_z_slice_ind], name=f"z={mid_z_slice_ind}") +viewer.add_image(d[mid_z_slice_ind], name=f"scaled: z={mid_z_slice_ind}", scale = scaled_voxel_dims[1:]) +viewer.add_image(d[:, mid_y_slice_ind, :], name=f"y={mid_y_slice_ind}") +viewer.add_image(d[:, mid_y_slice_ind, :], name=f"scaled: y={mid_y_slice_ind}", scale = [scaled_voxel_dims[0], scaled_voxel_dims[2]]) +viewer.add_image(d[:, :, mid_x_slice_ind], name=f"x={mid_x_slice_ind}") +viewer.add_image(d[:, :, mid_x_slice_ind], name=f"scaled: x={mid_x_slice_ind}", scale = scaled_voxel_dims[:2]) + +# View in grid mode side by side +viewer.grid.stride = 1 +viewer.grid.shape = (-1, 2) +viewer.grid.enabled=True diff --git a/_modules/volume_slicing.md b/_modules/volume_slicing.md index bdcafa3c..d7904ca7 100644 --- a/_modules/volume_slicing.md +++ b/_modules/volume_slicing.md @@ -17,13 +17,13 @@ concept_map: > V["Volume data"] --> S("Slicing") V --- A["Anisotropic"] S --> M["2D image"] - M -.- A + M -.- A figure: /figures/volume_slicing.png figure_legend: "Volume slicing: Extracting 2-D slices from a 3-D volume, e.g. to be visualised on a computer monitor. Anisotropy: The measured data points have a different spacing in pixel vs. physical space, which poses a practical challenge for the rendering." multiactivities: - - ["volume_slicing/volume_slicing_act1.md", [["ImageJ GUI", "volume_slicing/volume_slicing_act1_imagej-gui.md", "markdown"], ["ImageJ Macro", "volume_slicing/volume_slicing_act1_imagej-macro.ijm", "java"], ["ImageJ Jython", "volume_slicing/volume_slicing_act1_imagej-jython.py", "python"]]] + - ["volume_slicing/volume_slicing_act1.md", [["ImageJ GUI", "volume_slicing/volume_slicing_act1_imagej-gui.md", "markdown"], ["ImageJ Macro", "volume_slicing/volume_slicing_act1_imagej-macro.ijm", "java"], ["Python Napari", "volume_slicing/volume_slicing_act1_python-napari.py", "python"]]] assessment: >