dosma.MedicalVolume

class dosma.MedicalVolume(volume, affine, headers=None)[source]

The class for medical images.

Medical volumes use ndarrays to represent medical data. However, unlike standard ndarrays, these volumes have inherent spatial metadata, such as pixel/voxel spacing, global coordinates, rotation information, all of which can be characterized by an affine matrix following the RAS+ coordinate system. The code below creates a random 300x300x40 medical volume with scanner origin (0, 0, 0) and voxel spacing of (1,1,1):

>>> mv = MedicalVolume(np.random.rand(300, 300, 40), np.eye(4))

Medical volumes can also store header information that accompanies pixel data (e.g. DICOM headers). These headers are used to expose metadata, which can be fetched and set using get_metadata() and set_metadata(), respectively. Headers are also auto-aligned, which means that headers will be aligned with the slice(s) of data from which they originated, which makes Python slicing feasible. Currently, medical volumes support DICOM headers using pydicom when loaded with dosma.DicomReader.

>>> mv.get_metadata("EchoTime")  # Returns EchoTime
>>> mv.set_metadata("EchoTime", 10.0)  # Sets EchoTime to 10.0

Standard math and boolean operations are supported with other MedicalVolume objects, numpy arrays (following standard broadcasting), and scalars. Boolean operations are performed elementwise, resulting in a volume with shape as self.volume.shape. If performing operations between MedicalVolume objects, both objects must have the same shape and affine matrix (spacing, direction, and origin). Header information is not deep copied when performing these operations to reduce computational and memory overhead. The affine matrix (self.affine) is copied as it is lightweight and often modified.

2D images are also supported when viewed trivial 3D volumes with shape (H, W, 1):

>>> mv = MedicalVolume(np.random.rand(10,20,1), np.eye(4))

Many operations are in-place and modify the instance directly (e.g. reformat(inplace=True)). To allow chaining operations, operations that are in-place return self.

>>> mv2 = mv.reformat(ornt, inplace=True)
>>> id(mv2) == id(mv)
True

BETA: Medical volumes can interface with the gpu using the cupy library. Volumes can be moved between devices (see Device) using the .to() method. Only the volume data will be moved to the gpu. Headers and affine matrix will remain on the cpu. The following code moves a MedicalVolume to gpu 0 and back to the cpu:

>>> from dosma import Device
>>> mv = MedicalVolume(np.random.rand((10,20,30)), np.eye(4))
>>> mv_gpu = mv.to(Device(0))
>>> mv_cpu = mv.cpu()

Note, moving data across devices results in a full copy. Above, mv_cpu.volume and mv.volume do not share memory. Saving volumes and converting to other images (e.g. SimpleITK.Image) are only supported for cpu volumes. Volumes can also only be compared when on the same device. For example, both commands below will raise a RuntimeError:

>>> mv_gpu == mv_cpu
>>> mv_gpu.is_identical(mv_cpu)

While CuPy requires the current device be set using cp.cuda.Device(X).use() or inside the with context, MedicalVolume automatically sets the appropriate context for performing operations. This means the CuPy current device need to be the same as the MedicalVolume object. For example, the following still works:

>>> cp.cuda.Device(0).use()
>>> mv_gpu = MedicalVolume(cp.ones((3,3,3)), np.eye(4))
>>> cp.cuda.Device(1).use()
>>> mv_gpu *= 2

BETA: MedicalVolumes also have a limited NumPy/CuPy-compatible interface. Standard numpy/cupy functions that preserve array shapes can be performed on MedicalVolume objects:

>>> log_arr = np.log(mv)
>>> type(log_arr)
<class 'dosma.io.MedicalVolume'>
>>> exp_arr_gpu = cp.exp(mv_gpu)
>>> type(exp_arr_gpu)
<class 'dosma.io.MedicalVolume'>
Parameters
  • volume (array-like) – nD medical image.

  • affine (array-like) – 4x4 array corresponding to affine matrix transform in RAS+ coordinates. Must be on cpu (i.e. no cupy.ndarray).

  • headers (array-like[pydicom.FileDataset]) – Headers for DICOM files.

__init__(volume, affine, headers=None)[source]

Initialize self. See help(type(self)) for accurate signature.

Methods

__init__(volume, affine[, headers])

Initialize self.

astype(dtype, **kwargs)

Modifies dtype of self._volume.

clone([headers])

Clones the medical volume.

cpu()

Move to cpu.

from_sitk(image[, copy])

Constructs MedicalVolume from SimpleITK.Image.

get_metadata(key[, dtype, default])

Get metadata value from first header.

headers([flatten])

Returns headers.

is_identical(mv)

Check if another medical volume is identical.

is_same_dimensions(mv[, precision, err])

Check if two volumes have the same dimensions.

match_orientation(mv)

Reorient another MedicalVolume to orientation specified by self.orientation.

match_orientation_batch(mvs)

Reorient a collection of MedicalVolumes to orientation specified by self.orientation.

mean([axis, dtype, out, keepdims, where])

Compute the arithmetic mean along the specified axis.

reformat(new_orientation[, inplace])

Reorients volume to a specified orientation.

reformat_as(other[, inplace])

Reformat this to the same orientation as other.

round([decimals, affine])

Round array (and optionally affine matrix).

save_volume(file_path[, data_format])

Write volumes in specified data format.

set_metadata(key, value[, force])

Sets metadata for all headers.

sum([axis, dtype, out, keepdims, initial, where])

Compute the arithmetic sum along the specified axis.

to(device)

Move to device.

to_sitk([vdim])

Converts to SimpleITK Image.

Attributes

A

The pixel array.

affine

4x4 affine matrix for volume in current orientation.

device

The device the object is on.

dtype

The dtype of the ndarray.

ndim

The number of dimensions of the underlying ndarray.

orientation

Image orientation in standard orientation format.

pixel_spacing

Pixel spacing in order of current orientation.

scanner_origin

Scanner origin in global RAS+ x,y,z coordinates.

shape

The shape of the underlying ndarray.

volume

ndarray representing volume values.