a b/docs/source/guide_basic.rst
1
.. _basic_usage:
2
3
.. py:currentmodule:: dosma
4
5
**This guide is still under construction**
6
7
Basic Usage
8
-----------
9
10
Dosma is designed for simple imaging I/O, registration, quantitative fitting, and AI-based image processing. 
11
12
Dosma does not bundle Tensorflow and Keras installation by default.
13
To enable  support, you must install these libraries as an additional step.
14
15
We use the following abbreviations for libraries:
16
17
>>> import numpy as np
18
>>> import dosma as dm
19
20
21
Image I/O
22
=========================
23
24
Dosma provides data readers and writers to allow you to read/write image data stored in NIfTI and DICOM standards.
25
These I/O tools create or write from the Dosma image class :class:`MedicalVolume`.
26
27
For example to load a DICOM image series, which has multiple echos, with each echo corresponding to a volume,
28
we can do:
29
30
>>> dr = dm.DicomReader(num_workers=1, verbose=True) 
31
>>> dr.load("/path/to/dicom/folder", group_by="EchoNumbers")
32
33
We can also load specific files in the image series:
34
35
>>> dr.load(["file1", "file2", ...], group_by="EchoNumbers")
36
37
DICOM image data often has associated metadata. :class:`MedicalVolume` makes it easy to get
38
and set metadata:
39
40
>>> volume = volumes[0]  # first echo time
41
>>> volume.get_metadata("EchoTime", float)
42
10.0
43
>>> volume.set_metadata("EchoTime", 20)
44
>>> volume.get_metadata("EchoTime", float)
45
20.0
46
47
Similarly, to load a NIfTI volume, we use the :class:`NiftiReader` class:
48
49
>>> nr = dm.NiftiReader()
50
>>> volume = nr.load("/path/to/nifti/file.nii.gz")
51
52
NIfTI volumes can also be loaded in memmap mode. This makes loading much faster and allows easy interaction
53
with larger-than-memory arrays. Only when the volume is modified will the volume
54
be loaded into memory and modified.
55
56
>>> volume = nr.load("/path/to/nifti/file", mmap=True)
57
58
Images in all supported data formats can also be loaded and written using ``dosma.read`` and ``dosma.write``:
59
60
>>> import dosma as dm
61
>>> dm.load("/path/to/dicom/folder", group_by="EchoNumbers")
62
>>> dm.load("/path/to/nifti/file.nii.gz", mmap=True)
63
64
65
Reformatting Images
66
=========================
67
68
Given the multiple different orientation conventions used by different image formats and libraries,
69
reformatting medical images can be difficult to keep track of. Dosma simplifies this by introducing
70
an unambiguous convention for image orientation based on the RAS+ coordinate system, in which all
71
directions point to the increasing direction.
72
73
To reformat a :class:`MedicalVolume` instance (``mv``) such that the dimensions correspond to
74
superior -> inferior, anterior -> posterior, left -> right, we can do:
75
76
>>> mv = mv.reformat(("SI", "AP", "LR"))
77
78
To perform the operation in-place (i.e. modifying the existing instance), we can do:
79
80
>>> mv = mv.reformat(("SI", "AP", "LR"), inplace=True)
81
82
Note, in-place reformatting returns the same :class:`MedicalVolume` object that was modified
83
in-place (i.e. ``self``) to allow chaining methods together.
84
85
We may also want to reformat images to be in the same orientation as other images:
86
87
>>> mv = mv.reformat_as(other_image)
88
89
90
Image Slicing and Arithmetic Operations
91
========================================
92
93
:class:`MedicalVolume` supports some array-like functionality, including Python arithmetic
94
operations (``+``, ``-``, ``**``, ``/``, ``//``), NumPy shape-preserving operations
95
(e.g. ``np.exp``, ``np.log``, ``np.pow``, etc.), and slicing.
96
97
>>> mv += 5
98
>>> mv = mv * mv / mv
99
>>> mv = np.exp(mv)
100
>>> mv = mv[:5, :6, :7]
101
102
Note, in order to preserve dimensions, slicing cannot be used to reduce dimensions.
103
For example, the first line will throw an error; the second will not:
104
105
>>> mv = mv[2]
106
IndexError: Scalar indices disallowed in spatial dimensions; Use `[x]` or `x:x+1`
107
>>> mv[2:3]
108
109
110
NumPy Interoperability
111
========================================
112
113
In addition to standard shape-preserving universal functions (ufuncs) described above,
114
:class:`MedicalVolume` also support a subset of other numpy functions that, like the ufuncs,
115
operate on the pixel data in the medical volume:
116
117
- Boolean Functions: :func:`numpy.all`, :func:`numpy.any`, :func:`numpy.where`
118
- Statistics functions: :func:`numpy.mean`, :func:`numpy.sum`, :func:`numpy.std`, :func:`numpy.amin`, :func:`numpy.amax`, :func:`numpy.argmax`, :func:`numpy.argmin`
119
- Rounding functions: :func:`numpy.round`, :func:`numpy.around`, :func:`numpy.round_`
120
- NaN functions: :func:`numpy.nanmean`, :func:`numpy.nansum`, :func:`numpy.nanstd`, :func:`numpy.nan_to_num`
121
122
For example, ``np.all(mv)`` is equivalent to ``np.all(mv.volume)``. Note, headers are not deep copied.
123
NumPy operations that reduce spatial dimensions are not supported. For example, a 3D volume ``mv`` cannot
124
be summed over any two of the first three axes:
125
126
>>> np.sum(mv, 0)  # this will raise an error
127
>>> np.sum(mv)  # this will return a scalar
128
129
130
(BETA) Choosing A Computing Device
131
========================================
132
133
Dosma provides a device class :class:`dosma.Device`, which allows you to specify which device
134
to use for :class:`MedicalVolume` operations. It extends the Device class from `CuPy <https://cupy.dev/>`_.
135
To enable GPU computing support, install the correct build for CuPy on your machine.
136
137
To move a MedicalVolume to GPU 0, you can use the :meth:`MedicalVolume.to` method:
138
139
>>> mv_gpu = mv.to(dm.Device(0))
140
141
You can also move the image back to the cpu:
142
143
>>> mv_cpu = mv_gpu.cpu()  # or mv_gpu.to(dm.Device(-1))
144
145
If the device is already on the specified device, the same object is returned.
146
Note, some functionality such as curve fitting (:class:`dosma.curve_fit`), image registration,
147
and image I/O are not supported with images on the GPU.
148
149
150
(ALPHA) Multi-Library Interoperability
151
========================================
152
153
:class:`MedicalVolume` is also interoperable with popular image data structures
154
with zero-copy, meaning array data will not be copied. Structures currently include the
155
SimpleITK Image, Nibabel Nifti1Image, and PyTorch tensors.
156
157
For example, we can use the :meth:`MedicalVolume.to_sitk` method to convert a MedicalVolume
158
to a SimpleITK image:
159
160
>>> sitk_img = mv.to_sitk()
161
162
For PyTorch tensors, the zero-copy also applies to tensors on the GPU. Using ``mv_gpu``,
163
which is on GPU 0, from the previous section, we can do:
164
165
>>> torch_tensor = mv_gpu.to_torch()
166
>>> torch.device
167
cuda:0