Absolute Basics#
The main purpose of the pept
library is to provide a common, consistent foundation for PEPT-related algorithms, including tracer tracking, visualisation and post-processing tools - such that they can be used interchangeably, mixed and matched for different systems. Virtually any PEPT processing routine follows these steps:
Convert raw gamma camera / scanner data into 3D lines (i.e. the captured gamma rays, or lines of response - LoRs).
Take a sample of lines, locate tracer locations, then repeat for the next samples.
Separate out individual tracer trajectories.
Visualise and post-process trajectories.
For these algorithm-agnostic steps, pept
provides five base data structures upon which the rest of the library is built:
pept.LineData
: general 3D line samples, formatted as [time, x1, y1, z1, x2, y2, z2, extra…].pept.PointData
: general 3D point samples, formatted as [time, x, y, z, extra…].pept.Pixels
: single 2D pixellised space with physical dimensions, including fast line traversal.pept.Voxels
: single 3D voxellised space with physical dimensions, including fast line traversal.
All the data structures above are built on top of NumPy and integrate natively with the rest of the Python / SciPy ecosystem. The rest of the pept
library is organised into submodules:
pept.scanners
: converters between native scanner data and the base classes.pept.tracking
: radioactive tracer tracking algorithms, e.g. the Birmingham method, PEPT-ML, FPI.pept.plots
: PEPT data visualisation subroutines.pept.utilities
: general-purpose helpers, e.g.read_csv
,traverse3d
.pept.processing
: PEPT-oriented post-processing algorithms, e.g.occupancy2d
.
pept.LineData
#
Generally, PEPT Lines of Response (LoRs) are lines in 3D space, each
defined by two points, regardless of the geometry of the scanner used. This
class is used to wrap LoRs (or any lines!), efficiently yielding samples of
lines
of an adaptive sample_size
and overlap
.
It is an abstraction over PET / PEPT scanner geometries and data formats,
as once the raw LoRs (be they stored as binary, ASCII, etc.) are
transformed into the common LineData
format, any tracking, analysis or
visualisation algorithm in the pept
package can be used interchangeably.
Moreover, it provides a stable, user-friendly interface for iterating over
LoRs in samples - this is useful for tracking algorithms, as they
generally take a few LoRs (a sample), produce a tracer position, then
move to the next sample of LoRs, repeating the procedure. Using overlapping
samples is also useful for improving the tracking rate of the algorithms.
Here are some basic examples of creating and using LineData
samples - you’re
very much invited to copy and run them!
Initialise a LineData
instance containing 10 lines with a sample_size
of 3.
>>> import pept
>>> import numpy as np
>>> lines_raw = np.arange(70).reshape(10, 7)
>>> print(lines_raw)
[[ 0 1 2 3 4 5 6]
[ 7 8 9 10 11 12 13]
[14 15 16 17 18 19 20]
[21 22 23 24 25 26 27]
[28 29 30 31 32 33 34]
[35 36 37 38 39 40 41]
[42 43 44 45 46 47 48]
[49 50 51 52 53 54 55]
[56 57 58 59 60 61 62]
[63 64 65 66 67 68 69]]
>>> line_data = pept.LineData(lines_raw, sample_size = 3)
>>> line_data
pept.LineData (samples: 3)
--------------------------
sample_size = 3
overlap = 0
lines =
(rows: 10, columns: 7)
[[ 0. 1. ... 5. 6.]
[ 7. 8. ... 12. 13.]
...
[56. 57. ... 61. 62.]
[63. 64. ... 68. 69.]]
columns = ['t', 'x1', 'y1', 'z1', 'x2', 'y2', 'z2']
attrs = {}
Access samples using subscript notation. Notice how the samples are
consecutive, as overlap
is 0 by default.
>>> line_data[0]
pept.LineData (samples: 1)
--------------------------
sample_size = 3
overlap = 0
lines =
(rows: 3, columns: 7)
[[ 0. 1. ... 5. 6.]
[ 7. 8. ... 12. 13.]
[14. 15. ... 19. 20.]]
columns = ['t', 'x1', 'y1', 'z1', 'x2', 'y2', 'z2']
attrs = {}
>>> line_data[1]
pept.LineData (samples: 1)
--------------------------
sample_size = 3
overlap = 0
lines =
(rows: 3, columns: 7)
[[21. 22. ... 26. 27.]
[28. 29. ... 33. 34.]
[35. 36. ... 40. 41.]]
columns = ['t', 'x1', 'y1', 'z1', 'x2', 'y2', 'z2']
attrs = {}
Now set an overlap of 2; notice how the number of samples changes:
>>> len(line_data) # Number of samples
3
>>> line_data.overlap = 2
>>> len(line_data)
8