Skip to content

Commit

Permalink
Merge branch 'master' into prob-unet
Browse files Browse the repository at this point in the history
  • Loading branch information
pchlap committed Nov 27, 2023
2 parents 51c6df2 + 4a90b71 commit 673ce65
Show file tree
Hide file tree
Showing 32 changed files with 3,251 additions and 2,307 deletions.
12 changes: 12 additions & 0 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Delete huge unnecessary tools folder
run: rm -rf /opt/hostedtoolcache
- name: Checkout
uses: actions/checkout@v2
- name: Set up QEMU
Expand Down Expand Up @@ -124,3 +126,13 @@ jobs:
tags: |
platipy/platipy:nnunet
ghcr.io/pyplati/platipy:nnunet
- name: Build and Push TotalSegmentator Image
uses: docker/build-push-action@v2
with:
context: services/totalsegmentator
platforms: linux/amd64
push: true
tags: |
platipy/platipy:totalsegmentator
ghcr.io/pyplati/platipy:totalsegmentator
74 changes: 31 additions & 43 deletions CITATION.cff
Original file line number Diff line number Diff line change
@@ -1,46 +1,34 @@
cff-version: 1.2.0
title: "PlatiPy: Processing Library and Analysis Toolkit for Medical Imaging in Python"
message: >-
If you find the PlatiPy library useful in your research, please consider citing it using these
metadata.
abstract: >-
PlatiPy is a library of tools developed in Python for image processing and analysis of medical
images. A broad scope of functionality is provided including image conversion, registration
and segmentation is provided as well as computation of similarity and radiotherapy dosimtery
metrics and much more.
type: software
cff-version: "1.2.0"
authors:
- given-names: Phillip
family-names: Chlap
email: [email protected]
affiliation: University of New South Wales
- family-names: Chlap
given-names: Phillip
orcid: "https://orcid.org/0000-0002-6517-8745"
- family-names: Finnegan
given-names: Robert N.
orcid: "https://orcid.org/0000-0003-4728-8462"
doi: 10.5281/zenodo.8032858
message: If you use this software, please cite our article in the
Journal of Open Source Software.
preferred-citation:
authors:
- family-names: Chlap
given-names: Phillip
orcid: "https://orcid.org/0000-0002-6517-8745"
- given-names: Robert
family-names: Finnegan
email: [email protected]
affiliation: University of Sydney
- family-names: Finnegan
given-names: Robert N.
orcid: "https://orcid.org/0000-0003-4728-8462"
license: Apache-2.0
repository-code: "https://github.com/pyplati/platipy"
url: "https://pyplati.github.io/platipy/"
keywords:
- "medical image analysis"
- "auto-segmentation"
- "image registration"
references:
- authors:
- family-names: Lowekamp
given-names: Bradley C
- family-names: Chen
given-names: David T
- family-names: Ibáñez
given-names: Luis
- family-names: Blezek
given-names: Daniel
doi: 10.3389/fninf.2013.00045
issn: 16625196
journal: "Frontiers in Neuroinformatics"
title: "The design of simpleITK"
type: article
volume: 7
year: 2013
date-published: 2023-06-26
doi: 10.21105/joss.05374
issn: 2475-9066
issue: 86
journal: Journal of Open Source Software
publisher:
name: Open Journals
start: 5374
title: "PlatiPy: Processing Library and Analysis Toolkit for Medical
Imaging in Python"
type: article
url: "https://joss.theoj.org/papers/10.21105/joss.05374"
volume: 8
title: "PlatiPy: Processing Library and Analysis Toolkit for Medical
Imaging in Python"
60 changes: 33 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# PlatiPy
# PlatiPy

[![DOI](https://joss.theoj.org/papers/10.21105/joss.05374/status.svg)](https://doi.org/10.21105/joss.05374)

## Processing Library and Analysis Toolkit for Medical Imaging in Python

PlatiPy is a library of **amazing** tools for image processing and analysis - designed specifically
for medical imaging!
for medical imaging!

Check out the [PlatiPy documentation](https://pyplati.github.io/platipy/) for more info.

This project was motivated by the need for a simple way to use, visualise, process, and analyse
This project was motivated by the need for a simple way to use, visualise, process, and analyse
medical images. Many of the tools and algorithms are designed in the context of radiation therapy,
although they are more widely applicable to other fields that use 2D, 3D, or 4D imaging.

Expand All @@ -16,31 +19,33 @@ We welcome feedback and contributions from the community (yes, you!) and you can
information about contributing [here](https://pyplati.github.io/platipy/contributing.html).

## What can I do with **platipy**?
A lot! A good place to start is by looking in the

A lot! A good place to start is by looking in the
[examples directory](https://github.com/pyplati/platipy/tree/master/examples).

Some examples of what PlatiPy can do:
- DICOM organising and converting:
* Bulk convert from multiple series and studies with a single function
* Convert DICOM-RT structure and dose filesto NIfTI images
* Create DICOM-RT structure files from binary masks e.g. from automatic contouring algorithms
- Image registration
* Register images and transform labels with a few lines of code
* Linear transformations: rigid, affine, similarity
* Non-linear deformable transformations: demons, b-splines
* Multiple metrics for optimisation
- Atlas-based segmentation
* A suite of tools that can be used out-of-the-box
* Includes advanced algorithms for
[iterative atlas selection](https://doi.org/10.1088/1361-6560/ab652a/) and

- DICOM organising and converting:
- Bulk convert from multiple series and studies with a single function
- Convert DICOM-RT structure and dose files to NIfTI images
- Create DICOM-RT structure files from binary masks e.g. from automatic contouring algorithms
- Image registration
- Register images and transform labels with a few lines of code
- Linear transformations: rigid, affine, similarity
- Non-linear deformable transformations: demons, b-splines
- Multiple metrics for optimisation
- Atlas-based segmentation
- A suite of tools that can be used out-of-the-box
- Includes advanced algorithms for
[iterative atlas selection](https://doi.org/10.1088/1361-6560/ab652a/) and
[vessel splining](https://doi.org/10.1088/1361-6560/abcb1d/)
- Synthetic deformation field generation
* Simulate anatomically realistic shifts, expansions, and bending
* Compare DIR results from clinical systems
- Basic tools for image processing and analysis
* Computing label similarity metrics: DSC, mean distance to agreement, Hausdorff distance, and more
* Cropping images to a region of interest
* Rotate images and generate maximum/mean intensity projections (beams eye view modelling)
- Synthetic deformation field generation
- Simulate anatomically realistic shifts, expansions, and bending
- Compare DIR results from clinical systems
- Basic tools for image processing and analysis
- Computing label similarity metrics: DSC, mean distance to agreement, Hausdorff distance, and more
- Cropping images to a region of interest
- Rotate images and generate maximum/mean intensity projections (beams eye view modelling)

A major part of this package is **visualisation**, and some examples are shown below!

Expand Down Expand Up @@ -74,7 +79,7 @@ fig = vis.show()
![Figure 2](assets/figure_2.png)

#### Calculate deformation vector fields

```python
from platipy.imaging.registration.deformable import fast_symmetric_forces_demons_registration

Expand All @@ -99,6 +104,7 @@ fig = vis.show()
![Figure 3](assets/figure_3.png)

## Getting started

There aren't many requirements, just an installed Python interpreter (3.7 or greater). PlatiPy can
be installed with **pip**:

Expand All @@ -117,5 +123,5 @@ pip install platipy[backend]

## Authors

* **Phillip Chlap** - [[email protected]]([email protected])
* **Robert Finnegan** - [[email protected]]([email protected])
- **Phillip Chlap** - [[email protected]]([email protected])
- **Robert Finnegan** - [[email protected]]([email protected])
9 changes: 5 additions & 4 deletions examples/dvh_analysis.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
" !pip install platipy\n",
" import platipy\n",
"\n",
"import matplotlib\n",
"import matplotlib.pyplot as plt\n",
"import SimpleITK as sitk\n",
"\n",
Expand Down Expand Up @@ -103,7 +104,7 @@
"source": [
"vis = ImageVisualiser(ct_image, cut=get_com(structures[\"PTV60\"]))\n",
"\n",
"vis.add_scalar_overlay(dose, discrete_levels=20, colormap=plt.cm.get_cmap(\"inferno\"), name=\"Dose (Gy)\")\n",
"vis.add_scalar_overlay(dose, discrete_levels=20, colormap=matplotlib.colormaps.get_cmap(\"inferno\"), name=\"Dose (Gy)\")\n",
"vis.add_contour(structures)\n",
"\n",
"fig = vis.show()"
Expand Down Expand Up @@ -160,7 +161,7 @@
"\n",
"# Plot the DVH\n",
"fig, ax = plt.subplots()\n",
"plt_dvh.plot(ax=ax, kind=\"line\", colormap=plt.cm.get_cmap(\"rainbow\"), legend=False)\n",
"plt_dvh.plot(ax=ax, kind=\"line\", colormap=matplotlib.colormaps.get_cmap(\"rainbow\"), legend=False)\n",
"\n",
"# Add labels and show plot\n",
"plt.legend(loc='best')\n",
Expand Down Expand Up @@ -226,8 +227,8 @@
" d_cc_points=[10],\n",
" structure_for_limits=dose>5,\n",
" expansion_for_limits=40,\n",
" contour_cmap=plt.cm.get_cmap(\"rainbow\"),\n",
" dose_cmap=plt.cm.get_cmap(\"inferno\"),\n",
" contour_cmap=matplotlib.colormaps.get_cmap(\"rainbow\"),\n",
" dose_cmap=matplotlib.colormaps.get_cmap(\"inferno\"),\n",
" title=\"TCGA_CV_5977 Dose Metrics\")"
]
},
Expand Down
9 changes: 5 additions & 4 deletions examples/visualise.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@
"# Function to grab some test data\n",
"from platipy.imaging.tests.data import get_lung_nifti\n",
"\n",
"# Usual suspects\n",
"# Import some common libraries which we need\n",
"import numpy as np\n",
"import SimpleITK as sitk\n",
"from pathlib import Path\n",
"import matplotlib\n",
"import matplotlib.pyplot as plt\n",
"\n",
"# Some utilities we use for generating interesting data\n",
Expand Down Expand Up @@ -153,7 +154,7 @@
"metadata": {},
"outputs": [],
"source": [
"image_visualiser = ImageVisualiser(ct_image, window=(-900, 1100), colormap=plt.cm.get_cmap(\"terrain\"), figure_size_in=6)\n",
"image_visualiser = ImageVisualiser(ct_image, window=(-900, 1100), colormap=matplotlib.colormaps.get_cmap(\"terrain\"), figure_size_in=6)\n",
"\n",
"image_visualiser.set_limits_from_label(contours[\"LUNG_L\"] + contours[\"LUNG_R\"])\n",
"\n",
Expand Down Expand Up @@ -238,7 +239,7 @@
"image_visualiser.add_vector_overlay(\n",
" dvf_expansion,\n",
" name=\"DVF magnitude [mm]\",\n",
" colormap=plt.cm.get_cmap(\"gnuplot2\"),\n",
" colormap=matplotlib.colormaps.get_cmap(\"gnuplot2\"),\n",
" alpha=0.75,\n",
" arrow_scale=1,\n",
" arrow_width=1,\n",
Expand Down Expand Up @@ -314,7 +315,7 @@
"image_visualiser.add_vector_overlay(\n",
" dvf_expansion,\n",
" name=\"DVF magnitude [mm]\",\n",
" colormap=plt.cm.get_cmap(\"gnuplot2\"),\n",
" colormap=matplotlib.colormaps.get_cmap(\"gnuplot2\"),\n",
" alpha=0.5,\n",
" arrow_scale=1,\n",
" arrow_width=1,\n",
Expand Down
2 changes: 2 additions & 0 deletions platipy/backend/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ def on_celery_setup_logging(**kwargs):
app = FlaskApp(__name__)
app.config["SECRET_KEY"] = uuid.uuid4().hex

app.app_context().push()

# Configure SQL Alchemy
app.config["SQLALCHEMY_DATABASE_URI"] = f"sqlite:///{env_work}/{os.path.basename(os.getcwd())}.db"
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
Expand Down
4 changes: 2 additions & 2 deletions platipy/backend/static/js/jquery.min.js

Large diffs are not rendered by default.

10 changes: 6 additions & 4 deletions platipy/dicom/io/nifti_to_rtstruct.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,17 @@
import SimpleITK as sitk
import numpy as np
import logging
import matplotlib
from matplotlib import cm

from rt_utils import RTStructBuilder

logger = logging.getLogger(__name__)


def convert_nifti(dcm_path, mask_input, output_file, color_map=cm.get_cmap("rainbow")):
def convert_nifti(
dcm_path, mask_input, output_file, color_map=matplotlib.colormaps.get_cmap("rainbow")
):
"""Convert a set of masks to a DICOM RTStruct object.
This function now utilises the rt-utils package: https://github.com/qurit/rt-utils
Expand All @@ -37,8 +40,8 @@ def convert_nifti(dcm_path, mask_input, output_file, color_map=cm.get_cmap("rain
mask_input (dict|list): A dictionary containing the name as key and image as value. Or a
list of string with comma separated name and mask paths (name,path)
output_file (str|pathlib.Path): The path to the file to write the RTStruct
color_map (matplotlib.colors.Colormap, optional): Colormap to use for output. Defaults to
cm.get_cmap("rainbow").
color_map (matplotlib.colors.Colormap, optional): Colormap to use for output. Defaults to
matplotlib.colormaps.get_cmap("rainbow").
"""

logger.info("Will convert the following Nifti masks to RTStruct:")
Expand All @@ -65,7 +68,6 @@ def convert_nifti(dcm_path, mask_input, output_file, color_map=cm.get_cmap("rain
rtstruct = RTStructBuilder.create_new(dicom_series_path=str(dcm_series_path))

for mask_name in masks:

# Use a hash of the name to get the color from the supplied color map
color = color_map(hash(mask_name) % 256)
color = color[:3]
Expand Down
2 changes: 1 addition & 1 deletion platipy/imaging/dose/dvh.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ def calculate_dvh_for_labels(dose_grid, labels, bin_width=0.1, max_dose=None):
mask_array = sitk.GetArrayFromImage(mask)

# Compute cubic centimetre volume of structure
cc = mask_array.sum() * np.product([a / 10 for a in mask.GetSpacing()])
cc = mask_array.sum() * np.prod([a / 10 for a in mask.GetSpacing()])

bins, values = calculate_dvh(
dose_grid, labels[k], bins=np.arange(-bin_width / 2, max_dose + bin_width, bin_width)
Expand Down
4 changes: 2 additions & 2 deletions platipy/imaging/dose/metric.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def calculate_d_to_volume(dose_grid, label, volume, volume_in_cc=False):
mask_array = sitk.GetArrayFromImage(label)

if volume_in_cc:
volume = (volume * 1000 / ((mask_array > 0).sum() * np.product(label.GetSpacing()))) * 100
volume = (volume * 1000 / ((mask_array > 0).sum() * np.prod(label.GetSpacing()))) * 100

if volume > 100:
volume = 100
Expand Down Expand Up @@ -106,7 +106,7 @@ def calculate_v_receiving_dose(dose_grid, label, dose_threshold, relative=True):
if relative:
return relative_volume

total_volume = (mask_array > 0).sum() * np.product(label.GetSpacing()) / 1000
total_volume = (mask_array > 0).sum() * np.prod(label.GetSpacing()) / 1000

return relative_volume * total_volume

Expand Down
4 changes: 2 additions & 2 deletions platipy/imaging/label/comparison.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def compute_volume(label):
float: The volume (in cubic centimetres)
"""

return sitk.GetArrayFromImage(label).sum() * np.product(label.GetSpacing()) / 1000
return sitk.GetArrayFromImage(label).sum() * np.prod(label.GetSpacing()) / 1000


def compute_surface_dsc(label_a, label_b, tau=3.0):
Expand Down Expand Up @@ -159,7 +159,7 @@ def compute_volume_metrics(label_a, label_b):
arr_intersection = arr_a & arr_b
arr_union = arr_a | arr_b

voxel_volume = np.product(label_a.GetSpacing()) / 1000.0 # Conversion to cm^3
voxel_volume = np.prod(label_a.GetSpacing()) / 1000.0 # Conversion to cm^3

# 2|A & B|/(|A|+|B|)
dsc = (2.0 * arr_intersection.sum()) / (arr_a.sum() + arr_b.sum())
Expand Down
2 changes: 1 addition & 1 deletion platipy/imaging/label/fusion.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ def compute_weight_map(
view_mask = view_as_windows(arr_mask, window_box_im)

# Flatten to have a list of patches (that are also flattened)
new_shape = (np.product(view_target.shape[:3]), np.product(view_target.shape[3:]))
new_shape = (np.prod(view_target.shape[:3]), np.prod(view_target.shape[3:]))
view_target_flat = np.reshape(view_target, new_shape)
view_moving_flat = np.reshape(view_moving, new_shape)
view_mask_flat = np.reshape(view_mask, new_shape)
Expand Down
Loading

0 comments on commit 673ce65

Please sign in to comment.