Visualise Images#

This notebook demonstrates how to display an image along with some contours, scalar overlays and bounding boxes.

Import Modules#

The following cell imports the modules needed for this example. This ‘ImageVisualiser’ class does most of the heavy lifting regarding visualisation. It has a number of helpful functions, and makes adding additional information to an image (relatively) pain-free.

[1]:
# Check if platipy is installed, if not install it.
try:
    import platipy
except:
    !pip install platipy
    import platipy

# The ImageVisualiser clas
from platipy.imaging import ImageVisualiser

# Function to grab some test data
from platipy.imaging.tests.data import get_lung_nifti

# Import some common libraries which we need
import numpy as np
import SimpleITK as sitk
from pathlib import Path
import matplotlib
import matplotlib.pyplot as plt

# Some utilities we use for generating interesting data
from platipy.imaging.registration.utils import convert_mask_to_reg_structure, apply_transform
from platipy.imaging.registration.deformable import fast_symmetric_forces_demons_registration
from platipy.imaging.generation.dvf import generate_field_asymmetric_extend
from platipy.imaging.label.utils import get_com
from platipy.imaging.utils.crop import label_to_roi

Download Test Data#

Some Lung test data from LCTSC is fetched here for use in this example notebook.

[2]:
input_directory = get_lung_nifti()

Read in the data#

  • ct_image: the x-ray computed tomography scan (3D)

  • contours: a dictionary of binary masks

[3]:
pat_directory = input_directory.joinpath("LCTSC-Test-S1-101")

# Read in the CT image
ct_filename = next(pat_directory.glob("**/IMAGES/*.nii.gz"))
ct_image = sitk.ReadImage(ct_filename.as_posix())

# Read in the RTStruct contours as binary masks
contour_filename_list = list(pat_directory.glob("**/STRUCTURES/*.nii.gz"))

contours = {}
for contour_filename in contour_filename_list:
    _name = contour_filename.name.split(".nii.gz")[0].split("RTSTRUCT_")[-1]

    contours[_name] = sitk.ReadImage(contour_filename.as_posix())

Display Image with Contours#

Here we define ‘cut’, the location of orthogonal slices

  • cut: ( transaxial slice location or first axis, coronal slice location or second axis, saggital slice location or third axis, )

[4]:
image_visualiser = ImageVisualiser(ct_image, cut=(50,256,256))
image_visualiser.add_contour(contours)
fig = image_visualiser.show()
../_images/_examples_visualise_7_0.png

Display Image without Contours#

Here we change the intensity windowing, for example to better visualise lung tissue

  • window: ( minimum intensity, range in intensity )

It is also possible to use difference colormaps to display the image.

We can also change the field of view, for example using to the lung volumes.

We also don’t need to set ‘cut’, in which case the middle of the image volume is displayed.

[5]:
image_visualiser = ImageVisualiser(ct_image, window=(-900, 1100), colormap=matplotlib.colormaps.get_cmap("terrain"), figure_size_in=6)

image_visualiser.set_limits_from_label(contours["LUNG_L"] + contours["LUNG_R"])

fig = image_visualiser.show()
../_images/_examples_visualise_9_0.png

Display Image using Projection#

Another way to visualise a 3D image volume is using a projection. Here we demonstrate a mean intensity projection.

[6]:
image_visualiser = ImageVisualiser(ct_image, projection=True)

image_visualiser.add_bounding_box(label_to_roi(contours["HEART"], return_as_list=True), name="HEART BOUNDING BOX")

fig = image_visualiser.show()
../_images/_examples_visualise_11_0.png

Display Scalar Field on Image#

There are also tools to visualise scalar fields. Here is an example using a normalised distance map, which we compute for the heart volume.

[7]:
ndm_heart = convert_mask_to_reg_structure(contours["HEART"], expansion=1)

image_visualiser = ImageVisualiser(ct_image, cut=get_com(contours["HEART"]), figure_size_in=6)

image_visualiser.add_scalar_overlay(ndm_heart, name="Normalised distance from edge", discrete_levels=10)
image_visualiser.add_contour(contours["HEART"], name="Heart", show_legend=True)

image_visualiser.set_limits_from_label(contours["HEART"], expansion=30)

fig = image_visualiser.show()
../_images/_examples_visualise_13_0.png

Overlay Vector Fields on Image#

We can also display vector fields. Here we compute one, simulated by expanding the heart volume by 5mm (for this simulation, we also use some nice tools in platipy).

[8]:
contours["HEART_EXPAND"], tfm_expansion, dvf_expansion = generate_field_asymmetric_extend(contours["HEART"])
[9]:
image_visualiser = ImageVisualiser(ct_image, cut=get_com(contours["HEART"]), figure_size_in=7)

image_visualiser.add_vector_overlay(
    dvf_expansion,
    name="DVF magnitude [mm]",
    colormap=matplotlib.colormaps.get_cmap("gnuplot2"),
    alpha=0.75,
    arrow_scale=1,
    arrow_width=1,
    subsample=(2,6,6),
    color_function='magnitude',
    show_colorbar=True
)

image_visualiser.add_contour(contours["HEART_EXPAND"], name = "Heart (expanded)", color="#880088", linewidth=5)
image_visualiser.add_contour(contours["HEART"], name = "Heart (original)", color="#88FF88", linewidth=5)

image_visualiser.set_limits_from_label(contours["HEART_EXPAND"], expansion=40)

fig = image_visualiser.show()
../_images/_examples_visualise_16_0.png

Compare two Images#

We can also visualise comparisons between images. This can be helpful for checking registration performance visually.

In this example, we deform the ct_image using the transformation above (heart expansion).

[10]:
ct_image_deformed = apply_transform(
    input_image=ct_image,
    reference_image=None,
    transform=tfm_expansion,
    default_value=-1000,
    interpolator=sitk.sitkBSpline,
)

image_visualiser = ImageVisualiser(ct_image, cut=get_com(contours["HEART"]), figure_size_in=7, window=(-40, 200))

image_visualiser.add_comparison_overlay(ct_image_deformed)

image_visualiser.set_limits_from_label(contours["HEART_EXPAND"], expansion=40)

fig = image_visualiser.show()
../_images/_examples_visualise_18_0.png

Combine Visualisations#

For experienced matplotlib users, it is also possible to customise the figure after it has been created. We can add test, more axes, etc. as usual…

[11]:
image_visualiser = ImageVisualiser(ct_image, cut=get_com(contours["HEART"]), figure_size_in=9)

image_visualiser.add_scalar_overlay(ndm_heart, name="Normalised distance from edge", discrete_levels=10)

image_visualiser.add_contour(contours["HEART_EXPAND"], name = "Heart (expanded)", color="#880088", linewidth=5)
image_visualiser.add_contour(contours["HEART"], name = "Heart (original)", color="#88FF88", linewidth=5)

image_visualiser.add_vector_overlay(
    dvf_expansion,
    name="DVF magnitude [mm]",
    colormap=matplotlib.colormaps.get_cmap("gnuplot2"),
    alpha=0.5,
    arrow_scale=1,
    arrow_width=1,
    subsample=(2,6,6),
    color_function='magnitude',
    show_colorbar=True
)

image_visualiser.set_limits_from_label(contours["HEART_EXPAND"], expansion=40)

fig = image_visualiser.show()

# Here we can add stuff to the figure

fig.text(0.99, 0.99, "TEST VISUALISATION", va="top", ha="right", fontsize=14)
fig.text(0.99, 0.96, "using platipy", va="top", ha="right", fontsize=12, style="italic", c="red");

test_axis = fig.add_axes( (0.85, 0.55, 0.1, 0.1) )
test_axis.hist(np.random.random(1000), histtype="stepfilled", fc = "#1144EE", ec=None)
test_axis.set_xlabel("Results")

# Optionally, save the figure
# fig.savefig("./output.jpeg", dpi=500)
[11]:
Text(0.5, 0, 'Results')
../_images/_examples_visualise_20_1.png
[ ]: