Source code for pept

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# File   : __init__.py
# License: GNU v3.0
# Author : Andrei Leonard Nicusan <a.l.nicusan@bham.ac.uk>
# Date   : 19.08.2019


'''
PEPT
====

A Python library that unifies Positron Emission Particle Tracking (PEPT)
research, including tracking, simulation, data analysis and visualisation
tools.

Extended Summary
----------------
This Python library integrates all the tools necessary to perform research
using Positron Emission Particle Tracking (PEPT). The library includes
algorithms for the location, identification and tracking of particles, in
addition to tools for visualisation and analysis, and utilities allowing the
realistic simulation of PEPT data.

PEPT is a technique developed at the University of Birmingham [1]_ which allows
the non-invasive, three-dimensional tracking of one or more 'tracer' particles
through particulate, fluid or multiphase systems. The technique allows particle
or fluid motion to be tracked with sub-millimetre accuracy and sub-millisecond
temporal resolution and, due to its use of highly-penetrating 511keV gamma
rays, can be used to probe the internal dynamics of even large, dense,
optically opaque systems - making it ideal for industrial as well as
scientific applications.

PEPT is performed by radioactively labelling a particle with a positron-
emitting radioisotope such as fluorine-18 (18F) or gallium-66 (66Ga), and using
the back-to-back gamma rays produced by electron-positron annihilation events
in and around the tracer to triangulate its spatial position. Each detected
gamma ray represents a line of response (LoR) .

Subpackages Provided
--------------------
The `pept` package provides four base classes at its root - `PointData`,
`LineData`, `Pixels`, and `Voxels` - acting as common data formats that the
rest of the package will use for its tracking, analysis and visualisation
algorithms. Thus any subroutine integrated in the `pept` package can be used
interchangeably with new PET / PEPT scanner geometries, data formats or novel
algorithms.

Performance
-----------
Significant effort has been put into making the algorithms in this package as
fast as possible. The most compute-intensive parts have been implemented in
`C` / `Cython` and parallelised, where possible, using `joblib` and
`concurrent.futures.ThreadPoolExecutor`. For example, using the `peptml`
subpackage [2]_, analysing 1,000,000 LoRs on the author's machine (mid 2012
MacBook Pro) takes ~26 seconds.

Citing
------
If you used this codebase or any software making use of it in a scientific
publication, we ask you to cite the following paper:

    Nicuşan AL, Windows-Yule CR. Positron emission particle tracking using
    machine learning. Review of Scientific Instruments. 2020 Jan 1;91(1):013329
    https://doi.org/10.1063/1.5129251

Licensing
---------
The `pept` package is GNU v3.0 licensed.
Copyright (C) 2019-2021 the pept developers.

References
----------
Papers presenting PEPT algorithms included in this library: [1]_, [2]_, [3]_.

.. [1] Parker DJ, Broadbent CJ, Fowles P, Hawkesworth MR, McNeil P. Positron
   emission particle tracking-a technique for studying flow within engineering
   equipment. Nuclear Instruments and Methods in Physics Research Section A:
   Accelerators, Spectrometers, Detectors and Associated Equipment. 1993
   Mar 10;326(3):592-607.
.. [2] Nicuşan AL, Windows-Yule CR. Positron emission particle tracking using
   machine learning. Review of Scientific Instruments. 2020 Jan 1;91(1):013329.
.. [3] Wiggins C, Santos R, Ruggles A. A feature point identification method
   for positron emission particle tracking with multiple tracers. Nuclear
   Instruments and Methods in Physics Research Section A: Accelerators,
   Spectrometers, Detectors and Associated Equipment. 2017 Jan 21;843:22-8.

Examples
--------
You can download data samples from the UoB Positron Imaging Centre's
Repository (`link <https://bit.ly/pept-example-data-repo>`_). A
small but complete analysis script using the PEPT-ML algorithm is given below.

First import the `pept` package and read in lines of response (LoRs) from our
online repository. We'll use a sample of real data from an experiment conducted
at Birmingham - two tracers rotating at 42RPM.

.. code-block:: python

    import pept

    lors_raw = pept.utilities.read_csv(
        ("https://raw.githubusercontent.com/uob-positron-imaging-centres/"
         "example_data/master/sample_2p_42rpm.csv"),  # Concatenate long string
        skiprows = 16                                 # Skip file header
    )

Our data is in a format typical of the parallel screens PEPT detector from
Birmingham. Convert it to our package's scanner-agnostic line format; we'll
iterate through them one sample at a time, tracking the tracer locations:

.. code-block:: python

    lors = pept.scanners.ParallelScreens(
        lors_raw, screen_separation = 712, sample_size = 200
    )

Now it's time for some machine learning! Import the `peptml` package and
transform the LoRs into *cutpoints* that we'll use to find tracer locations.

.. code-block:: python

    from pept.tracking import peptml
    cutpoints = peptml.Cutpoints(lors, max_distance = 0.15)

Create a *clusterer* and use the cutpoints to find the centres of the tracers
as they move through our system:

.. code-block:: python

    clusterer = peptml.HDBSCANClusterer()
    centres = clusterer.fit(cutpoints)

This data looks alright (we'll plot everything in a minute), but we can do
better. We can iterate through the centres once more, clustering them again,
to get smooth, tight trajectories; for this, we'll set a small sample size and
large overlap:

.. code-block:: python

    centres.sample_size = 60
    centres.overlap = 59
    centres_2pass = clusterer.fit(centres)

One more step! The locations of the two tracers tracked above are intertwined;
we need to separate them into individual trajectories. We have a module for
just that:

.. code-block:: python

    import pept.tracking.trajectory_separation as tsp
    points_window = 10
    trajectory_cut_distance = 20
    trajectories = tsp.segregate_trajectories(
        centres_2pass, points_window, trajectory_cut_distance
    )

Finally, let's plot some LoRs, the tracer centres after one pass of clustering
and after the second pass of clustering, and the separated trajectories. Let's
use Plotly to produce some beautiful, interactive, 3D graphs:

.. code-block:: python

    from pept.plots import PlotlyGrapher
    subplot_titles = ["Lines of Response (LoRs)", "HDBSCAN Clustering",
                      "2-pass Clustering", "Separated Trajectories"]
    grapher = PlotlyGrapher(rows = 2, cols = 2, zlim = [0, 712],
                            subplot_titles = subplot_titles)

    grapher.add_lines(lors.lines[:400])             # Only the first 400 LoRs
    grapher.add_points(centres, col = 2)
    grapher.add_points(centres_2pass, row = 2)
    grapher.add_points(trajectories, row = 2, col = 2)

    grapher.show()

A more in-depth tutorial is available on
`Google Colab <https://bit.ly/pept-tutorial-1>`_.

'''


# Import base data structures
from    .base           import  TimeWindow
from    .base           import  AdaptiveWindow

from    .base           import  LineData
from    .base           import  PointData
from    konigcell       import  Pixels
from    konigcell       import  Voxels

# Import other general-purpose subroutines
from    .base           import  Pipeline

# Import subpackages
from    .               import  processing
from    .               import  scanners
from    .               import  simulation
from    .               import  tracking
from    .               import  utilities
from    .               import  plots

# Import package version
from    .__version__    import  __version__


# Define binary / pickled data save / load functions
import  pickle
from    .utilities      import  read_csv


# Backwards compatibility
visualisation = plots




[docs]def save(filepath, obj): '''Save an object `obj` instance as a binary file at `filepath`. Saves the full object state, including e.g. the inner `.lines` NumPy array, `sample_size`, etc. in a fast, portable binary format. Load back the object using the `pept.load` method. Parameters ---------- filepath : filename or file handle If filepath is a path (rather than file handle), it is relative to where python is called. obj : object Any - tipically PEPT-oriented - object to be saved in the binary `pickle` format. Examples -------- Save a `LineData` instance, then load it back: >>> lines = pept.LineData([[1, 2, 3, 4, 5, 6, 7]]) >>> pept.save("lines.pickle", lines) >>> lines_reloaded = pept.load("lines.pickle") ''' with open(filepath, "wb") as f: pickle.dump(obj, f)
[docs]def load(filepath): '''Load a binary saved / pickled object from `filepath`. Most often the full object state was saved using the `pept.save` method. Parameters ---------- filepath : filename or file handle If filepath is a path (rather than file handle), it is relative to where python is called. Returns ------- object The loaded Python object instance (e.g. `pept.LineData`). Examples -------- Save a `LineData` instance, then load it back: >>> lines = pept.LineData([[1, 2, 3, 4, 5, 6, 7]]) >>> pept.save("lines.pickle", lines) >>> lines_reloaded = pept.load("lines.pickle") ''' with open(filepath, "rb") as f: obj = pickle.load(f) return obj
__all__ = [ 'read_csv', 'save', 'load', 'TimeWindow', 'AdaptiveWindow', 'LineData', 'PointData', 'Pixels', 'Voxels', 'Pipeline', 'processing', 'scanners', 'simulation', 'tracking', 'utilities', 'plots', ] __author__ = "The pept developers" __credits__ = [ "Andrei Leonard Nicusan (University of Birmingham)", "Dr. Kit Windows-Yule (University of Birmingham)", "Dr. Sam Manger (University of Birmingham)", "Matthew Herald (University of Birmingham)", "Chris Jones (University of Birmingham)", "Prof. David Parker (University of Birmingham)", "Dr. Antoine Renaud (University of Edinburgh)", "Dr. Cody Wiggins (Virginia Commonwealth University", ] __license__ = "GNU v3.0" __maintainer__ = "Andrei Leonard Nicusan" __email__ = "a.l.nicusan@bham.ac.uk" __status__ = "Beta"