Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Volume slicing refactoring #630

Merged
merged 6 commits into from
Mar 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion _includes/volume_slicing/volume_slicing_act1_imagej-gui.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
67 changes: 40 additions & 27 deletions _includes/volume_slicing/volume_slicing_act1_imagej-macro.ijm
Original file line number Diff line number Diff line change
@@ -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")
30 changes: 30 additions & 0 deletions _includes/volume_slicing/volume_slicing_act1_python-napari.py
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions _modules/volume_slicing.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: >

Expand Down
Loading