Skip to content

Commit

Permalink
Merge pull request #138 from pyplati/nnunet-segmentation
Browse files Browse the repository at this point in the history
Open Atlas
  • Loading branch information
pchlap authored Jun 2, 2022
2 parents 04e0602 + 6f0366e commit 73017e4
Show file tree
Hide file tree
Showing 14 changed files with 552 additions and 139 deletions.
Binary file added assets/cardiac.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions docs/cardiac.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

.. mdinclude:: ../platipy/imaging/projects/cardiac/README.md
7 changes: 7 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ PlatiPy documentation
getting_started
contributing

.. toctree::
:caption: Projects
:maxdepth: 2
:hidden:

cardiac

.. toctree::
:caption: Examples
:maxdepth: 2
Expand Down
126 changes: 20 additions & 106 deletions examples/cardiac_segmentation.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"# Cardiac Multi Atlas-Based Segmentation Example\n",
"# Cardiac Sub-Structure Segmentation Example\n",
"\n",
"## Import Modules"
]
Expand All @@ -21,12 +21,14 @@
" !pip install git+https://github.com/pyplati/platipy.git\n",
" import platipy\n",
"\n",
"from matplotlib import pyplot as plt\n",
"\n",
"import SimpleITK as sitk\n",
"\n",
"%matplotlib inline\n",
"\n",
"from platipy.imaging.tests.data import get_lung_nifti\n",
"from platipy.imaging.projects.cardiac.run import run_cardiac_segmentation\n",
"from platipy.imaging.projects.cardiac.run import run_hybrid_segmentation\n",
"from platipy.imaging import ImageVisualiser\n",
"from platipy.imaging.label.utils import get_com"
]
Expand All @@ -52,101 +54,6 @@
"data_path = get_lung_nifti()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Configure Settings\n",
"\n",
"We define the settings used for the end-to-end segmentation\n",
"Check out the guide in \"PlatiPy-GettingStarted.pdf\" for a description of these settings"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"settings = {\n",
" \"output_format\": \"Auto_{0}.nii.gz\",\n",
" \"atlas_settings\": {\n",
" \"atlas_id_list\": [\"101\",\"102\",\"103\",\"104\"],\n",
" \"atlas_structure_list\": [\"HEART\",\"ESOPHAGUS\",\"LUNG_L\",\"LUNG_R\",\"SPINALCORD\"],\n",
" \"atlas_path\": str(data_path),\n",
" \"atlas_image_format\": \"LCTSC-Test-S1-{0}/IMAGES/LCTSC_TEST_S1_{0}_0_CT_0.nii.gz\",\n",
" \"atlas_label_format\": \"LCTSC-Test-S1-{0}/STRUCTURES/LCTSC_TEST_S1_{0}_0_RTSTRUCT_{1}.nii.gz\",\n",
" \"crop_atlas_to_structures\": False,\n",
" \"crop_atlas_expansion_mm\": (10, 10, 10),\n",
" \"guide_structure_name\": \"HEART\",\n",
" },\n",
" \"auto_crop_target_image_settings\": {\n",
" \"expansion_mm\": [2, 2, 2],\n",
" },\n",
" \"linear_registration_settings\": {\n",
" \"reg_method\": \"similarity\",\n",
" \"shrink_factors\": [16, 8, 4],\n",
" \"smooth_sigmas\": [0, 0, 0],\n",
" \"sampling_rate\": 0.75,\n",
" \"default_value\": -1024,\n",
" \"number_of_iterations\": 50,\n",
" \"metric\": \"mean_squares\",\n",
" \"optimiser\": \"gradient_descent_line_search\",\n",
" \"verbose\": False,\n",
" },\n",
" \"deformable_registration_settings\": {\n",
" \"isotropic_resample\": True,\n",
" \"resolution_staging\": [\n",
" 16,\n",
" 8,\n",
" 4,\n",
" ], # specify voxel size (mm) since isotropic_resample is set\n",
" \"iteration_staging\": [15, 15, 15],\n",
" \"smoothing_sigmas\": [4, 2, 1],\n",
" \"ncores\": 8,\n",
" \"default_value\": -1000,\n",
" \"verbose\": False,\n",
" },\n",
" \"structure_guided_registration_settings\": {\n",
" \"isotropic_resample\": True,\n",
" \"resolution_staging\": [\n",
" 16,\n",
" 8,\n",
" 2,\n",
" ], # specify voxel size (mm) since isotropic_resample is set\n",
" \"iteration_staging\": [25, 25, 25],\n",
" \"smoothing_sigmas\": [0, 0, 0],\n",
" \"ncores\": 8,\n",
" \"default_value\": 0,\n",
" \"verbose\": False,\n",
" },\n",
" \"iar_settings\": {\n",
" \"reference_structure\": \"HEART\",\n",
" \"smooth_distance_maps\": True,\n",
" \"smooth_sigma\": 1,\n",
" \"z_score_statistic\": \"mad\",\n",
" \"outlier_method\": \"iqr\",\n",
" \"outlier_factor\": 1.5,\n",
" \"min_best_atlases\": 5,\n",
" \"project_on_sphere\": False,\n",
" },\n",
" \"label_fusion_settings\": {\n",
" \"vote_type\": \"unweighted\",\n",
" \"vote_params\": {}, # No parameters needed for majority voting\n",
" \"optimal_threshold\": {\"HEART\": 0.5, \"ESOPHAGUS\": 0.5, \"LUNG_L\": 0.5, \"LUNG_R\": 0.5, \"SPINALCORD\": 0.5},\n",
" },\n",
" \"vessel_spline_settings\": {\n",
" \"vessel_name_list\": [\"SPINALCORD\"],\n",
" \"vessel_radius_mm_dict\": {\"SPINALCORD\": 6},\n",
" \"scan_direction_dict\": {\"SPINALCORD\": \"z\"},\n",
" \"stop_condition_type_dict\": {\"SPINALCORD\": \"count\"},\n",
" \"stop_condition_value_dict\": {\"SPINALCORD\": 1},\n",
" },\n",
" \"return_as_cropped\": False,\n",
" 'returnAsCropped': False\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down Expand Up @@ -183,7 +90,7 @@
},
"outputs": [],
"source": [
"auto_structures = run_cardiac_segmentation(test_image, settings=settings)"
"auto_structures, _ = run_hybrid_segmentation(test_image)"
]
},
{
Expand All @@ -200,12 +107,11 @@
"metadata": {},
"outputs": [],
"source": [
"output_name = settings[\"output_format\"]\n",
"output_directory = test_pat_path.joinpath(\"SEGMENTATIONS\")\n",
"output_directory = test_pat_path.joinpath(\"substructures\")\n",
"output_directory.mkdir(exist_ok=True)\n",
"\n",
"for struct_name in list(auto_structures.keys()):\n",
" sitk.WriteImage(auto_structures[struct_name], str(output_directory.joinpath(output_name.format(struct_name))))\n",
" sitk.WriteImage(auto_structures[struct_name], str(output_directory.joinpath(f\"{struct_name}.nii.gz\")))\n",
"\n",
"print(f\"Segmentations saved to: {output_directory}\")\n",
"\n"
Expand All @@ -226,11 +132,19 @@
"metadata": {},
"outputs": [],
"source": [
"vis = ImageVisualiser(test_image, cut=get_com(auto_structures[\"HEART\"]))\n",
"\n",
"vis = ImageVisualiser(test_image, cut=get_com(auto_structures[\"Heart\"]))\n",
"vis.add_contour({struct: auto_structures[struct] for struct in auto_structures.keys()})\n",
"\n",
"fig = vis.show()"
"fig = vis.show()\n",
"plt.savefig(output_directory.joinpath(f\"snapshot.png\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"fig"
]
},
{
Expand All @@ -257,7 +171,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.9"
"version": "3.8.10"
}
},
"nbformat": 4,
Expand Down
5 changes: 3 additions & 2 deletions platipy/backend/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ class Dataset(db.Model):
output_data_objects = relationship(
"DataObject",
primaryjoin="and_(DataObject.dataset_id == Dataset.id, DataObject.is_input == False)",
overlaps="input_data_objects"
)

# The Dicom location from which to retrieve data
Expand All @@ -143,7 +144,7 @@ class DataObject(db.Model):

id = db.Column(db.Integer, primary_key=True)
dataset_id = db.Column(db.Integer, db.ForeignKey("Dataset.id"), nullable=False)
dataset = relationship("Dataset")
dataset = relationship("Dataset", overlaps="input_data_objects,output_data_objects")
is_input = db.Column(db.Boolean, default=False)

path = db.Column(db.String(256))
Expand All @@ -163,7 +164,7 @@ class DataObject(db.Model):
parent_id = db.Column(db.Integer, db.ForeignKey("DataObject.id"), index=True)
parent = relationship("DataObject", remote_side=[id])
children = relationship(
"DataObject", primaryjoin="and_(DataObject.parent_id == DataObject.id)"
"DataObject", primaryjoin="and_(DataObject.parent_id == DataObject.id)", overlaps="parent"
)

def __repr__(self):
Expand Down
3 changes: 2 additions & 1 deletion platipy/backend/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ def fetch_status():

status_context["applications"] = []
for ak in APIKey.query.all():
status_context["applications"].append({"name": ak.name, "key": ak.key})
# Hide Key from the dashboard
status_context["applications"].append({"name": ak.name, "key": "XXX"})

return jsonify(status_context)

Expand Down
13 changes: 9 additions & 4 deletions platipy/cli/segmentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@
BRONCHUS_SETTINGS_DEFAULTS,
)
from platipy.imaging.projects.cardiac.run import (
run_cardiac_segmentation,
CARDIAC_SETTINGS_DEFAULTS,
run_hybrid_segmentation,
HYBRID_SETTINGS_DEFAULTS,
)

segmentation_algorithms = {
"cardiac": {
"algorithm": run_cardiac_segmentation,
"default_settings": CARDIAC_SETTINGS_DEFAULTS,
"algorithm": run_hybrid_segmentation,
"default_settings": HYBRID_SETTINGS_DEFAULTS
},
"bronchus": {
"algorithm": run_bronchus_segmentation,
Expand Down Expand Up @@ -105,6 +105,11 @@ def click_command(algorithm, input_path, config, default, output):
# Run the algorithm
results = segmentation_algorithms[algorithm]["algorithm"](image, algorithm_config)

# If results is a tuple, the algorithm is returning other items as well as the semgmentation.
# We are only interested in the segmentation here (the first element of the tuple)
if isinstance(results, tuple):
results = results[0]

# Save the output to the output directory
if not output:
output = "."
Expand Down
Loading

0 comments on commit 73017e4

Please sign in to comment.