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()andset_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 usingpydicomwhen loaded withdosma.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
MedicalVolumeobjects, numpy arrays (following standard broadcasting), and scalars. Boolean operations are performed elementwise, resulting in a volume with shape asself.volume.shape. If performing operations betweenMedicalVolumeobjects, 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
cupylibrary. Volumes can be moved between devices (seeDevice) 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.volumeandmv.volumedo 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 thewithcontext,MedicalVolumeautomatically sets the appropriate context for performing operations. This means the CuPy current device need to be the same as theMedicalVolumeobject. 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
AThe pixel array.
affine4x4 affine matrix for volume in current orientation.
deviceThe device the object is on.
dtypeThe
dtypeof the ndarray.ndimThe number of dimensions of the underlying ndarray.
orientationImage orientation in standard orientation format.
pixel_spacingPixel spacing in order of current orientation.
scanner_originScanner origin in global RAS+ x,y,z coordinates.
shapeThe shape of the underlying ndarray.
volumendarray representing volume values.