Metadata-Version: 2.4
Name: ctrecon
Version: 0.1.9
Summary: an interface and some tools for computed tomography reconstruction
Author-email: "J. Hoffman" <contact@jmh.lol>
Project-URL: Homepage, https://gitlab.com/hoffman-lab/ctrecon
Project-URL: Bug Tracker, https://gitlab.com/hoffman-lab/ctrecon/issues
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: numpy
Requires-Dist: matplotlib
Requires-Dist: pandas
Requires-Dist: smcore

# CTRECON: Some basic simulation tools for CT scanning

### Development note:

I'm recording this here so I can make sure to undo at a future timepoint.

As of 0.1.7, with the addition of the ResultStore a few dependencies have "polluted" 
the tree:
- pandas
- smcore

smcore is included for serialization/deserialization (convenient for blob storage)
pandas is is a nice tabular, familiar python interface to the SQLite backend.

Pandas may translate to a permanent dependency (about as close to a "fundamental"
python package as numpy or matplotlib). smcore is not a dependency and must be 
removed at some point in the future.  

The ResultStore is likely only a temporary resident in this package and is temporarily
here for quick deployment and testing.

## Motivation and Intent

More than "fast" or "efficient", I'm trying to bolt an interface on that feels
intuitive and reusable. Something I want to reach for when developing a
reserach question. The implemented math that backs this stuff should be swappable over
time as better implementations are developed.

The idea is to apply some sane logical structure the many components required
to go from simulated phantom, to CT sinogram, and back to image.  My hope is
that using the library feels like describing the experiment you're doing with
normal language, and your code makes it feel obvious what experiment is being
done.

- `phantoms` contains prebaked phantoms, as well as the tools to build your own
- `scanners` contains prebaked scan geometries, and the interface classes to
  build your own (and preserve compatibility with the rest of the toolchain).
- `points` contains different image types (collections of points for sampling)
  for reconstruction (such as pixel grids)
- `recon` contains code to perform reconstruction onto points objects

## Environment setup

The package is available exclusively via pip.  We strongly recommend using an
environment manager (virtualenv, conda, micromamba, uv, etc.) to track and
manage your dependencies.

```
pip install ctrecon
```

If you get a warning that matplotlib cannot show the window, you may also need
to install a different backend for matplotlib (e.g., `pip install PtQt5`).
Refer to the [matplotlib
documentation](https://matplotlib.org/stable/users/explain/figure/backends.html)
if you encounter any issues.

## Hello world example

This example comes from the package's "unit tests".  They're not actually unit tests
because they're interactive, but they're great sanity checks to see if and how
the package runs on your computer:

```
TODO
```


## BYO Phantom

This is the full definition of our `DebugPhantom`:

```python
class DebugPhantom(Phantom):
    def __init__(self):

        # tuneable elements, units are cm
        bg_attenuation = 0.0192
        ph_radius = 10.0

        elements = []

        bg = Circle(0, 0, ph_radius, bg_attenuation)

        findable_insert_1 = Circle(0, 5, 3.0, 0.04)
        findable_insert_2 = Circle(5, 0, 1.5, 0.04)

        # Intuitively, we draw from "bg" -> "fg"
        elements.append(bg)
        elements.append(findable_insert_1)
        elements.append(findable_insert_2)

        # When raycasting, its most efficient to intersect the fg first
        elements.reverse()

        # Save the elements of the phantom
        self.circles = elements
```

The concept is that we basically draw objects into the world space.
We calculate sinograms via raycasting and each step iterates through 
the list of objects to check if we're inside.  For that increment, 
we utilize the attenuation of the first object in the list that we
encounter and move on.

The same phantom can be completely described via CSV as well.  Note that the
row order sets the object "precedence" in the sinogram generation process.

`debug_phantom.csv`:

```csv
5,0,1.5,0.04
0,5,3.0,0.04
0,0,10.0,0.0192
```

You can save a phantom a CSV file:

```
from ctrecon import phantoms

ph = phantoms.DebugPhantom()
ph.to_csv('debug_phantom.csv')
```

A phantom can be instantiate from such a CSV:

```python
debug_ph = Phantom.from_csv("debug_phantom.csv")
```

This at least describes phantoms in a *mostly* portable manner.  Free to use
in other places and between research groups.

## Scanning a phantom

`scanners` provides basic geometries and specific implementations of some geometries.

In keeping with our goal of language-like interaction to perform a scan:

```python
ph = phantoms.Debug()
scanner = scanners.PencilBeam(...)
sinogram, metadata = scanner.scan(ph)
```

If a scanner is implemented correctly within our library, these three lines
(using any phantom we provide and any scanner) *must* produce a valid sinogram. 
Moreover, the defaults should hopefully suffice for many applications.

The reason this can work is that we follow the philosophy of "pick good
defaults" and require the scanner implementor to define a `default_protocol`.

```python
class Scanner:
    def default_protocol(self) -> Protocol:
        raise NotYetImplemented
```

However specific scan protocols (including truncated scanning, sparse view, etc.)
are among some of the most important ongoing research questions in the field.


### Protocols

While scanners record physical details about the relationships between x-ray
source and detector, as well as detector shape, etc., it doesn't desribe how
that hardware should be used to scan an object. 

How may projections we should acquire, at which angles, even "unconstrained"
acquisitions coming from random locations, etc. is all modeled via an
intermediate `Trajectory` data structure that represents a scan's geometric
*intent* in an abstract sense so that it can be applied to multiple specific
geometries.

Fundamentally, the `Scanner` object represents the physical constraints between
the source and the detector.

#### Telemetry

If the protocol is the intent, telemetry represents what was actually
*executed* by the scanner.  In a simulation context, Telemetry will generally
match the protocol exactly, however this abstraction layer could sit atop real
hardware.  Telemetry just allows us a dedicated path to report back information
about the scan that was conducted.


