Images

The Image class can be used to read and write images in DICOM, NIfTI, or NumPy format. These images can be plotted and compared.

To load the Image class:

from skrt import Image

Images will be processed into a consistent format:

  • The Image.data property contains a numpy array, which stores (y, x, z) in (row, column, slice) respectively. Note that numpy elements are indexed in order (row, column, slice); so if you did Image.data[i, j, k], i would correspond to y index, j would correspond to x index, k would correspond to z index.

  • The Image.affine property contains a 4x4 matrix that can convert a (row, column, slice) index to an (x, y, z) position. This will always be diagonal, so (0, 0) contains x voxel size etc, (0, 3) contains x origin.

  • The voxel_size and origin properties are the diagonal and third column, respectively; they give voxel sizes and origin position in order (x, y, z).

  • The n_voxels property containins the number of voxels in the (x, y, z) directions (same as Image.data.shape, but with 0 and 1 swapped).

Image properties shouldn’t usually be accessed directly. To ensure that their values are set, they should instead by accessed via getter methods: get_data(); get_affine(); get_voxel_size(), get_origin(), get_n_voxels().

In the standard dicom-style configuration (Left, Posterior, Superior):

  • The x-axis increases along each column, and points towards the patient’s left (i.e. towards the heart, away from the liver).

  • The y-axis increase down each row, and points from the patient’s front to back (posterior).

  • The z-axis increases along the slice index, and points from the patient’s feet to head (superior).

A canonical nifti-style array and affine can be obtained by running Image.get_nifti_array_and_affine(). By convention, this points in the same z direction but has x and y axes reversed (Right, Anterior, Superior). In the affine matrix, the x and y origins are therefore defined as being at the opposite end of the scale.

Note that positions can also be specified in terms of slice number:

  • For x and y, slice number is just numpy array index + 1 (slice number ranges from 1 - n_voxels, whereas array index ranges from 0 - n_voxels-1)

  • For z, by convention the slice numbers increases from 1 at the head to n_voxels at the feet, so it is in the opposite direction to the array index (convert as n_voxels[2] - idx).

Loading from a file

An image can be loaded from a dicom, nifti, or numpy file via:

im = Image(filepath)

If the dicom file is part of a series, any other files in that series in the same directory will also be loaded. Alternatively, you can give the path to a directory containing multiple dicom files. The first dicom file in that directory alphabetically will be loaded along with any other files in its series.

Loading from an array

An image can also be loaded from a numpy array. By default, it will be taken to have origin (0, 0, 0) and voxel sizes (1, 1, 1) mm; otherwise, these can be set manually, either via:

im = Image(array, voxel_size=(1.5, 1.5, 3), origin=(-100, -100, 40))

where the origin/voxel size lists are in order (x, y, z).

The origin and voxel sizes can also be specified via an affine matrix, e.g.

affine = np.array([
    [1.5, 0, 0, -100],
    [0, 1.5, 0, -100],
    [0, 0, 3, 40],
    [0, 0, 0, 1]
])
im = Image(array, affine=affine)

where the first row of the affine matrix contains the x voxel size and origin, second row contains y, third row contains z.

Plotting

To plot a slice of the image, you need to specify the orientation (x-y, y-z, or x-z; default x-y) and either the slice number, array index, or position in mm (by default, the central slice in the chosen orientation will be plotted). e.g.

im.plot('y-z', idx=5)

Writing out image data

Images can be written out with the Image.write(filename) function. The output filetype will be inferred from the filename.

Writing to dicom

If filename ends in .dcm or is a directory (i.e. has no extension), the image will be written in dicom format. Each x-y slice will be written to a separate file labelled by slice number, e.g. slice 1 (corresponding to [:, :, -1] in the image array) would be saved to 1.dcm.

The path to a dicom file from which to take the header can be specified via the header_source parameter. If no path is given but the input source for the Image was a dicom file, the header will be taken from the source. Otherwise (e.g. if the file was loaded from a nifti and no header_source is given), a brand new header with new UIDs will be created. In that case, you can set the following info for that header:

  • patient_id

  • modality

  • root_uid (an ID unique to your institue that will prefix the generated dicom UIDs so that they are globally unique; one can be obtained here: https://www.medicalconnections.co.uk/FreeUID/)

Writing to nifti

If filename ends in .nii or .nii.gz, the image will be written to nifti. The nifti will be in canonical format, i.e. in Right, Anterior, Superior configuration. (Note that this means the nifti you write out may not be the same as the one you read in).

Writing to a numpy array

If filename ends in .npy, the image array will be written to a numpy binary file. To write a canonical nifti-style array instead of the dicom-style array, set nifti_array=True. If set_geometry is True (which it is by default), a text file will be written to the same directory as the .npy file containing the voxel sizes and origin.