Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Trabecular Segmentation Example

  • By: Michael T. Kuczynski, 2024

  • License: CC-BY

  • How to cite: Cite the ORMIR_XCT publication: Kuczynski et al., (2024). ORMIR_XCT: A Python package for high resolution peripheral quantitative computed tomography image processing. Journal of Open Source Software, 9(97), 6084, Kuczynski et al. (2024)


Aims


Step 1: Imports

Import modules/packages and set the input image path.

import os
import numpy as np
import SimpleITK as sitk

from matplotlib import pyplot as plt

from ormir_xct.core.segmentation.gauss_seg import threshold_dict, gauss_seg
from ormir_xct.core.util.segmentation_evaluation import (
    calculate_dice_and_jaccard,
    hausdorff_sitk,
)
joint_seg_path = os.path.join("images", "GRAY_JOINT.nii")
joint_seg_ipl_path = os.path.join("images", "TRAB_SEG_IPL.nii")
output_path = "images"

gray_img = sitk.ReadImage(joint_seg_path, sitk.sitkFloat32)
ipl_mask = sitk.ReadImage(joint_seg_ipl_path, sitk.sitkUInt8)

Step 2: Trabecular Segmentation

Run the ORMIR_XCT trabecular segmentation script on the input grayscale joint image.

We also need the lower and upper threshold limits. These are constant values defined in the IPL segmentation workflow and a re provided in threshold_dict in the ipl_seg.py file. Depending on your image units, these thresholds will change. In this example, we will use HU units since our image is in HU.

mu_water = 0.24090
mu_scaling = 8192
resale_slope = 1603.51904
rescale_intercept = -391.209015
lower_thresh, upper_thresh = threshold_dict["hu"]

trab_seg_ormir = gauss_seg(
    gray_img,
    lower_thresh,
    upper_thresh,
    voxel_size=0.0606964,
    sigma=0.5,
)

Step 3: Display Results

Now display the ORMIR_XCT and IPL segmentations together.

ipl_mask = sitk.Resample(
    ipl_mask, trab_seg_ormir, interpolator=sitk.sitkNearestNeighbor
)

# Convert the images to numpy arrays for plotting with Matplotlib
gray_np = sitk.GetArrayFromImage(gray_img)
ormir_np = sitk.GetArrayFromImage(trab_seg_ormir)
ipl_np = sitk.GetArrayFromImage(ipl_mask)
# Get the slices we want to view
view_slice1 = (slice(None), 45 - ormir_np.shape[1] // 2, slice(None))
view_slice2 = (slice(None), ormir_np.shape[1] // 2, slice(None))
view_slice3 = (slice(None), 45 + ormir_np.shape[1] // 2, slice(None))

slice1 = abs(int(45 - ormir_np.shape[1] // 2))
slice2 = int(ormir_np.shape[1] // 2)
slice3 = int(45 + ormir_np.shape[1] // 2)
# Plot the segmentations overlaid onto the grayscale image for 3 slices
fig, axs = plt.subplots(3, 3, figsize=(50, 30))

# Slice 1
axs[0][0].set_title(
    "ORMIR_XCT Trabecular Segmentation\nSlice: {}".format(slice1), fontsize=40
)
axs[0][0].imshow(gray_np[view_slice1], cmap="gray")
axs[0][0].imshow(ormir_np[view_slice1], cmap="rainbow", alpha=0.3)

axs[0][1].set_title(
    "IPL Trabecular Segmentation\nSlice: {}".format(slice1), fontsize=40
)
axs[0][1].imshow(gray_np[view_slice1], cmap="gray")
axs[0][1].imshow(ipl_np[view_slice1], cmap="rainbow", alpha=0.3)

axs[0][2].set_title("ORMIR_XCT and IPL Overlaid\nSlice: {}".format(slice1), fontsize=40)
axs[0][2].imshow(ormir_np[view_slice1], cmap="gray")
axs[0][2].imshow(ipl_np[view_slice1], cmap="rainbow", vmin=0, vmax=1, alpha=0.3)

# Slice 2
axs[1][0].set_title("Slice: {}".format(slice2), fontsize=40)
axs[1][0].imshow(gray_np[view_slice2], cmap="gray")
axs[1][0].imshow(ormir_np[view_slice2], cmap="rainbow", alpha=0.3)

axs[1][1].set_title("Slice: {}".format(slice2), fontsize=40)
axs[1][1].imshow(gray_np[view_slice2], cmap="gray")
axs[1][1].imshow(ipl_np[view_slice2], cmap="rainbow", alpha=0.3)

axs[1][2].set_title("Slice: {}".format(slice2), fontsize=40)
axs[1][2].imshow(ormir_np[view_slice2], cmap="gray")
axs[1][2].imshow(ipl_np[view_slice2], cmap="rainbow", vmin=0, vmax=1, alpha=0.3)

# Slice 3
axs[2][0].set_title("Slice: {}".format(slice3), fontsize=40)
axs[2][0].imshow(gray_np[view_slice3], cmap="gray")
axs[2][0].imshow(ormir_np[view_slice3], cmap="rainbow", alpha=0.3)

axs[2][1].set_title("Slice: {}".format(slice3), fontsize=40)
axs[2][1].imshow(gray_np[view_slice3], cmap="gray")
axs[2][1].imshow(ipl_np[view_slice3], cmap="rainbow", alpha=0.3)

axs[2][2].set_title("Slice: {}".format(slice3), fontsize=40)
axs[2][2].imshow(ormir_np[view_slice3], cmap="gray")
axs[2][2].imshow(ipl_np[view_slice3], cmap="rainbow", vmin=0, vmax=1, alpha=0.3)

plt.show()
<Figure size 5000x3000 with 9 Axes>

Step 4: Compare IPL to ORMIR_XCT

Now compare segmentations using metrics.

To compute the metrics, we need to make sure our images are the same size. Use the SimpleITK resample procedural interface to ensure image sizes match.

dice, jaccard = calculate_dice_and_jaccard(ipl_np, ormir_np)
hausdorff = hausdorff_sitk(ipl_mask, trab_seg_ormir)

print("DICE: ", dice)
print("Jaccard: ", jaccard)
print("Mean Hausdorff Distance: ", hausdorff[0])
print("Maximum Hausdorff Distance: ", hausdorff[1])
DICE:  0.9169765300789231
Jaccard:  0.8466820484931374
Mean Hausdorff Distance:  0.052157967935281165
Maximum Hausdorff Distance:  3.9416047257362727

Notebook created using the template of the ORMIR community (version 1.0, 2023)

References
  1. Kuczynski, M. T., Neeteson, N. J., Stok, K. S., Burghardt, A. J., Hernandez, M. A. E., Vicory, J., Tse, J. J., Durongbhan, P., Bonaretti, S., Wong, A. K. O., Boyd, S. K., & Manske, S. L. (2024). ORMIR_XCT: A Python package for high resolution peripheral quantitative computed tomography image processing. Journal of Open Source Software, 9(97), 6084. 10.21105/joss.06084