Skip to content

Commit fdf9bd1

Browse files
authored
Merge pull request #697 from askorikov/add-astra-support
Feat: add support for CT using ASTRA Toolbox
2 parents c1e3a7e + d7ecc1b commit fdf9bd1

19 files changed

Lines changed: 426 additions & 142 deletions

.github/workflows/build.yaml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,14 @@ jobs:
2424
run: |
2525
python -m pip install --upgrade pip setuptools
2626
pip install flake8 pytest
27-
pip install -r requirements-dev.txt
28-
pip install -r requirements-pyfftw.txt
27+
28+
if [[ "${{ matrix.platform }}" == ubuntu* ]]; then
29+
pip install -r requirements-dev.txt
30+
pip install -r requirements-pyfftw.txt
31+
else
32+
pip install -r requirements-dev-arm.txt
33+
pip install -r requirements-pyfftw.txt
34+
fi
2935
pip install -r requirements-torch.txt
3036
- name: Install pylops
3137
run: |

azure-pipelines.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ jobs:
6161

6262
- script: |
6363
python -m pip install --upgrade pip setuptools wheel django
64-
pip install -r requirements-dev.txt
64+
pip install -r requirements-dev-arm.txt
6565
pip install -r requirements-pyfftw.txt
6666
pip install -r requirements-torch.txt
6767
pip install .

docs/source/api/index.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,17 @@ Geophysical subsurface characterization
158158
prestack.PrestackWaveletModelling
159159

160160

161+
Medical imaging
162+
~~~~~~~~~~~~~~~
163+
164+
.. currentmodule:: pylops.medical
165+
166+
.. autosummary::
167+
:toctree: generated/
168+
169+
CT2D
170+
171+
161172
Solvers
162173
-------
163174
Template

docs/source/gpu.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,22 @@ Geophysical subsurface characterization:
579579
- |:white_check_mark:|
580580
- |:warning:|
581581

582+
583+
Medical:
584+
585+
.. list-table::
586+
:widths: 50 25 25 25
587+
:header-rows: 1
588+
589+
* - Operator/method
590+
- CPU
591+
- GPU with CuPy
592+
- GPU/TPU with JAX
593+
* - :class:`pylops.medical.ct.CT2D`
594+
- |:white_check_mark:|
595+
- |:red_circle:|
596+
- |:red_circle:|
597+
582598
.. warning::
583599

584600
1. The JAX backend of the :class:`pylops.signalprocessing.Convolve1D` operator

docs/source/installation.rst

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,21 @@ More details about the installation process for the different optional dependenc
329329
in the following (an asterisc is used to indicate those dependencies that are automatically installed
330330
when installing PyLops from conda-forge or via ``pip install pylops[advanced]``):
331331

332+
ASTRA
333+
-----
334+
`ASTRA <https://www.astra-toolbox.com>`_ is library used to perform computerized
335+
tomography. It is used in PyLops in the operator :py:class:`pylops.medical.CT2D`
336+
337+
To use this library, install it manually either via ``conda``:
338+
339+
.. code-block:: bash
340+
>> conda install --channel astra-toolbox astra-toolbox
341+
342+
or via pip:
343+
344+
.. code-block:: bash
345+
>> pip install astra-toolbox
346+
332347
333348
dtcwt*
334349
------
@@ -345,13 +360,13 @@ Install it via ``pip`` with:
345360
``dtcwt`` does not support NumPy 2 yet, so make sure you use NumPy 1.x
346361
to be able to use the ``DTCWT`` operator.
347362

363+
348364
Devito
349365
------
350366
`Devito <https://github.com/devitocodes/devito>`_ is a library used to solve PDEs via
351367
the finite-difference method. It is used in PyLops to compute wavefields
352368
:py:class:`pylops.waveeqprocessing.AcousticWave2D`
353369

354-
355370
Install it via ``pip`` with
356371

357372
.. code-block:: bash

environment-dev-arm.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dependencies:
1313
- sympy
1414
- pymc>=5
1515
- pytensor
16+
- astra-toolbox
1617
- matplotlib
1718
- ipython
1819
- pytest

environment-dev.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dependencies:
1313
- sympy
1414
- pymc>=5
1515
- pytensor
16+
- astra-toolbox
1617
- matplotlib
1718
- ipython
1819
- pytest

pylops/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
Linear Operators for Seismic Reservoir Characterization
3939
waveeqprocessing
4040
Linear Operators for Wave Equation oriented processing
41+
medical
42+
Linear Operators for Medical imaging
4143
optimization
4244
Solvers
4345
utils
@@ -56,6 +58,7 @@
5658
from . import (
5759
avo,
5860
basicoperators,
61+
medical,
5962
optimization,
6063
signalprocessing,
6164
utils,

pylops/medical/__init__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
"""
2+
Medical Operators
3+
=================
4+
5+
The subpackage medical provides linear operators and applications
6+
aimed at solving various inverse problems in the area of Medical Imaging.
7+
8+
A list of operators present in pylops.medical:
9+
10+
CT2D 2D Computerized Tomography.
11+
12+
"""
13+
14+
from .ct import *
15+
16+
__all__ = [
17+
"CT2D",
18+
]

pylops/medical/ct.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
__all__ = [
2+
"CT2D",
3+
]
4+
5+
import logging
6+
from typing import Optional
7+
8+
import numpy as np
9+
10+
from pylops import LinearOperator
11+
from pylops.utils import deps
12+
from pylops.utils.decorators import reshaped
13+
from pylops.utils.typing import DTypeLike, InputDimsLike, NDArray
14+
15+
logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.WARNING)
16+
17+
18+
astra_message = deps.astra_import("the astra module")
19+
20+
if astra_message is None:
21+
import astra
22+
23+
24+
class CT2D(LinearOperator):
25+
r"""2D Computerized Tomography
26+
27+
Apply 2D computerized tomography operator to model to obtain a
28+
2D sinogram.
29+
30+
Note that the CT2D operator is an overload of the ``astra``
31+
implementation of the tomographic operator. Refer to
32+
https://www.astra-toolbox.com/ for a detailed description of the
33+
input parameters.
34+
35+
Parameters
36+
----------
37+
dims : :obj:`list` or :obj:`int`
38+
Number of samples for each dimension. Must be 2-dimensional and of size :math:`n_y \times n_x`
39+
det_width : :obj:`float`
40+
Detector width
41+
det_count : :obj:`int`
42+
Number of detectors
43+
thetas : :obj:`numpy.ndarray`
44+
Vector of angles in degrees
45+
proj_geom_type : :obj:`str`, optional
46+
Type of projection geometry (``parallel`` or ``fanflat``)
47+
source_origin_dist : :obj:`float`, optional
48+
Distance between source and origin (only for ``proj_geom_type=fanflat``)
49+
origin_detector_dist : :obj:`float`, optional
50+
Distance between origin and detector along the source-origin line
51+
(only for "proj_geom_type=fanflat")
52+
projector_type : :obj:`int`, optional
53+
Type of projection geometry (``strip``, or ``line``, or ``linear``)
54+
dtype : :obj:`str`, optional
55+
Type of elements in input array.
56+
name : :obj:`str`, optional
57+
Name of operator (to be used by :func:`pylops.utils.describe.describe`)
58+
59+
Attributes
60+
----------
61+
shape : :obj:`tuple`
62+
Operator shape
63+
explicit : :obj:`bool`
64+
Operator contains a matrix that can be solved
65+
explicitly (``True``) or not (``False``)
66+
67+
Notes
68+
-----
69+
The CT2D operator applies parallel or fan beam computerized tomography operators
70+
to 2-dimensional objects and produces their corresponding sinograms.
71+
72+
Mathematically the forward operator can be described as [1]_:
73+
74+
.. math::
75+
s(r,\theta; i) = \int_l i(l(r,\theta)) dl
76+
77+
where :math:`l(r,\theta)` is the summation line and :math:`i(x, y)`
78+
is the intensity map of the model. Here, :math:`\theta` refers to the angle
79+
between the y-axis (:math:`y`) and the summation line and :math:`r` is
80+
the distance from the origin of the summation line.
81+
82+
.. [1] http://people.compute.dtu.dk/pcha/HDtomo/astra-introduction.pdf
83+
84+
"""
85+
86+
def __init__(
87+
self,
88+
dims: InputDimsLike,
89+
det_width: int,
90+
det_count: float,
91+
thetas: NDArray,
92+
proj_geom_type: Optional[str] = "parallel",
93+
source_origin_dist: float = None,
94+
origin_detector_dist: float = None,
95+
projector_type: Optional[str] = "strip",
96+
dtype: DTypeLike = "float64",
97+
name: str = "C",
98+
) -> None:
99+
if astra_message is not None:
100+
raise NotImplementedError(astra_message)
101+
102+
# create volume and projection geometries
103+
self.vol_geom = astra.create_vol_geom(dims)
104+
if proj_geom_type == "parallel":
105+
self.proj_geom = astra.create_proj_geom(
106+
proj_geom_type, det_width, det_count, thetas
107+
)
108+
else:
109+
self.proj_geom = astra.create_proj_geom(
110+
proj_geom_type,
111+
det_width,
112+
det_count,
113+
thetas,
114+
source_origin_dist,
115+
origin_detector_dist,
116+
)
117+
118+
# create projector
119+
self.proj_id = astra.create_projector(
120+
projector_type, self.proj_geom, self.vol_geom
121+
)
122+
super().__init__(
123+
dtype=np.dtype(dtype), dims=dims, dimsd=(len(thetas), det_count), name=name
124+
)
125+
126+
@reshaped
127+
def _matvec(self, x):
128+
y_id, y = astra.create_sino(x, self.proj_id)
129+
astra.data2d.delete(y_id)
130+
return y
131+
132+
@reshaped
133+
def _rmatvec(self, x):
134+
y_id, y = astra.create_backprojection(x, self.proj_id)
135+
astra.data2d.delete(y_id)
136+
return y
137+
138+
def __del__(self):
139+
astra.projector.delete(self.proj_id)

0 commit comments

Comments
 (0)