Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Docs draft #40

Open
wants to merge 2 commits into
base: DCE_DRO
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs-mk/docs/references/dro/dro_read_mri.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# osipi.dro.read_mri_from_dicom

::: osipi.DRO.read_dicom_slices_as_4d_signal
3 changes: 3 additions & 0 deletions docs-mk/docs/references/dro/fit_models/extended_tofts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Extended Tofts Model

:::osipi.DRO.extended_tofts_model
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Forward Modified Tofts Model

:::osipi.DRO.ForwardsModTofts
name: ForwardsModTofts
3 changes: 3 additions & 0 deletions docs-mk/docs/references/dro/fit_models/modified_tofts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Modified Tofts Model

:::osipi.DRO.modifiedToftsMurase
File renamed without changes.
3 changes: 3 additions & 0 deletions docs-mk/docs/references/dro/signal_enhance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# osipi.dro.SignalEnhancementExtract

::: osipi.DRO.SignalEnhancementExtract
9 changes: 9 additions & 0 deletions docs-mk/docs/user-guide/dro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# What is a DRO(digital reference object)?
Digital reference object are synthetic images that have been created
by computer simulations of a target in its environment, you can look here for a more detailed explanation of what a [DRO](https://qibawiki.rsna.org/images/1/14/QIBA_DRO_2015_v1.42.pdf) is.

# How to use DCE DROs in OSIPI
- [Read DRO data](synthetic.md#read-dro-data)
- [Enhance the signal](synthetic.md#enhance-the-signal)
- [Get the AIF](synthetic.md#get-the-aif)
- [Get the perfusion and tissue parameters](synthetic.md#get-the-perfusion-and-tissue-parameters)
2 changes: 2 additions & 0 deletions docs-mk/docs/user-guide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@ Welcome to the User Guide section.
- [Relaxation time to concentration](simulate.md)
- [Concentration to tissue parameters](simulate.md)
- [All in one go: signal to tissue parameters](simulate.md)
4. [DRO](dro.md)
- [Read data](simulate.md)
62 changes: 62 additions & 0 deletions docs-mk/docs/user-guide/synthetic.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
## Read MR data from a folder of dicom files
``` python
import osipi


dicom_folder = 'path/to/dicom/folder'

signal, slices, dicom_ref = osipi.read_dicom(dicom_folder)

#Read a DICOM series from a folder path.
#Returns the signal, slices and dicom reference.
# - The signal is a 4D numpy array with dimensions (x, y, z, t)
# - The slices is a list of slices
# - The dicom reference is a sample dicom file from the folder

```

## Enhance the signal
Enhance the signal by removing baseline signal which is the first time point of the signal.
Before the contrast agent is arrived, the signal is assumed to be constant. This constant signal is removed from the signal to enhance the signal.
``` python
import osipi
from osipi import enhance_signal

E, S0, S = enhance_signal(signal, data_shape, 5)

# - E is the enhanced signal after removing the baseline signal
# - S0 is the average baseline signal here in this example of the first 5 time points
# - S is the original raw signal
```

## Get the AIF
Get the Arterial Input Function (AIF) from the signal.
Using a mask, the AIF is extracted from the signal.

``` python
import osipi
from osipi import get_aif_from_ROI
# first you may have to create a mask for the AIF manually or using a saved mask and apply it to the signal
aif_mask, rio_voxels = osipi.rio(signal, slice, saved=True)
# this returns the mask and the number of voxels in the mask
aif = get_aif_from_ROI(signal, aif_mask)
# this returns the AIF from the signal by averaging the signal over the voxels in the mask
```

## Get the perfusion and tissue parameters

Get the perfusion and tissue parameters from the signal and the AIF.
You may here choose different models to fit the signal to get the parameters.

``` python
import osipi
from osipi import extended_tofts_model

# Fit the signal to the extended Tofts model
ktrans, ve, vp = extended_tofts_model(signal, aif, data_shape)

# - ktrans is the volume transfer constant
# - ve is the extravascular extracellular volume fraction
# - vp is the plasma volume fraction
# visit CAPLEX for more information on the model and the parameters
```
18 changes: 15 additions & 3 deletions docs-mk/mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ theme:
language: en
palette:
- scheme: default
primary: teal
primary: deep-purple
accent: purple
toggle:
icon: material/toggle-switch
Expand All @@ -36,7 +36,7 @@ theme:
toggle:
icon: material/toggle-switch
name: Switch to dark mode
primary: teal
primary: purple
accent: lime

plugins:
Expand All @@ -57,12 +57,24 @@ nav:
- Simulating data:
- Overview: user-guide/simulate.md
- Simulate: user-guide/gen_aif.md
- DRO:
- Overview: user-guide/dro.md
- DRO: user-guide/synthetic.md

- About: about/index.md
- Developer Guide: contribution/index.md
- References:
- references/index.md
- Index:
- Dro:
- references/dro/index.md
- DRO functions:
- read_mri_from_dicoms: references/dro/dro_read_mri.md
- signal_enhancement: references/dro/signal_enhance.md
- Fitting:
- Modified Tofts: references/dro/fit_models/modified_tofts.md
- Extended Tofts: references/dro/fit_models/extended_tofts.md
- Forward Tofts: references/dro/fit_models/forward_modified_tofts.md
- Models:
- references/models/index.md
- AIF models:
- references/models/aif_models/index.md
Expand Down
35 changes: 29 additions & 6 deletions src/osipi/DRO/DICOM_processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,20 @@
from osipi.DRO.filters_and_noise import median_filter


def read_dicom_slices_as_4d_signal(folder_path):
"""
Read a DICOM series from a folder path.
Returns the signal data as a 4D numpy array (x, y, z, t).
def read_dicom_slices_as_4d_signal(folder_path: str) -> (np.ndarray, dict, np.ndarray):
"""Read DICOM files from a folder and return the 4D signal data.

Args:
folder_path (str): Path to the folder containing DICOM files.

Returns:
signal: The 4D signal data (x, y, z, t).
slices: Dictionary containing the slices.
example_data: The first slice.


"""

slices = {}
for root, _, files in os.walk(folder_path):
for file in files:
Expand Down Expand Up @@ -40,8 +49,22 @@ def read_dicom_slices_as_4d_signal(folder_path):
return signal, slices, slices[slice_location][0][1]


def SignalEnhancementExtract(S, datashape, baselinepoints):
# Take baseline average
def SignalEnhancementExtract(
S: np.ndarray, datashape: list, baselinepoints: int = 5
) -> (np.ndarray, np.ndarray, np.ndarray):
"""Signal enhancement extraction from 4D signal data.

Args:
S: is the 4D signal data (x, y, z, t).
datashape: is the shape of the data (x, y, z, t).
baselinepoints: is the number of time points before contrast injection.

Returns:
E: The signal enhancement.
S0: The baseline signal (S0).
S: The 4D signal data (x, y, z, t).

"""
S0 = np.average(S[:, :, :, 0:baselinepoints], axis=3) # Take baseline signal
E = np.zeros_like(S)

Expand Down
103 changes: 93 additions & 10 deletions src/osipi/DRO/Model.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,40 @@
import osipi


def modifiedToftsMurase(Cp, Ctiss, dt, datashape):
def modifiedToftsMurase(
Cp: np.ndarray, Ctiss: np.ndarray, dt: int, datashape: tuple
) -> (np.ndarray, np.ndarray, np.ndarray):
"""Fit the Modified Tofts model to the data

This function uses a linear solver (np.linalg.lstsq)
to fit the parameters of the Modified Tofts model.
The linear solver is used to solve the equation C = AB for B,
where C is the tissue concentration,
A is a matrix that depends on the arterial input function and the tissue concentration,
and B is the array of parameters.


Args:
Cp: is the arterial input function [OSIPI CAPLEX Q.IC1.001]
Ctiss: is the tissue concentration [OSIPI CAPLEX Q.IC1.001] as 4d array (x, y, z, t)
dt: is the time interval between samples
datashape: is the shape of the data array (x, y, z, t)

Returns:
K1: is the rate constant of the forward model (Ktrans) [OSIPI CAPLEX Q.PH1.008]
k2: is the rate constant of the reverse model [OSIPI CAPLEX Q.PH1.009]
Vp: is the plasma volume fraction [OSIPI CAPLEX Q.PH1.001]

See Also:
`Exteded Tofts Model`
`Tofts Model`

References:
- Lexicon url:
https://osipi.github.io/OSIPI_CAPLEX/perfusionModels/#indicator-concentration-models
- Lexicon code: M.PH1.001
- OSIPI name: Modified Tofts Model
"""
# Fit Modified Tofts (Linear from Murase, 2004)
# Cp = Ea/0.45, Ctis=E/0.45
# Matrix equation C=AB (same notation as Murase)
Expand Down Expand Up @@ -120,7 +153,43 @@ def process_voxel(j, i, k, c_tiss, ca, t, type="ET"):
return j, i, k, popt


def extended_tofts_model(ca, c_tiss, t):
def extended_tofts_model(
ca: np.ndarray, c_tiss: np.ndarray, t: np.ndarray
) -> (np.ndarray, np.ndarray, np.ndarray):
"""
Fit the Extended Tofts model to the data

The function uses a curve fit from SciPy to fit the parameters of the Extended Tofts model.

The function maintain a parallel processing using the multiprocessing library;
the number of processes is equal to the number of CPUs in the machine.

The function returns the fitted parameters for each voxel in the 3D data array.




Args:
ca: is the arterial input function [OSIPI CAPLEX Q.IC1.001]
c_tiss: is the tissue concentration [OSIPI CAPLEX Q.IC1.001] as 4d array (x, y, z, t)
t: is the time as 1D array

Returns:
K1: is the rate constant of the forward model (Ktrans) [OSIPI CAPLEX Q.PH1.008]
ve: is the extravascular extracellular volume fraction [OSIPI CAPLEX Q.PH1.001]
vp: is the plasma volume fraction [OSIPI CAPLEX Q.PH1.001]

See Also:
`Modified Tofts Model`
`Tofts Model`

References:
- Lexicon url:
https://osipi.github.io/OSIPI_CAPLEX/perfusionModels/#ETM
- Lexicon code: M.PH1.002
- OSIPI name: Extended Tofts

"""
ktrans = np.zeros(c_tiss.shape[:-1])
ve = np.zeros(c_tiss.shape[:-1])
vp = np.zeros(c_tiss.shape[:-1])
Expand Down Expand Up @@ -175,10 +244,28 @@ def tofts_model(ca, c_tiss, t):
return ktrans, ve


def ForwardsModTofts(K1, k2, Vp, Cp, dt):
# To be carried out as matmul C=BA
# Where C is the output Ctiss and B the parameters
# With A a matrix of cumulative integrals
def ForwardsModTofts(K1: np.ndarray, k2: np.ndarray, Vp: np.ndarray, Cp: np.ndarray, dt: int):
"""Calculate the tissue concentration using the Tofts model

Args:
K1: is the rate constant of the forward model (Ktrans) [OSIPI CAPLEX Q.PH1.008]
k2: is the rate constant of the reverse model [OSIPI CAPLEX Q.PH1.009]
Vp: is the plasma volume fraction [OSIPI CAPLEX Q.PH1.001]
Cp: is the arterial input function [OSIPI CAPLEX Q.IC1.001]
dt: is the time interval between samples

Returns:
Ctiss: is the tissue concentration [OSIPI CAPLEX Q.IC1.001] as 4d array (x, y, z, t)

See Also:
`Modified Tofts Model`
`Extended Tofts Model`

References:
- Lexicon url:
https://osipi.github.io/OSIPI_CAPLEX/perfusionModels/#TM
- OSIPI name: Tofts Model
"""

x, y, z = K1.shape
t = Cp.shape[0]
Expand All @@ -203,10 +290,6 @@ def ForwardsModTofts(K1, k2, Vp, Cp, dt):


def ForwardsModTofts_1vox(K1, k2, Vp, Cp, dt):
# To be carried out as matmul C=BA
# Where C is the output Ctiss and B the parameters
# With A a matrix of cumulative integrals

t = Cp.shape[0]

Ctiss = np.zeros(t)
Expand Down
4 changes: 4 additions & 0 deletions src/osipi/DRO/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .DICOM_processing import read_dicom_slices_as_4d_signal, SignalEnhancementExtract
from .Model import (modifiedToftsMurase, extended_tofts_model,
tofts_model, forward_extended_tofts,
ForwardsModTofts)
Loading